From 6621069924da88ebe393a52e50e610be58f4375b Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 9 Jul 2024 15:54:20 +0200 Subject: [PATCH 001/298] Use already existing type definitions from the sync-rules package in jpqwire --- packages/jpgwire/package.json | 1 + packages/jpgwire/src/pgwire_types.ts | 21 +------------------ packages/jpgwire/tsconfig.json | 3 +++ .../service-core/src/replication/WalStream.ts | 6 +++--- 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/packages/jpgwire/package.json b/packages/jpgwire/package.json index 746c2dcd6..12ff0976a 100644 --- a/packages/jpgwire/package.json +++ b/packages/jpgwire/package.json @@ -20,6 +20,7 @@ "dependencies": { "@powersync/service-jsonbig": "workspace:^", "@powersync/service-types": "workspace:^", + "@powersync/service-sync-rules": "workspace:^", "date-fns": "^3.6.0", "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87" } diff --git a/packages/jpgwire/src/pgwire_types.ts b/packages/jpgwire/src/pgwire_types.ts index aa21d7fd5..a93aeba66 100644 --- a/packages/jpgwire/src/pgwire_types.ts +++ b/packages/jpgwire/src/pgwire_types.ts @@ -3,6 +3,7 @@ import type { PgoutputRelation } from 'pgwire/mod.js'; import { dateToSqlite, lsnMakeComparable, timestampToSqlite, timestamptzToSqlite } from './util.js'; import { JsonContainer } from '@powersync/service-jsonbig'; +import { DatabaseInputRow } from '@powersync/service-sync-rules'; export class PgType { static decode(text: string, typeOid: number) { @@ -253,23 +254,3 @@ export function decodeTuple(relation: PgoutputRelation, tupleRaw: Record): Generator { + static *getQueryData(results: Iterable): Generator { for (let row of results) { yield toSyncRulesRow(row); } @@ -387,7 +387,7 @@ WHERE oid = $1::regclass`, } const rows = chunk.rows.map((row) => { - let q: pgwire.DatabaseInputRow = {}; + let q: DatabaseInputRow = {}; for (let c of columns) { q[c.name] = row[c.i]; } From 3b7461e8ff8da1172727ba36be18ffa6ca6ebc13 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 9 Jul 2024 15:57:06 +0200 Subject: [PATCH 002/298] Introduced general DataSource config type to the PowerSync Config. --- packages/types/src/config/PowerSyncConfig.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index a0bf765b7..bf7467ed3 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -19,7 +19,16 @@ export const portParser = { }) }; -export const postgresConnection = t.object({ +export const dataSourceConfig = t.object({ + // Unique string identifier for the data source + type: t.string, + /** Unique identifier for the connection - optional when a single connection is present. */ + id: t.string.optional(), +}); + +export type DataSourceConfig = t.Decoded; + +export const postgresConnection = dataSourceConfig.and(t.object({ type: t.literal('postgresql'), /** Unique identifier for the connection - optional when a single connection is present. */ id: t.string.optional(), @@ -49,7 +58,7 @@ export const postgresConnection = t.object({ * Prefix for the slot name. Defaults to "powersync_" */ slot_name_prefix: t.string.optional() -}); +})); export type PostgresConnection = t.Decoded; @@ -95,7 +104,8 @@ export type StorageConfig = t.Decoded; export const powerSyncConfig = t.object({ replication: t .object({ - connections: t.array(postgresConnection).optional() + connections: t.array(postgresConnection).optional(), + data_sources: t.array(dataSourceConfig).optional() }) .optional(), From 878c776dc8858d64da7c7ee997d6b8df8d017242 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:48:09 +0200 Subject: [PATCH 003/298] Add the base classes and interfaces for general modules --- .../src/modules/AbstractModule.ts | 24 ++++++++++++ .../service-core/src/modules/ModuleManager.ts | 38 +++++++++++++++++++ .../service-core/src/system/ServiceContext.ts | 11 ++++++ 3 files changed, 73 insertions(+) create mode 100644 packages/service-core/src/modules/AbstractModule.ts create mode 100644 packages/service-core/src/modules/ModuleManager.ts create mode 100644 packages/service-core/src/system/ServiceContext.ts diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts new file mode 100644 index 000000000..dfad51f97 --- /dev/null +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -0,0 +1,24 @@ +import { ServiceContext } from '../system/ServiceContext.js'; + +export abstract class AbstractModule { + public name: string; + + protected constructor(name: string) { + this.name = name; + } + + /** + * Initialize the module using any required services from the ServiceContext + */ + public abstract initialize(context: ServiceContext): Promise; + + /** + * Finish processing any requests and gracefully shut down any resources managed by the module + */ + public abstract shutdown(): Promise; + + /** + * Terminate and clean up any resources managed by the module right away + */ + public abstract teardown(): Promise; +} diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts new file mode 100644 index 000000000..72a0c4921 --- /dev/null +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -0,0 +1,38 @@ +import { logger } from '@powersync/lib-services-framework'; +import { AbstractModule } from './AbstractModule.js'; +import { ServiceContext } from '../system/ServiceContext.js'; + +/** + * The module manager is responsible for managing the lifecycle of all modules in the system. + */ +export class ModuleManager { + private readonly modules: Map = new Map(); + + public register(modules: AbstractModule[]) { + for (const module of modules) { + if (this.modules.has(module.name)) { + logger.warn(`Module ${module.name} already registered, skipping...`); + } else { + this.modules.set(module.name, module); + } + } + } + + async initialize(context: ServiceContext) { + for (const module of this.modules.values()) { + await module.initialize(context); + } + } + + async shutDown() { + for (const module of this.modules.values()) { + await module.shutdown(); + } + } + + async tearDown() { + for (const module of this.modules.values()) { + await module.teardown(); + } + } +} diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts new file mode 100644 index 000000000..478192944 --- /dev/null +++ b/packages/service-core/src/system/ServiceContext.ts @@ -0,0 +1,11 @@ +import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; +import { SyncAPIProvider } from '../api/SyncAPIProvider.js'; +import { ResolvedPowerSyncConfig } from '../util/config/types.js'; +import { BucketStorageFactory } from '../storage/BucketStorage.js'; + +export type ServiceContext = { + configuration: ResolvedPowerSyncConfig; + storage: BucketStorageFactory; + replicationEngine: ReplicationEngine; + syncAPIProvider: SyncAPIProvider; +}; From 95d5c904aca29e1ac283c6b20b37d56fb4ef3570 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:52:08 +0200 Subject: [PATCH 004/298] Add the SyncAPI interface describing the functionality required by the routes. --- packages/service-core/src/api/SyncAPI.ts | 67 +++++++++++++++++++ .../service-core/src/api/SyncAPIProvider.ts | 26 +++++++ packages/service-core/src/api/api-index.ts | 2 + 3 files changed, 95 insertions(+) create mode 100644 packages/service-core/src/api/SyncAPI.ts create mode 100644 packages/service-core/src/api/SyncAPIProvider.ts diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts new file mode 100644 index 000000000..c0771b9f0 --- /dev/null +++ b/packages/service-core/src/api/SyncAPI.ts @@ -0,0 +1,67 @@ +import { TablePattern } from '@powersync/service-sync-rules'; +import * as types from '@powersync/service-types'; + +/** + * Describes all the methods currently required to service the sync API endpoints. + * TODO: This interface needs to be cleaned up and made more generic. It describes the current functionality required by our API routes + */ +export interface SyncAPI { + /** + * Performs diagnostics on the "connection" + * This is usually some test query to verify the source can be reached. + */ + getDiagnostics(): Promise<{ + connected: boolean; + errors?: Array<{ level: string; message: string }>; + }>; + + /** + * Generates replication table information from a given pattern of tables. + * + * @param tablePatterns A set of table patterns which typically come from + * the tables listed in sync rules definitions. + * + * @returns A result of all the tables and columns which should be replicated + * based off the input patterns. Certain tests are executed on the + * tables to ensure syncing should function according to the input + * pattern. Debug errors and warnings are reported per table. + */ + getDebugTablesInfo(tablePatterns: TablePattern[]): Promise; + + /** + * @returns The replication lag: that is the amount of data which has not been + * replicated yet, in bytes. + */ + getReplicationLag(): Promise; + + /** + * Get the current LSN or equivalent replication position identifier + */ + getCheckpoint(): Promise; + + /** + * @returns The schema for tables inside the connected database. This is typically + * used to validate sync rules. + * Side Note: https://github.com/powersync-ja/powersync-service/blob/33bbb8c0ab1c48555956593f427fc674a8f15768/packages/types/src/definitions.ts#L100 + * contains `pg_type` which we might need to deprecate and add another generic + * type field - or just use this field as the connection specific type. + */ + getConnectionSchema(): Promise; + + /** + * Executes a SQL statement and returns the result. This is currently used in the + * admin API which is exposed in Collide. + */ + executeSQL(sql: string, params: any[]): Promise; + + //CRUD API : I don't think this is used besides maybe niche dev use cases +} + +// TODO: Export this when the existing definition in WALConnection is removed +interface PatternResult { + schema: string; + pattern: string; + wildcard: boolean; + tables?: types.TableInfo[]; + table?: types.TableInfo; +} diff --git a/packages/service-core/src/api/SyncAPIProvider.ts b/packages/service-core/src/api/SyncAPIProvider.ts new file mode 100644 index 000000000..ac1cb276e --- /dev/null +++ b/packages/service-core/src/api/SyncAPIProvider.ts @@ -0,0 +1,26 @@ +import { SyncAPI } from './SyncAPI.js'; +import { logger } from '@powersync/lib-services-framework'; + +/** + * Serves as a registry from which SyncAPIs can be retrieved based on Replication DataSource type + * Initially only one SyncAPI per DataSource type is supported + */ +export class SyncAPIProvider { + private api: SyncAPI | undefined; + + public register(api: SyncAPI) { + if (this.api) { + logger.warn('A SyncAPI has already been registered. Overriding existing implementation'); + } + + this.api = api; + } + + public getSyncAPI(): SyncAPI { + if (!this.api) { + throw new Error('No SyncAPI has been registered yet.'); + } + + return this.api; + } +} \ No newline at end of file diff --git a/packages/service-core/src/api/api-index.ts b/packages/service-core/src/api/api-index.ts index f6063e867..5b73c18b8 100644 --- a/packages/service-core/src/api/api-index.ts +++ b/packages/service-core/src/api/api-index.ts @@ -1,2 +1,4 @@ export * from './diagnostics.js'; export * from './schema.js'; +export * from './SyncAPI.js'; +export * from './SyncAPIProvider.js'; From aeedc3769f4a14986c9fe8b673cc8b53e3049887 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:52:49 +0200 Subject: [PATCH 005/298] Added generic config definitions for the data sources --- .../service-core/src/util/config/compound-config-collector.ts | 1 + packages/service-core/src/util/config/types.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 3ae856604..45073b26a 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -99,6 +99,7 @@ export class CompoundConfigCollector { let config: ResolvedPowerSyncConfig = { connection: mapped[0], + data_sources: baseConfig.replication?.data_sources || [], storage: baseConfig.storage, client_keystore: keyStore, // Dev tokens only use the static keys, no external key sources diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index 94a09ba6c..52f34c7ef 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -30,6 +30,7 @@ export type SyncRulesConfig = { export type ResolvedPowerSyncConfig = { connection?: ResolvedConnection; + data_sources?: configFile.DataSourceConfig[]; storage: configFile.StorageConfig; dev: { demo_auth: boolean; From 8842f91432b3c168c26d9f8dcb8201ee3e90e065 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:55:24 +0200 Subject: [PATCH 006/298] Added the initial definitions for the modular replication support --- .../replication/core/ReplicationAdapter.ts | 78 ++++++++++++++++++ .../src/replication/core/ReplicationEngine.ts | 43 ++++++++++ .../src/replication/core/ReplicationEntity.ts | 74 +++++++++++++++++ .../src/replication/core/ReplicationModule.ts | 81 +++++++++++++++++++ .../src/replication/core/Replicator.ts | 25 ++++++ 5 files changed, 301 insertions(+) create mode 100644 packages/service-core/src/replication/core/ReplicationAdapter.ts create mode 100644 packages/service-core/src/replication/core/ReplicationEngine.ts create mode 100644 packages/service-core/src/replication/core/ReplicationEntity.ts create mode 100644 packages/service-core/src/replication/core/ReplicationModule.ts create mode 100644 packages/service-core/src/replication/core/Replicator.ts diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts new file mode 100644 index 000000000..6485926bd --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -0,0 +1,78 @@ +import { TablePattern } from '@powersync/service-sync-rules'; +import { ReplicationEntity } from './ReplicationEntity.js'; +import { SaveOptions } from '../../storage/BucketStorage.js'; + +/** + * Manage the creation and termination of connections for a datasource. + * For some data sources there will be no difference between snapshot and replication connections + */ +interface ConnectionManager { + /** + * Create a connection to use for replication. + */ + createReplicationConnection(): Promise; + + /** + * Create a connection to use for the initial snapshot replication. + * This connection should not be shared + */ + createConnection(): Promise; + + /** + * Map a datasource specific connection error to a general category of powersync errors + * @param error + */ + mapError(error: Error): ConnectionError; +} + +/** + * The ReplicationAdapter describes all the methods that are required by the + * Replicator to replicate data from a datasource into the PowerSync Bucket storage + */ +export interface ReplicationAdapter { + /** + * Unique name to identify this adapter in the PowerSync system + * Suggestion: datasource type + datasource name + */ + name(): string; + + /** + * Return a manager that can create connections to the DataSource + */ + createConnectionManager(): ConnectionManager; + + /** + * Validate any configuration on the datasource that needs to be in place for + * replication. If any configuration is missing or incorrect, an error should be thrown with the details. + */ + validateConfiguration(connection: TConnection): void; + + /** + * Get all the fully qualified entities that match the provided pattern + * @param connection + * @param pattern // TODO: Need something more generic than TablePattern + */ + toReplicationEntities(connection: TConnection, pattern: TablePattern): Promise[]>; + + /** + * Start replicating data, assumes that initializeData has already finished running + * Stream any changes back on the provided changeListener + * @param connection + * @param changeListener + */ + startReplication(connection: TConnection, changeListener: (change: SaveOptions) => {}): Promise; + + /** + * Immediately interrupt and stop any currently running replication. + */ + terminateReplication(): Promise; +} + +export enum ConnectionError { + INCORRECT_CREDENTIALS, + HOST_INACCESSIBLE, + CONNECTION_REFUSED, + CONNECTION_CLOSED, + RETRIEVAL_FAILED, + GENERAL +} diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts new file mode 100644 index 000000000..56c392120 --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -0,0 +1,43 @@ +import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +import { Replicator } from './Replicator.js'; + +export class ReplicationEngine { + private readonly storage: BucketStorageFactory; + private readonly replicators: Map, Replicator> = new Map(); + + constructor(storage: BucketStorageFactory) { + this.storage = storage; + } + + /** + * Create a new Replicator from the provided ReplicationAdapter. Once started the Replicator will begin + * replicating data from the DataSource to PowerSync and keep it up to date. + * + * @param adapter + */ + public register(adapter: ReplicationAdapter) { + if (this.replicators.has(adapter)) { + throw new Error(`Replicator for type ${adapter.name} already registered`); + } + this.replicators.set(adapter, new Replicator(this.storage, adapter)); + } + + /** + * Start replication on all managed Replicators + */ + public async start(): Promise { + for (const replicator of this.replicators.values()) { + await replicator.start(); + } + } + + /** + * Stop replication on all managed Replicators + */ + public async stop(): Promise { + for (const replicator of this.replicators.values()) { + await replicator.stop(); + } + } +} diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts new file mode 100644 index 000000000..01d8f7b8f --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationEntity.ts @@ -0,0 +1,74 @@ +import { SourceTable } from '../../storage/SourceTable.js'; +import { SqliteRow } from '@powersync/service-sync-rules'; + +/** + * Describes a replication entity, which is a logical representation of a table or collection in a data source. + * Extend this interface to add additional properties specific entities in the data source. + */ +export interface ReplicationEntityDescriptor { + name: string; + + /** + * Fully qualified name of the entity, including any schema/namespace prefixes + */ + fullyQualifiedName: string; + + /** + * Identifier to uniquely identify the entity in PowerSync + */ + id: string; + /** + * The entity's native identifier in the data source + */ + dataSourceEntityId: string; + /** + * The field(s) that uniquely identify an entry/row in this replication entity + */ + primaryIdentifierFields: string[]; +} + +export abstract class ReplicationEntity { + public descriptor: T; + + /** + * Defaults to true for tests. + */ + public syncData: boolean = true; + + /** + * Defaults to true for tests. + */ + public syncParameters: boolean = true; + + /** + * Indicates whether the snapshot of the entity has already been completed + */ + public snapshotComplete: boolean = false; + + constructor(descriptor: T) { + this.descriptor = descriptor; + } + + public hasPrimaryIdentifierFields(): boolean { + return this.descriptor.primaryIdentifierFields.length > 0; + } + + public syncAny() { + return this.syncData || this.syncParameters; + } + + /** + * Get the number of entries for this Entity + * @param connection + */ + public abstract count(connection: TConnection): Promise; + + /** + * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. + * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. + * This LSN will be used as the starting point for the replication stream. + * @param connection + * @param entryConsumer + */ + public abstract getSnapshot(connection: TConnection, entryConsumer: (batch: SqliteRow[]) => {}): Promise; +} diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts new file mode 100644 index 000000000..70b93f338 --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -0,0 +1,81 @@ +import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSyncConfig.js'; +import * as t from 'ts-codec'; +import { logger, schema } from '@powersync/lib-services-framework'; +import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { SyncAPI } from '../../api/SyncAPI.js'; +import { AbstractModule } from '../../modules/AbstractModule.js'; +import { ServiceContext } from '../../system/ServiceContext.js'; + +/** + * A replication module describes all the functionality that PowerSync requires to + * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. + */ +export abstract class ReplicationModule extends AbstractModule { + protected type: string; + + /** + * @param name + * @param type The type of the data source + * @protected + */ + protected constructor(name: string, type: string) { + super(name); + this.type = type; + } + + /** + * Create the API adapter for the DataSource required by the sync API + * endpoints. + */ + protected abstract createSyncAPIAdapter(): SyncAPI; + + /** + * Create the ReplicationAdapter to be used by PowerSync replicator. + */ + protected abstract createReplicationAdapter(): ReplicationAdapter; + + /** + * Return the TS codec schema describing the required configuration values for this module. + */ + protected abstract configSchema(): t.ObjectCodec; + + /** + * Register this module's replication adapters and sync API providers if the required configuration is present. + */ + public async initialize(context: ServiceContext): Promise { + if (!context.configuration.data_sources) { + // No data source configuration found in the config skip for now + // TODO: Consider a mechanism to check for config in the ENV variables as well + return; + } + + const matchingConfig = context.configuration.data_sources.filter((dataSource) => dataSource.type === this.type); + + if (matchingConfig.length > 1) { + logger.warning( + `Multiple data sources of type ${this.type} found in the configuration. Only the first will be used.` + ); + } + + try { + // If validation fails, log the error and continue, no replication will happen for this data source + this.validateConfig(matchingConfig[0]); + context.replicationEngine.register(this.createReplicationAdapter()); + context.syncAPIProvider.register(this.createSyncAPIAdapter()); + } catch (e) { + logger.error(e); + } + } + + private validateConfig(config: DataSourceConfig): void { + const validator = schema + .parseJSONSchema(t.generateJSONSchema(this.configSchema(), { allowAdditional: true })) + .validator(); + + const valid = validator.validate(config); + + if (!valid.valid) { + throw new Error(`Failed to validate Module ${this.name} configuration: ${valid.errors.join(', ')}`); + } + } +} diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts new file mode 100644 index 000000000..12b05f062 --- /dev/null +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -0,0 +1,25 @@ +import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { BucketStorageFactory } from '../../storage/BucketStorage.js'; + +/** + * A replicator manages the mechanics for replicating data from a data source to a storage bucket. + * This includes copying across the original data set and then keeping it in sync with the data source. + * TODO: Implement this. This will replace the current WallStreamManager + */ +export class Replicator { + private readonly adapter: ReplicationAdapter; + private storage: BucketStorageFactory; + + constructor(storage: BucketStorageFactory, adapter: ReplicationAdapter) { + this.adapter = adapter; + this.storage = storage; + } + + public async start(): Promise { + // start the replication + } + + public async stop(): Promise { + // stop the replication + } +} From 1f7389522f5f69e091d5a9ea2ef8be736819e522 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:55:41 +0200 Subject: [PATCH 007/298] Lock file update --- pnpm-lock.yaml | 9127 +++++++++++++++++++----------------------------- 1 file changed, 3612 insertions(+), 5515 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3134582d4..2c250d75f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '9.0' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -10,7 +10,7 @@ importers: devDependencies: '@changesets/cli': specifier: ^2.27.3 - version: 2.27.3 + version: 2.27.7 '@types/node': specifier: 18.11.11 version: 18.11.11 @@ -19,13 +19,13 @@ importers: version: 3.2.5 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 concurrently: specifier: ^8.2.2 version: 8.2.2 inquirer: specifier: ^9.2.7 - version: 9.2.22 + version: 9.3.5 npm-check-updates: specifier: ^16.10.15 version: 16.14.20 @@ -52,19 +52,19 @@ importers: version: 5.2.2 ws: specifier: ^8.2.3 - version: 8.2.3 + version: 8.18.0 libs/lib-services: dependencies: ajv: specifier: ^8.12.0 - version: 8.14.0 + version: 8.16.0 better-ajv-errors: specifier: ^1.2.0 - version: 1.2.0(ajv@8.14.0) + version: 1.2.0(ajv@8.16.0) bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -76,14 +76,14 @@ importers: version: 1.2.2 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 zod: specifier: ^3.23.8 version: 3.23.8 devDependencies: '@types/lodash': specifier: ^4.17.5 - version: 4.17.5 + version: 4.17.6 vitest: specifier: ^0.34.6 version: 0.34.6 @@ -93,6 +93,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:^ version: link:../jsonbig + '@powersync/service-sync-rules': + specifier: workspace:^ + version: link:../sync-rules '@powersync/service-types': specifier: workspace:^ version: link:../types @@ -101,7 +104,7 @@ importers: version: 3.6.0 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 packages/jsonbig: dependencies: @@ -125,7 +128,7 @@ importers: version: 9.0.1 ws: specifier: ^8.17.0 - version: 8.17.0 + version: 8.18.0 devDependencies: '@types/uuid': specifier: ^9.0.4 @@ -135,7 +138,7 @@ importers: version: 8.2.3 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 rsocket-websocket-client: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 @@ -162,7 +165,7 @@ importers: version: 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': specifier: ^1.24.1 - version: 1.24.1(@opentelemetry/api@1.8.0) + version: 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': specifier: 1.24.1 version: 1.24.1(@opentelemetry/api@1.8.0) @@ -192,7 +195,7 @@ importers: version: 0.5.0 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 commander: specifier: ^12.0.0 version: 12.1.0 @@ -207,22 +210,22 @@ importers: version: 5.0.0 jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 lodash: specifier: ^4.17.21 version: 4.17.21 lru-cache: specifier: ^10.2.2 - version: 10.2.2 + version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) + version: 6.8.0 node-fetch: specifier: ^3.3.2 version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -231,17 +234,17 @@ importers: version: 9.0.1 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 yaml: specifier: ^2.3.2 - version: 2.4.2 + version: 2.4.5 devDependencies: '@types/async': specifier: ^3.2.24 version: 3.2.24 '@types/lodash': specifier: ^4.17.5 - version: 4.17.5 + version: 4.17.6 '@types/uuid': specifier: ^9.0.4 version: 9.0.8 @@ -253,10 +256,10 @@ importers: version: 4.5.1 typescript: specifier: ^5.2.2 - version: 5.4.5 + version: 5.2.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@18.11.11)) + version: 4.3.2(typescript@5.2.2) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -271,13 +274,13 @@ importers: version: 0.5.2 ajv: specifier: ^8.12.0 - version: 8.14.0 + version: 8.16.0 pgsql-ast-parser: specifier: ^11.1.0 version: 11.2.0 yaml: specifier: ^2.3.1 - version: 2.4.2 + version: 2.4.5 devDependencies: '@types/node': specifier: 18.11.11 @@ -332,13 +335,13 @@ importers: version: link:../packages/types '@sentry/node': specifier: ^8.9.2 - version: 8.9.2 + version: 8.17.0 async-mutex: specifier: ^0.5.0 version: 0.5.0 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 commander: specifier: ^12.0.0 version: 12.1.0 @@ -356,19 +359,19 @@ importers: version: 5.0.0 jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 lru-cache: specifier: ^10.0.1 - version: 10.2.2 + version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) + version: 6.8.0 node-fetch: specifier: ^3.3.2 version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -377,14 +380,14 @@ importers: version: 9.0.1 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 yaml: specifier: ^2.3.2 - version: 2.4.2 + version: 2.4.5 devDependencies: '@sentry/types': specifier: ^8.9.2 - version: 8.9.2 + version: 8.17.0 '@types/uuid': specifier: ^9.0.4 version: 9.0.8 @@ -393,7 +396,7 @@ importers: version: 2.4.1 nodemon: specifier: ^3.0.1 - version: 3.1.1 + version: 3.1.4 npm-check-updates: specifier: ^16.14.4 version: 16.14.20 @@ -409,4137 +412,778 @@ importers: packages: - '@babel/code-frame@7.24.6': - resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==} + /@babel/code-frame@7.24.7: + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + dev: false - '@babel/helper-validator-identifier@7.24.6': - resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==} + /@babel/helper-validator-identifier@7.24.7: + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} + dev: false - '@babel/highlight@7.24.6': - resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==} + /@babel/highlight@7.24.7: + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + dev: false - '@babel/runtime@7.24.6': - resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==} + /@babel/runtime@7.24.8: + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: true - '@changesets/apply-release-plan@7.0.1': - resolution: {integrity: sha512-aPdSq/R++HOyfEeBGjEe6LNG8gs0KMSyRETD/J2092OkNq8mOioAxyKjMbvVUdzgr/HTawzMOz7lfw339KnsCA==} + /@changesets/apply-release-plan@7.0.4: + resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/config': 3.0.2 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.6.2 + dev: true - '@changesets/assemble-release-plan@6.0.0': - resolution: {integrity: sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==} + /@changesets/assemble-release-plan@6.0.3: + resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.6.2 + dev: true - '@changesets/changelog-git@0.2.0': + /@changesets/changelog-git@0.2.0: resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + dependencies: + '@changesets/types': 6.0.0 + dev: true - '@changesets/cli@2.27.3': - resolution: {integrity: sha512-ve/VpWApILlSs8cr0okNx5C2LKRawI9XZgvfmf58S8sar2nhx5DPJREFXYZBahs0FeTfvH0rdVl+nGe8QF45Ig==} + /@changesets/cli@2.27.7: + resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} hasBin: true + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/apply-release-plan': 7.0.4 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.2 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/get-release-plan': 4.0.3 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.1 + '@manypkg/get-packages': 1.1.3 + '@types/semver': 7.5.8 + ansi-colors: 4.1.3 + chalk: 2.4.2 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + mri: 1.2.0 + outdent: 0.5.0 + p-limit: 2.3.0 + preferred-pm: 3.1.4 + resolve-from: 5.0.0 + semver: 7.6.2 + spawndamnit: 2.0.0 + term-size: 2.2.1 + dev: true - '@changesets/config@3.0.0': - resolution: {integrity: sha512-o/rwLNnAo/+j9Yvw9mkBQOZySDYyOr/q+wptRLcAVGlU6djOeP9v1nlalbL9MFsobuBVQbZCTp+dIzdq+CLQUA==} + /@changesets/config@3.0.2: + resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/logger': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.7 + dev: true - '@changesets/errors@0.2.0': + /@changesets/errors@0.2.0: resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + dependencies: + extendable-error: 0.1.7 + dev: true - '@changesets/get-dependents-graph@2.0.0': - resolution: {integrity: sha512-cafUXponivK4vBgZ3yLu944mTvam06XEn2IZGjjKc0antpenkYANXiiE6GExV/yKdsCnE8dXVZ25yGqLYZmScA==} + /@changesets/get-dependents-graph@2.1.1: + resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + chalk: 2.4.2 + fs-extra: 7.0.1 + semver: 7.6.2 + dev: true - '@changesets/get-release-plan@4.0.0': - resolution: {integrity: sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w==} + /@changesets/get-release-plan@4.0.3: + resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/config': 3.0.2 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + dev: true - '@changesets/get-version-range-type@0.4.0': + /@changesets/get-version-range-type@0.4.0: resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + dev: true - '@changesets/git@3.0.0': + /@changesets/git@3.0.0: resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.7 + spawndamnit: 2.0.0 + dev: true - '@changesets/logger@0.1.0': + /@changesets/logger@0.1.0: resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + dependencies: + chalk: 2.4.2 + dev: true - '@changesets/parse@0.4.0': + /@changesets/parse@0.4.0: resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + dev: true - '@changesets/pre@2.0.0': + /@changesets/pre@2.0.0: resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + dev: true - '@changesets/read@0.6.0': + /@changesets/read@0.6.0: resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + chalk: 2.4.2 + fs-extra: 7.0.1 + p-filter: 2.1.0 + dev: true + + /@changesets/should-skip-package@0.1.0: + resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + dev: true - '@changesets/types@4.1.0': + /@changesets/types@4.1.0: resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + dev: true - '@changesets/types@6.0.0': + /@changesets/types@6.0.0: resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + dev: true - '@changesets/write@0.3.1': + /@changesets/write@0.3.1: resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + dev: true - '@colors/colors@1.5.0': + /@colors/colors@1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + requiresBuild: true + dev: true + optional: true - '@colors/colors@1.6.0': + /@colors/colors@1.6.0: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} + dev: false - '@cspotcode/source-map-support@0.8.1': + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true - '@dabh/diagnostics@2.0.3': + /@dabh/diagnostics@2.0.3: resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + dev: false - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + /@esbuild/aix-ppc64@0.21.5: + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + /@esbuild/android-arm64@0.21.5: + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + /@esbuild/android-arm@0.21.5: + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + /@esbuild/android-x64@0.21.5: + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + /@esbuild/darwin-arm64@0.21.5: + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + /@esbuild/darwin-x64@0.21.5: + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + /@esbuild/freebsd-arm64@0.21.5: + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + /@esbuild/freebsd-x64@0.21.5: + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + /@esbuild/linux-arm64@0.21.5: + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + /@esbuild/linux-arm@0.21.5: + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + /@esbuild/linux-ia32@0.21.5: + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + /@esbuild/linux-loong64@0.21.5: + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + /@esbuild/linux-mips64el@0.21.5: + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + /@esbuild/linux-ppc64@0.21.5: + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + /@esbuild/linux-riscv64@0.21.5: + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + /@esbuild/linux-s390x@0.21.5: + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + /@esbuild/linux-x64@0.21.5: + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + /@esbuild/netbsd-x64@0.21.5: + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + /@esbuild/openbsd-x64@0.21.5: + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + /@esbuild/sunos-x64@0.21.5: + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + /@esbuild/win32-arm64@0.21.5: + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + /@esbuild/win32-ia32@0.21.5: + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + /@esbuild/win32-x64@0.21.5: + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@fastify/ajv-compiler@3.5.0': - resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} + /@fastify/ajv-compiler@3.6.0: + resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} + dependencies: + ajv: 8.16.0 + ajv-formats: 2.1.1(ajv@8.16.0) + fast-uri: 2.4.0 - '@fastify/cors@8.4.1': + /@fastify/cors@8.4.1: resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} + dependencies: + fastify-plugin: 4.5.1 + mnemonist: 0.39.5 + dev: false - '@fastify/error@3.4.1': + /@fastify/error@3.4.1: resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} - '@fastify/fast-json-stringify-compiler@4.3.0': + /@fastify/fast-json-stringify-compiler@4.3.0: resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + dependencies: + fast-json-stringify: 5.16.1 - '@fastify/merge-json-schemas@0.1.1': + /@fastify/merge-json-schemas@0.1.1: resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + dependencies: + fast-deep-equal: 3.1.3 - '@gar/promisify@1.1.3': + /@gar/promisify@1.1.3: resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + dev: true - '@humanwhocodes/momoa@2.0.4': + /@humanwhocodes/momoa@2.0.4: resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} engines: {node: '>=10.10.0'} + dev: false - '@inquirer/figures@1.0.2': - resolution: {integrity: sha512-4F1MBwVr3c/m4bAUef6LgkvBfSjzwH+OfldgHqcuacWwSUetFebM2wi58WfG9uk1rR98U6GwLed4asLJbwdV5w==} + /@inquirer/figures@1.0.3: + resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} engines: {node: '>=18'} + dev: true - '@isaacs/cliui@8.0.2': + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true - '@jest/schemas@29.6.3': + /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true - '@jridgewell/resolve-uri@3.1.2': + /@jridgewell/resolve-uri@3.1.2: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + dev: true - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + /@jridgewell/sourcemap-codec@1.5.0: + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + dev: true - '@jridgewell/trace-mapping@0.3.9': + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true - '@js-sdsl/ordered-set@4.4.2': + /@js-sdsl/ordered-set@4.4.2: resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} + dev: false - '@ljharb/through@2.3.13': - resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} - engines: {node: '>= 0.4'} - - '@manypkg/find-root@1.1.0': + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + dependencies: + '@babel/runtime': 7.24.8 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + dev: true - '@manypkg/get-packages@1.1.3': + /@manypkg/get-packages@1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + dev: true - '@mongodb-js/saslprep@1.1.7': + /@mongodb-js/saslprep@1.1.7: resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + dependencies: + sparse-bitfield: 3.0.3 + dev: false - '@nodelib/fs.scandir@2.1.5': + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true - '@nodelib/fs.stat@2.0.5': + /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} + dev: true - '@nodelib/fs.walk@1.2.8': + /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + dev: true - '@npmcli/fs@2.1.2': + /@npmcli/fs@2.1.2: resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.6.2 + dev: true - '@npmcli/fs@3.1.1': + /@npmcli/fs@3.1.1: resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + semver: 7.6.2 + dev: true - '@npmcli/git@4.1.0': + /@npmcli/git@4.1.0: resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + '@npmcli/promise-spawn': 6.0.2 + lru-cache: 7.18.3 + npm-pick-manifest: 8.0.2 + proc-log: 3.0.0 + promise-inflight: 1.0.1 + promise-retry: 2.0.1 + semver: 7.6.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + dev: true - '@npmcli/installed-package-contents@2.1.0': + /@npmcli/installed-package-contents@2.1.0: resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true + dependencies: + npm-bundled: 3.0.1 + npm-normalize-package-bin: 3.0.1 + dev: true - '@npmcli/move-file@2.0.1': + /@npmcli/move-file@2.0.1: resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + dev: true - '@npmcli/node-gyp@3.0.0': + /@npmcli/node-gyp@3.0.0: resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - '@npmcli/promise-spawn@6.0.2': + /@npmcli/promise-spawn@6.0.2: resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + which: 3.0.1 + dev: true - '@npmcli/run-script@6.0.2': + /@npmcli/run-script@6.0.2: resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + '@npmcli/node-gyp': 3.0.0 + '@npmcli/promise-spawn': 6.0.2 + node-gyp: 9.4.1 + read-package-json-fast: 3.0.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + dev: true - '@opentelemetry/api-logs@0.51.1': + /@opentelemetry/api-logs@0.51.1: resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} engines: {node: '>=14'} + dependencies: + '@opentelemetry/api': 1.8.0 + dev: false - '@opentelemetry/api-logs@0.52.0': - resolution: {integrity: sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q==} + /@opentelemetry/api-logs@0.52.1: + resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} engines: {node: '>=14'} + dependencies: + '@opentelemetry/api': 1.6.0 + dev: false - '@opentelemetry/api@1.6.0': + /@opentelemetry/api@1.6.0: resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} engines: {node: '>=8.0.0'} + dev: false - '@opentelemetry/api@1.8.0': + /@opentelemetry/api@1.8.0: resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} engines: {node: '>=8.0.0'} + dev: false - '@opentelemetry/api@1.9.0': + /@opentelemetry/api@1.9.0: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + dev: false - '@opentelemetry/context-async-hooks@1.25.0': - resolution: {integrity: sha512-sBW313mnMyFg0cp/40BRzrZBWG+581s2j5gIsa5fgGadswyILk4mNFATsqrCOpAx945RDuZ2B7ThQLgor9OpfA==} + /@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + dev: false - '@opentelemetry/core@1.17.0': + /@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0): resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.7.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/semantic-conventions': 1.17.0 + dev: false - '@opentelemetry/core@1.24.1': + /@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0): resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.9.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/core@1.25.0': - resolution: {integrity: sha512-n0B3s8rrqGrasTgNkXLKXzN0fXo+6IYP7M5b7AMsrZM33f/y6DS6kJ0Btd7SespASWq8bgL3taLo0oe0vB52IQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/exporter-metrics-otlp-http@0.51.1': - resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} + /@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.9.0' + dependencies: + '@opentelemetry/api': 1.8.0 + '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/exporter-prometheus@0.43.0': - resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} + /@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.9.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/exporter-prometheus@0.51.1': - resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} + /@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/instrumentation-connect@0.37.0': - resolution: {integrity: sha512-SeQktDIH5rNzjiEiazWiJAIXkmnLOnNV7wwHpahrqE0Ph+Z3heqMfxRtoMtbdJSIYLfcNZYO51AjxZ00IXufdw==} + /@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.8.0 + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/instrumentation-express@0.40.1': - resolution: {integrity: sha512-+RKMvVe2zw3kIXRup9c1jFu3T4d0fs5aKy015TpiMyoCKX1UMu3Z0lfgYtuyiSTANvg5hZnDbWmQmqSPj9VTvg==} + /@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/instrumentation-fastify@0.37.0': - resolution: {integrity: sha512-WRjwzNZgupSzbEYvo9s+QuHJRqZJjVdNxSEpGBwWK8RKLlHGwGVAu0gcc2gPamJWUJsGqPGvahAPWM18ZkWj6A==} + /@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-graphql@0.41.0': - resolution: {integrity: sha512-R/gXeljgIhaRDKquVkKYT5QHPnFouM8ooyePZEP0kqyaVAedtR1V7NfAUJbxfTG5fBQa5wdmLjvu63+tzRXZCA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-hapi@0.39.0': - resolution: {integrity: sha512-ik2nA9Yj2s2ay+aNY+tJsKCsEx6Tsc2g/MK0iWBW5tibwrWKTy1pdVt5sB3kd5Gkimqj23UV5+FH2JFcQLeKug==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-http@0.52.0': - resolution: {integrity: sha512-E6ywZuxTa4LnVXZGwL1oj3e2Eog1yIaNqa8KjKXoGkDNKte9/SjQnePXOmhQYI0A9nf0UyFbP9aKd+yHrkJXUA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-ioredis@0.41.0': - resolution: {integrity: sha512-rxiLloU8VyeJGm5j2fZS8ShVdB82n7VNP8wTwfUQqDwRfHCnkzGr+buKoxuhGD91gtwJ91RHkjHA1Eg6RqsUTg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-koa@0.41.0': - resolution: {integrity: sha512-mbPnDt7ELvpM2S0vixYUsde7122lgegLOJQxx8iJQbB8YHal/xnTh9v7IfArSVzIDo+E+080hxZyUZD4boOWkw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongodb@0.45.0': - resolution: {integrity: sha512-xnZP9+ayeB1JJyNE9cIiwhOJTzNEsRhXVdLgfzmrs48Chhhk026mQdM5CITfyXSCfN73FGAIB8d91+pflJEfWQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongoose@0.39.0': - resolution: {integrity: sha512-J1r66A7zJklPPhMtrFOO7/Ud2p0Pv5u8+r23Cd1JUH6fYPmftNJVsLp2urAt6PHK4jVqpP/YegN8wzjJ2mZNPQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql2@0.39.0': - resolution: {integrity: sha512-Iypuq2z6TCfriAXCIZjRq8GTFCKhQv5SpXbmI+e60rYdXw8NHtMH4NXcGF0eKTuoCsC59IYSTUvDQYDKReaszA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql@0.39.0': - resolution: {integrity: sha512-8snHPh83rhrDf31v9Kq0Nf+ts8hdr7NguuszRqZomZBHgE0+UyXZSkXHAAFZoBPPRMGyM68uaFE5hVtFl+wOcA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-nestjs-core@0.38.0': - resolution: {integrity: sha512-M381Df1dM8aqihZz2yK+ugvMFK5vlHG/835dc67Sx2hH4pQEQYDA2PpFPTgc9AYYOydQaj7ClFQunESimjXDgg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-pg@0.42.0': - resolution: {integrity: sha512-sjgcM8CswYy8zxHgXv4RAZ09DlYhQ+9TdlourUs63Df/ek5RrB1ZbjznqW7PB6c3TyJJmX6AVtPTjAsROovEjA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-redis-4@0.40.0': - resolution: {integrity: sha512-0ieQYJb6yl35kXA75LQUPhHtGjtQU9L85KlWa7d4ohBbk/iQKZ3X3CFl5jC5vNMq/GGPB3+w3IxNvALlHtrp7A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation@0.43.0': - resolution: {integrity: sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation@0.51.1': - resolution: {integrity: sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation@0.52.0': - resolution: {integrity: sha512-LPwSIrw+60cheWaXsfGL8stBap/AppKQJFE+qqRvzYrgttXFH2ofoIMxWadeqPTq4BYOXM/C7Bdh/T+B60xnlQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/otlp-exporter-base@0.51.1': - resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.0.0 - - '@opentelemetry/otlp-transformer@0.51.1': - resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' - - '@opentelemetry/redis-common@0.36.2': - resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} - engines: {node: '>=14'} - - '@opentelemetry/resources@1.17.0': - resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.7.0' - - '@opentelemetry/resources@1.24.1': - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' - - '@opentelemetry/resources@1.25.0': - resolution: {integrity: sha512-iHjydPMYJ+Li1auveJCq2rp5U2h6Mhq8BidiyE0jfVlDTFyR1ny8AfJHfmFzJ/RAM8vT8L7T21kcmGybxZC7lQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/sdk-logs@0.51.1': - resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.9.0' - '@opentelemetry/api-logs': '>=0.39.1' - - '@opentelemetry/sdk-metrics@1.17.0': - resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.7.0' - - '@opentelemetry/sdk-metrics@1.24.1': - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' - - '@opentelemetry/sdk-trace-base@1.24.1': - resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' - - '@opentelemetry/sdk-trace-base@1.25.0': - resolution: {integrity: sha512-6+g2fiRQUG39guCsKVeY8ToeuUf3YUnPkN6DXRA1qDmFLprlLvZm9cS6+chgbW70cZJ406FTtSCDnJwxDC5sGQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/semantic-conventions@1.17.0': - resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} - engines: {node: '>=14'} - - '@opentelemetry/semantic-conventions@1.24.1': - resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} - engines: {node: '>=14'} - - '@opentelemetry/semantic-conventions@1.25.0': - resolution: {integrity: sha512-M+kkXKRAIAiAP6qYyesfrC5TOmDpDVtsxuGfPcqd9B/iBrac+E14jYwrgm0yZBUIbIP2OnqC3j+UgkXLm1vxUQ==} - engines: {node: '>=14'} - - '@opentelemetry/sql-common@0.40.1': - resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.1.0 - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@pnpm/config.env-replace@1.1.0': - resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} - engines: {node: '>=12.22.0'} - - '@pnpm/network.ca-file@1.0.2': - resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} - engines: {node: '>=12.22.0'} - - '@pnpm/npm-conf@2.2.2': - resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} - engines: {node: '>=12'} - - '@prisma/instrumentation@5.15.0': - resolution: {integrity: sha512-fCWOOOajTKOUEp43gRmBqwt6oN9bPJcLiloi2OG/2ED0N5z62Cuza6FDrlm3SJHQAXYlXqLE0HLdEE5WcUkOzg==} - - '@rollup/rollup-android-arm-eabi@4.18.0': - resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.18.0': - resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.18.0': - resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.18.0': - resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.18.0': - resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.18.0': - resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.18.0': - resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.18.0': - resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.18.0': - resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-win32-arm64-msvc@4.18.0': - resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.18.0': - resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.18.0': - resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} - cpu: [x64] - os: [win32] - - '@sentry/core@8.9.2': - resolution: {integrity: sha512-ixm8NISFlPlEo3FjSaqmq4nnd13BRHoafwJ5MG+okCz6BKGZ1SexEggP42/QpGvDprUUHnfncG6WUMgcarr1zA==} - engines: {node: '>=14.18'} - - '@sentry/node@8.9.2': - resolution: {integrity: sha512-Q+JBpR4yx3eUyyhwgugucfRtPg65gYvzJGEmjzcnDJXJqX8ms4HPpNv9o2Om7A4014JxIibUdrQ+p5idcT7SZA==} - engines: {node: '>=14.18'} - - '@sentry/opentelemetry@8.9.2': - resolution: {integrity: sha512-Q6SHDQhrsBPcMi7ejqVdNTkt6SCTIhpGsFN8QR7daH3uvM0X2O7ciCuO9gRNRTEkflEINV4SBZEjANYH7BkRAg==} - engines: {node: '>=14.18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/core': ^1.25.0 - '@opentelemetry/instrumentation': ^0.52.0 - '@opentelemetry/sdk-trace-base': ^1.25.0 - '@opentelemetry/semantic-conventions': ^1.25.0 - - '@sentry/types@8.9.2': - resolution: {integrity: sha512-+LFOyQGl+zk5SZRGZD2MEURf7i5RHgP/mt3s85Rza+vz8M211WJ0YsjkIGUJFSY842nged5QLx4JysLaBlLymg==} - engines: {node: '>=14.18'} - - '@sentry/utils@8.9.2': - resolution: {integrity: sha512-A4srR9mEBFdVXwSEKjQ94msUbVkMr8JeFiEj9ouOFORw/Y/ux/WV2bWVD/ZI9wq0TcTNK8L1wBgU8UMS5lIq3A==} - engines: {node: '>=14.18'} - - '@sigstore/bundle@1.1.0': - resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sigstore/protobuf-specs@0.2.1': - resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sigstore/sign@1.0.0': - resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sigstore/tuf@1.0.3': - resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - - '@sindresorhus/is@5.6.0': - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} - - '@syncpoint/wkx@0.5.2': - resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} - - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} - - '@tootallnate/once@2.0.0': - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@tufjs/canonical-json@1.0.0': - resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@tufjs/models@1.0.4': - resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@types/accepts@1.3.7': - resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} - - '@types/async@3.2.24': - resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - - '@types/chai-subset@1.3.5': - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} - - '@types/chai@4.3.16': - resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} - - '@types/connect@3.4.36': - resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/content-disposition@0.5.8': - resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} - - '@types/cookies@0.9.0': - resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} - - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - - '@types/express-serve-static-core@4.19.1': - resolution: {integrity: sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==} - - '@types/express@4.17.21': - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} - - '@types/http-assert@1.5.5': - resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} - - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - - '@types/keygrip@1.0.6': - resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} - - '@types/koa-compose@3.2.8': - resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} - - '@types/koa@2.14.0': - resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==} - - '@types/koa__router@12.0.3': - resolution: {integrity: sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==} - - '@types/lodash@4.17.5': - resolution: {integrity: sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==} - - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - - '@types/minimist@1.2.5': - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - - '@types/mysql@2.15.22': - resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} - - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - - '@types/node@13.13.52': - resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} - - '@types/node@15.14.9': - resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - - '@types/node@18.11.11': - resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - - '@types/pg-pool@2.0.4': - resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} - - '@types/pg@8.6.1': - resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} - - '@types/qs@6.9.15': - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/semver-utils@1.1.3': - resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} - - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - - '@types/shimmer@1.0.5': - resolution: {integrity: sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==} - - '@types/strip-bom@3.0.0': - resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - - '@types/strip-json-comments@0.0.30': - resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - - '@types/triple-beam@1.3.5': - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - - '@types/webidl-conversions@7.0.3': - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - - '@types/whatwg-url@11.0.5': - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} - - '@types/ws@8.2.3': - resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} - - '@vitest/expect@0.34.6': - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} - - '@vitest/runner@0.34.6': - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} - - '@vitest/snapshot@0.34.6': - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} - - '@vitest/spy@0.34.6': - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} - - '@vitest/utils@0.34.6': - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} - - abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - - abstract-logging@2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - - acorn-import-assertions@1.9.0: - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} - peerDependencies: - acorn: ^8 - - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} - engines: {node: '>=0.4.0'} - - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} - - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv@8.14.0: - resolution: {integrity: sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==} - - ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - - are-we-there-yet@3.0.1: - resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - - arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - - async-mutex@0.5.0: - resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} - - async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - avvio@8.3.2: - resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - better-ajv-errors@1.2.0: - resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - ajv: 4.11.8 - 8 - - better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - boxen@7.1.1: - resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} - engines: {node: '>=14.16'} - - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - breakword@1.0.6: - resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} - - bson@6.7.0: - resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==} - engines: {node: '>=16.20.1'} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - - cacache@16.1.3: - resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - cacache@17.1.4: - resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} - - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - - camelcase-keys@6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - - chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.3.1: - resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} - - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - - cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} - - cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - - color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} - - colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} - hasBin: true - - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - - config-chain@1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - - configstore@6.0.0: - resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} - engines: {node: '>=12'} - - console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - - copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} - - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - - crypto-random-string@4.0.0: - resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} - engines: {node: '>=12'} - - csv-generate@3.4.3: - resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} - - csv-parse@4.16.3: - resolution: {integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==} - - csv-stringify@5.6.5: - resolution: {integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==} - - csv@5.5.3: - resolution: {integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==} - engines: {node: '>= 0.1.90'} - - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} - - date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} - - date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-eql@4.1.3: - resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} - engines: {node: '>=6'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - discontinuous-range@1.0.0: - resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} - - dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} - - dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - - duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - - dynamic-dedupe@0.3.0: - resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - - encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - - esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} - engines: {node: '>=12'} - hasBin: true - - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - - escape-goat@4.0.0: - resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} - engines: {node: '>=12'} - - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - event-stream@3.3.4: - resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - exponential-backoff@3.1.1: - resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} - - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - - fast-content-type-parse@1.1.0: - resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} - - fast-decode-uri-component@1.0.1: - resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - - fast-json-stringify@5.16.0: - resolution: {integrity: sha512-A4bg6E15QrkuVO3f0SwIASgzMzR6XC4qTyTqhf3hYXy0iazbAdZKwkE+ox4WgzKyzM6ygvbdq3r134UjOaaAnA==} - - fast-memoize@2.5.2: - resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} - - fast-querystring@1.1.2: - resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} - - fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} - - fast-uri@2.3.0: - resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} - - fastify-plugin@4.5.1: - resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} - - fastify@4.23.2: - resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} - - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - - fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-my-way@7.7.0: - resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} - engines: {node: '>=14'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - find-yarn-workspace-root2@1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} - - fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} - - form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fp-and-or@0.1.4: - resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} - engines: {node: '>=10'} - - from@0.1.7: - resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} - - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - gauge@4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - - get-stdin@8.0.0: - resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} - engines: {node: '>=10'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@10.4.1: - resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} - engines: {node: '>=16 || 14 >=14.18'} - hasBin: true - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported - - global-dirs@3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - - got@12.6.1: - resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} - engines: {node: '>=14.16'} - - graceful-fs@4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - - hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} - - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - - has-yarn@3.0.0: - resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - hosted-git-info@5.2.1: - resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - hosted-git-info@6.1.1: - resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - - http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - - http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - - ignore-walk@6.0.5: - resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - - import-in-the-middle@1.4.2: - resolution: {integrity: sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==} - - import-in-the-middle@1.7.4: - resolution: {integrity: sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==} - - import-in-the-middle@1.8.0: - resolution: {integrity: sha512-/xQjze8szLNnJ5rvHSzn+dcVXqCAU6Plbk4P24U/jwPmg1wy7IIp9OjKIO5tYue8GSPhDpPDiApQjvBUmWwhsQ==} - - import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - infer-owner@1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - ini@2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - - ini@4.1.3: - resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - inquirer@9.2.22: - resolution: {integrity: sha512-SqLLa/Oe5rZUagTR9z+Zd6izyatHglbmbvVofo1KzuVB54YHleWzeHNLoR7FOICGOeQSqeLh1cordb3MzhGcEw==} - engines: {node: '>=18'} - - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} - engines: {node: '>= 10'} - - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} - - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-installed-globally@0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} - - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - - is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-npm@6.0.0: - resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - - is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - is-yarn-global@0.4.1: - resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} - engines: {node: '>=12'} - - isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - ix@5.0.0: - resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} - - jackspeak@3.1.2: - resolution: {integrity: sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==} - engines: {node: '>=14'} - - jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - - jose@4.15.5: - resolution: {integrity: sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - json-parse-helpfulerror@1.0.3: - resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} - - json-schema-ref-resolver@1.0.1: - resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonlines@0.1.1: - resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} - - jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - - jsonpointer@5.0.1: - resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} - engines: {node: '>=0.10.0'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - - latest-version@7.0.0: - resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} - engines: {node: '>=14.16'} - - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - light-my-request@5.13.0: - resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - load-yaml-file@0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} - - local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - logform@2.6.0: - resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} - engines: {node: '>= 12.0.0'} - - lossless-json@2.0.11: - resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} - - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - - lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lru-cache@10.2.2: - resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} - engines: {node: 14 || >=16.14} - - lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} - - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - - magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - make-fetch-happen@10.2.1: - resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - make-fetch-happen@11.1.1: - resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - - map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - - map-stream@0.1.0: - resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} - - memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - - meow@6.1.1: - resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} - engines: {node: '>=8'} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} - - minipass-fetch@2.1.2: - resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - minipass-fetch@3.0.5: - resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - - minipass-json-stream@1.0.1: - resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} - - minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - - minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mixme@0.5.10: - resolution: {integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==} - engines: {node: '>= 8.0.0'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - mlly@1.7.0: - resolution: {integrity: sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==} - - mnemonist@0.39.5: - resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} - - module-details-from-path@1.0.3: - resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} - - mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} - - mongodb@6.7.0: - resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true - - moo@0.5.2: - resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} - - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nearley@2.20.1: - resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} - hasBin: true - - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - node-cleanup@2.1.2: - resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} - - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - node-gyp@9.4.1: - resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} - engines: {node: ^12.13 || ^14.13 || >=16} - hasBin: true - - nodemon@3.1.1: - resolution: {integrity: sha512-k43xGaDtaDIcufn0Fc6fTtsdKSkV/hQzoQFigNH//GaKta28yoKVYXCnV+KXRqfT/YzsFaQU9VdeEG+HEyxr6A==} - engines: {node: '>=10'} - hasBin: true - - noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} - - nopt@6.0.0: - resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true - - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-package-data@5.0.0: - resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} - engines: {node: '>=14.16'} - - npm-bundled@3.0.1: - resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-check-updates@16.14.20: - resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} - engines: {node: '>=14.14'} - hasBin: true - - npm-install-checks@6.3.0: - resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-package-arg@10.1.0: - resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-packlist@7.0.4: - resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-pick-manifest@8.0.2: - resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-registry-fetch@14.0.5: - resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - - obliterator@2.0.4: - resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} - - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - opentelemetry-instrumentation-fetch-node@1.2.0: - resolution: {integrity: sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA==} - engines: {node: '>18.0.0'} - - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - - os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-json@8.1.1: - resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} - engines: {node: '>=14.16'} - - pacote@15.2.0: - resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - parse-github-url@1.0.2: - resolution: {integrity: sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==} - engines: {node: '>=0.10.0'} - hasBin: true - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - - pause-stream@0.0.11: - resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} - - pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - - pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} - - pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} - - pgsql-ast-parser@11.2.0: - resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} - - pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: - resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} - version: 0.7.0 - engines: {node: '>=14.18.0'} - - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - pino-abstract-transport@1.2.0: - resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} - - pino-std-serializers@6.2.2: - resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} - - pino@8.21.0: - resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} - hasBin: true - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - - pkg-types@1.1.1: - resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} - - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} - engines: {node: ^10 || ^12 || >=14} - - postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - - postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - - postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - - postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} - - preferred-pm@3.1.3: - resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} - engines: {node: '>=10'} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process-warning@2.3.2: - resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} - - process-warning@3.0.0: - resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - - promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - - prompts-ncu@3.0.0: - resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} - engines: {node: '>= 14'} - - proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - ps-tree@1.2.0: - resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} - engines: {node: '>= 0.10'} - hasBin: true - - pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - - pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - pupa@3.1.0: - resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} - engines: {node: '>=12.20'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - quick-lru@4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - railroad-diagrams@1.0.0: - resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} - - randexp@0.4.6: - resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} - engines: {node: '>=0.12'} - - rc-config-loader@4.1.3: - resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - read-package-json@6.0.4: - resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - deprecated: This package is no longer supported. Please use @npmcli/package-json instead. - - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - - readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - - registry-auth-token@5.0.2: - resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} - engines: {node: '>=14'} - - registry-url@6.0.1: - resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} - engines: {node: '>=12'} - - remote-git-tags@3.0.0: - resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} - engines: {node: '>=8'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - require-in-the-middle@7.3.0: - resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} - engines: {node: '>=8.6.0'} - - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - - responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - - ret@0.1.15: - resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} - engines: {node: '>=0.12'} - - ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} - - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rfdc@1.3.1: - resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} - - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - rimraf@5.0.7: - resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} - engines: {node: '>=14.18'} - hasBin: true - - rollup@4.18.0: - resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rsocket-core@1.0.0-alpha.3: - resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} - - rsocket-websocket-client@1.0.0-alpha.3: - resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} - - run-async@3.0.0: - resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} - engines: {node: '>=0.12.0'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - - safe-regex2@2.0.0: - resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} - - safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} - engines: {node: '>=10'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - - semver-diff@4.0.0: - resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} - engines: {node: '>=12'} - - semver-utils@1.1.4: - resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - - semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - - shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sigstore@1.9.0: - resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - - simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - smartwrap@2.0.2: - resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} - engines: {node: '>=6'} - hasBin: true - - socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} - - socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - - sonic-boom@3.8.1: - resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} - - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} - - spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - - spawn-please@2.0.2: - resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} - engines: {node: '>=14'} - - spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - split@0.3.3: - resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - - ssri@10.0.6: - resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - ssri@9.0.1: - resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - - stream-combiner@0.0.4: - resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} - - stream-transform@2.1.3: - resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} - - string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - - string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - strip-json-comments@5.0.1: - resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} - engines: {node: '>=14.16'} - - strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - - text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - - thread-stream@2.7.0: - resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} - - through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - - tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - - tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} - - tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toad-cache@3.7.0: - resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} - engines: {node: '>=12'} - - touch@3.1.1: - resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} - hasBin: true - - tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} - - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - - trim-newlines@3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - - triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - - ts-codec@1.2.2: - resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} - - ts-node-dev@2.0.0: - resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} - engines: {node: '>=0.8.0'} - hasBin: true - peerDependencies: - node-notifier: '*' - typescript: '*' - peerDependenciesMeta: - node-notifier: - optional: true - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - tsc-watch@6.2.0: - resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} - engines: {node: '>=12.12.0'} - hasBin: true - peerDependencies: - typescript: '*' - - tsconfck@3.0.3: - resolution: {integrity: sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - - tsconfig@7.0.0: - resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} - - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - - tty-table@4.2.3: - resolution: {integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA==} - engines: {node: '>=8.0.0'} - hasBin: true - - tuf-js@1.1.7: - resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} - - typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - - typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} - engines: {node: '>=14.17'} - hasBin: true - - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true - - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - - undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - - unique-filename@2.0.1: - resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - unique-slug@3.0.0: - resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - unique-string@3.0.0: - resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} - engines: {node: '>=12'} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - - update-notifier@6.0.2: - resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} - engines: {node: '>=14.16'} - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - vite-node@0.34.6: - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true - - vite-tsconfig-paths@4.3.2: - resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true - - vite@5.2.11: - resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} - - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which-pm@2.0.0: - resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} - engines: {node: '>=8.15'} - - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - why-is-node-running@2.2.2: - resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} - engines: {node: '>=8'} - hasBin: true - - wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - - widest-line@4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} - - winston-transport@4.7.0: - resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} - engines: {node: '>= 12.0.0'} - - winston@3.13.0: - resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} - engines: {node: '>= 12.0.0'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - - ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.2.3: - resolution: {integrity: sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xdg-basedir@5.1.0: - resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} - engines: {node: '>=12'} - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml@2.4.2: - resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==} - engines: {node: '>= 14'} - hasBin: true - - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} - engines: {node: '>=12.20'} - - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - -snapshots: - - '@babel/code-frame@7.24.6': - dependencies: - '@babel/highlight': 7.24.6 - picocolors: 1.0.1 - - '@babel/helper-validator-identifier@7.24.6': {} - - '@babel/highlight@7.24.6': - dependencies: - '@babel/helper-validator-identifier': 7.24.6 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 - - '@babel/runtime@7.24.6': - dependencies: - regenerator-runtime: 0.14.1 - - '@changesets/apply-release-plan@7.0.1': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/config': 3.0.0 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.6.2 - - '@changesets/assemble-release-plan@6.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.6.2 - - '@changesets/changelog-git@0.2.0': - dependencies: - '@changesets/types': 6.0.0 - - '@changesets/cli@2.27.3': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/apply-release-plan': 7.0.1 - '@changesets/assemble-release-plan': 6.0.0 - '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.0 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/get-release-plan': 4.0.0 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 - '@changesets/types': 6.0.0 - '@changesets/write': 0.3.1 - '@manypkg/get-packages': 1.1.3 - '@types/semver': 7.5.8 - ansi-colors: 4.1.3 - chalk: 2.4.2 - ci-info: 3.9.0 - enquirer: 2.4.1 - external-editor: 3.1.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - meow: 6.1.1 - outdent: 0.5.0 - p-limit: 2.3.0 - preferred-pm: 3.1.3 - resolve-from: 5.0.0 - semver: 7.6.2 - spawndamnit: 2.0.0 - term-size: 2.2.1 - tty-table: 4.2.3 - - '@changesets/config@3.0.0': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/logger': 0.1.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.7 - - '@changesets/errors@0.2.0': - dependencies: - extendable-error: 0.1.7 - - '@changesets/get-dependents-graph@2.0.0': - dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - chalk: 2.4.2 - fs-extra: 7.0.1 - semver: 7.6.2 - - '@changesets/get-release-plan@4.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/assemble-release-plan': 6.0.0 - '@changesets/config': 3.0.0 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} - - '@changesets/git@3.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.7 - spawndamnit: 2.0.0 - - '@changesets/logger@0.1.0': - dependencies: - chalk: 2.4.2 - - '@changesets/parse@0.4.0': - dependencies: - '@changesets/types': 6.0.0 - js-yaml: 3.14.1 - - '@changesets/pre@2.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - - '@changesets/read@0.6.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 - '@changesets/parse': 0.4.0 - '@changesets/types': 6.0.0 - chalk: 2.4.2 - fs-extra: 7.0.1 - p-filter: 2.1.0 - - '@changesets/types@4.1.0': {} - - '@changesets/types@6.0.0': {} - - '@changesets/write@0.3.1': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - prettier: 2.8.8 - - '@colors/colors@1.5.0': - optional: true - - '@colors/colors@1.6.0': {} - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@dabh/diagnostics@2.0.3': - dependencies: - colorspace: 1.1.4 - enabled: 2.0.0 - kuler: 2.0.0 - - '@esbuild/aix-ppc64@0.20.2': - optional: true - - '@esbuild/android-arm64@0.20.2': - optional: true - - '@esbuild/android-arm@0.20.2': - optional: true - - '@esbuild/android-x64@0.20.2': - optional: true - - '@esbuild/darwin-arm64@0.20.2': - optional: true - - '@esbuild/darwin-x64@0.20.2': - optional: true - - '@esbuild/freebsd-arm64@0.20.2': - optional: true - - '@esbuild/freebsd-x64@0.20.2': - optional: true - - '@esbuild/linux-arm64@0.20.2': - optional: true - - '@esbuild/linux-arm@0.20.2': - optional: true - - '@esbuild/linux-ia32@0.20.2': - optional: true - - '@esbuild/linux-loong64@0.20.2': - optional: true - - '@esbuild/linux-mips64el@0.20.2': - optional: true - - '@esbuild/linux-ppc64@0.20.2': - optional: true - - '@esbuild/linux-riscv64@0.20.2': - optional: true - - '@esbuild/linux-s390x@0.20.2': - optional: true - - '@esbuild/linux-x64@0.20.2': - optional: true - - '@esbuild/netbsd-x64@0.20.2': - optional: true - - '@esbuild/openbsd-x64@0.20.2': - optional: true - - '@esbuild/sunos-x64@0.20.2': - optional: true - - '@esbuild/win32-arm64@0.20.2': - optional: true - - '@esbuild/win32-ia32@0.20.2': - optional: true - - '@esbuild/win32-x64@0.20.2': - optional: true - - '@fastify/ajv-compiler@3.5.0': - dependencies: - ajv: 8.14.0 - ajv-formats: 2.1.1(ajv@8.14.0) - fast-uri: 2.3.0 - - '@fastify/cors@8.4.1': - dependencies: - fastify-plugin: 4.5.1 - mnemonist: 0.39.5 - - '@fastify/error@3.4.1': {} - - '@fastify/fast-json-stringify-compiler@4.3.0': - dependencies: - fast-json-stringify: 5.16.0 - - '@fastify/merge-json-schemas@0.1.1': - dependencies: - fast-deep-equal: 3.1.3 - - '@gar/promisify@1.1.3': {} - - '@humanwhocodes/momoa@2.0.4': {} - - '@inquirer/figures@1.0.2': {} - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.4.15': {} - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - - '@js-sdsl/ordered-set@4.4.2': {} - - '@ljharb/through@2.3.13': - dependencies: - call-bind: 1.0.7 - - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.24.6 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - - '@mongodb-js/saslprep@1.1.7': - dependencies: - sparse-bitfield: 3.0.3 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - - '@npmcli/fs@2.1.2': - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.6.2 - - '@npmcli/fs@3.1.1': - dependencies: - semver: 7.6.2 - - '@npmcli/git@4.1.0': - dependencies: - '@npmcli/promise-spawn': 6.0.2 - lru-cache: 7.18.3 - npm-pick-manifest: 8.0.2 - proc-log: 3.0.0 - promise-inflight: 1.0.1 - promise-retry: 2.0.1 - semver: 7.6.2 - which: 3.0.1 - transitivePeerDependencies: - - bluebird - - '@npmcli/installed-package-contents@2.1.0': - dependencies: - npm-bundled: 3.0.1 - npm-normalize-package-bin: 3.0.1 - - '@npmcli/move-file@2.0.1': - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - - '@npmcli/node-gyp@3.0.0': {} - - '@npmcli/promise-spawn@6.0.2': - dependencies: - which: 3.0.1 - - '@npmcli/run-script@6.0.2': - dependencies: - '@npmcli/node-gyp': 3.0.0 - '@npmcli/promise-spawn': 6.0.2 - node-gyp: 9.4.1 - read-package-json-fast: 3.0.2 - which: 3.0.1 - transitivePeerDependencies: - - bluebird - - supports-color - - '@opentelemetry/api-logs@0.51.1': - dependencies: - '@opentelemetry/api': 1.8.0 - - '@opentelemetry/api-logs@0.52.0': - dependencies: - '@opentelemetry/api': 1.8.0 - - '@opentelemetry/api@1.6.0': {} - - '@opentelemetry/api@1.8.0': {} - - '@opentelemetry/api@1.9.0': {} - - '@opentelemetry/context-async-hooks@1.25.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/semantic-conventions': 1.17.0 - - '@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/semantic-conventions': 1.24.1 - - '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/semantic-conventions': 1.24.1 - - '@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.24.1 - - '@opentelemetry/core@1.25.0(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/semantic-conventions': 1.25.0 - - '@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.25.0 - - '@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) @@ -4547,201 +1191,327 @@ snapshots: '@opentelemetry/otlp-transformer': 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0)': + /@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/sdk-metrics': 1.17.0(@opentelemetry/api@1.6.0) + dev: false - '@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/instrumentation-connect@0.37.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@types/connect': 3.4.36 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-express@0.40.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-fastify@0.37.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-graphql@0.41.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-hapi@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-http@0.52.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 semver: 7.6.2 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-ioredis@0.41.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-koa@0.41.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@types/koa': 2.14.0 - '@types/koa__router': 12.0.3 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mongodb@0.45.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mongoose@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mysql2@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mysql@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@types/mysql': 2.15.22 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-nestjs-core@0.38.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-pg@0.42.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) '@types/pg': 8.6.1 '@types/pg-pool': 2.0.4 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-redis-4@0.40.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.8.0)': + /@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} + engines: {node: '>=14'} + requiresBuild: true + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: - '@opentelemetry/api': 1.8.0 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.4.2 + '@opentelemetry/api': 1.9.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.7.1 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color + dev: false optional: true - '@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 + require-in-the-middle: 7.3.0 + semver: 7.6.2 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/api-logs': 0.51.1 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.7.4 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.52.0 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.8.0 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 @@ -4750,632 +1520,944 @@ snapshots: '@opentelemetry/sdk-logs': 0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/redis-common@0.36.2': {} + /@opentelemetry/redis-common@0.36.2: + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0)': + /@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.17.0 + dev: false - '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0)': + /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false + + /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/resources@1.25.0(@opentelemetry/api@1.8.0)': + /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/resources@1.25.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.9.0' + '@opentelemetry/api-logs': '>=0.39.1' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0)': + /@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0)': + /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.9.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/api': 1.6.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/api': 1.8.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/semantic-conventions@1.17.0': {} + /@opentelemetry/semantic-conventions@1.17.0: + resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/semantic-conventions@1.24.1': {} + /@opentelemetry/semantic-conventions@1.24.1: + resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/semantic-conventions@1.25.0': {} + /@opentelemetry/semantic-conventions@1.25.1: + resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + dev: false - '@pkgjs/parseargs@0.11.0': + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true optional: true - '@pnpm/config.env-replace@1.1.0': {} + /@pnpm/config.env-replace@1.1.0: + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + dev: true - '@pnpm/network.ca-file@1.0.2': + /@pnpm/network.ca-file@1.0.2: + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} dependencies: graceful-fs: 4.2.10 + dev: true - '@pnpm/npm-conf@2.2.2': + /@pnpm/npm-conf@2.2.2: + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + dev: true - '@prisma/instrumentation@5.15.0': + /@prisma/instrumentation@5.16.1: + resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.8.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.8.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.8.0) transitivePeerDependencies: - supports-color + dev: false - '@rollup/rollup-android-arm-eabi@4.18.0': + /@rollup/rollup-android-arm-eabi@4.18.1: + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-android-arm64@4.18.0': + /@rollup/rollup-android-arm64@4.18.1: + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-darwin-arm64@4.18.0': + /@rollup/rollup-darwin-arm64@4.18.1: + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-darwin-x64@4.18.0': + /@rollup/rollup-darwin-x64@4.18.1: + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + /@rollup/rollup-linux-arm-gnueabihf@4.18.1: + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm-musleabihf@4.18.0': + /@rollup/rollup-linux-arm-musleabihf@4.18.1: + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm64-gnu@4.18.0': + /@rollup/rollup-linux-arm64-gnu@4.18.1: + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm64-musl@4.18.0': + /@rollup/rollup-linux-arm64-musl@4.18.1: + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-riscv64-gnu@4.18.0': + /@rollup/rollup-linux-riscv64-gnu@4.18.1: + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-s390x-gnu@4.18.0': + /@rollup/rollup-linux-s390x-gnu@4.18.1: + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-x64-gnu@4.18.0': + /@rollup/rollup-linux-x64-gnu@4.18.1: + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-x64-musl@4.18.0': + /@rollup/rollup-linux-x64-musl@4.18.1: + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-win32-arm64-msvc@4.18.0': + /@rollup/rollup-win32-arm64-msvc@4.18.1: + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-win32-ia32-msvc@4.18.0': + /@rollup/rollup-win32-ia32-msvc@4.18.1: + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-win32-x64-msvc@4.18.0': + /@rollup/rollup-win32-x64-msvc@4.18.1: + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true optional: true - '@sentry/core@8.9.2': + /@sentry/core@8.17.0: + resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} + engines: {node: '>=14.18'} dependencies: - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 + dev: false - '@sentry/node@8.9.2': + /@sentry/node@8.17.0: + resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} + engines: {node: '>=14.18'} dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.37.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.40.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fastify': 0.37.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.45.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-nestjs-core': 0.38.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.42.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis-4': 0.40.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@prisma/instrumentation': 5.15.0 - '@sentry/core': 8.9.2 - '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 + '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.39.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@prisma/instrumentation': 5.16.1 + '@sentry/core': 8.17.0 + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 optionalDependencies: - opentelemetry-instrumentation-fetch-node: 1.2.0 + opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color + dev: false - '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': + /@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1): + resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} + engines: {node: '>=14.18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.25.1 + '@opentelemetry/instrumentation': ^0.52.1 + '@opentelemetry/sdk-trace-base': ^1.25.1 + '@opentelemetry/semantic-conventions': ^1.25.1 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@sentry/core': 8.9.2 - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 - - '@sentry/types@8.9.2': {} + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@sentry/core': 8.17.0 + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 + dev: false + + /@sentry/types@8.17.0: + resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} + engines: {node: '>=14.18'} - '@sentry/utils@8.9.2': + /@sentry/utils@8.17.0: + resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} + engines: {node: '>=14.18'} dependencies: - '@sentry/types': 8.9.2 + '@sentry/types': 8.17.0 + dev: false - '@sigstore/bundle@1.1.0': + /@sigstore/bundle@1.1.0: + resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@sigstore/protobuf-specs': 0.2.1 + dev: true - '@sigstore/protobuf-specs@0.2.1': {} + /@sigstore/protobuf-specs@0.2.1: + resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - '@sigstore/sign@1.0.0': + /@sigstore/sign@1.0.0: + resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color + dev: true - '@sigstore/tuf@1.0.3': + /@sigstore/tuf@1.0.3: + resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@sigstore/protobuf-specs': 0.2.1 tuf-js: 1.1.7 transitivePeerDependencies: - supports-color + dev: true - '@sinclair/typebox@0.27.8': {} + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true - '@sindresorhus/is@5.6.0': {} + /@sindresorhus/is@5.6.0: + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + dev: true - '@syncpoint/wkx@0.5.2': + /@syncpoint/wkx@0.5.2: + resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} dependencies: '@types/node': 15.14.9 + dev: false - '@szmarczak/http-timer@5.0.1': + /@szmarczak/http-timer@5.0.1: + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} dependencies: defer-to-connect: 2.0.1 + dev: true - '@tootallnate/once@2.0.0': {} + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true - '@tsconfig/node10@1.0.11': {} + /@tsconfig/node10@1.0.11: + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + dev: true - '@tsconfig/node12@1.0.11': {} + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true - '@tsconfig/node14@1.0.3': {} + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true - '@tsconfig/node16@1.0.4': {} + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true - '@tufjs/canonical-json@1.0.0': {} + /@tufjs/canonical-json@1.0.0: + resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - '@tufjs/models@1.0.4': + /@tufjs/models@1.0.4: + resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@tufjs/canonical-json': 1.0.0 - minimatch: 9.0.4 - - '@types/accepts@1.3.7': - dependencies: - '@types/node': 18.11.11 - - '@types/async@3.2.24': {} + minimatch: 9.0.5 + dev: true - '@types/body-parser@1.19.5': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 18.11.11 + /@types/async@3.2.24: + resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} + dev: true - '@types/chai-subset@1.3.5': + /@types/chai-subset@1.3.5: + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} dependencies: '@types/chai': 4.3.16 + dev: true - '@types/chai@4.3.16': {} - - '@types/connect@3.4.36': - dependencies: - '@types/node': 18.11.11 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 18.11.11 - - '@types/content-disposition@0.5.8': {} - - '@types/cookies@0.9.0': - dependencies: - '@types/connect': 3.4.38 - '@types/express': 4.17.21 - '@types/keygrip': 1.0.6 - '@types/node': 18.11.11 - - '@types/estree@1.0.5': {} - - '@types/express-serve-static-core@4.19.1': - dependencies: - '@types/node': 18.11.11 - '@types/qs': 6.9.15 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - - '@types/express@4.17.21': - dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.1 - '@types/qs': 6.9.15 - '@types/serve-static': 1.15.7 - - '@types/http-assert@1.5.5': {} - - '@types/http-cache-semantics@4.0.4': {} - - '@types/http-errors@2.0.4': {} - - '@types/keygrip@1.0.6': {} - - '@types/koa-compose@3.2.8': - dependencies: - '@types/koa': 2.14.0 + /@types/chai@4.3.16: + resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + dev: true - '@types/koa@2.14.0': + /@types/connect@3.4.36: + resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} dependencies: - '@types/accepts': 1.3.7 - '@types/content-disposition': 0.5.8 - '@types/cookies': 0.9.0 - '@types/http-assert': 1.5.5 - '@types/http-errors': 2.0.4 - '@types/keygrip': 1.0.6 - '@types/koa-compose': 3.2.8 '@types/node': 18.11.11 + dev: false - '@types/koa__router@12.0.3': - dependencies: - '@types/koa': 2.14.0 - - '@types/lodash@4.17.5': {} + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true - '@types/mime@1.3.5': {} + /@types/http-cache-semantics@4.0.4: + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + dev: true - '@types/minimist@1.2.5': {} + /@types/lodash@4.17.6: + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + dev: true - '@types/mysql@2.15.22': + /@types/mysql@2.15.22: + resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} dependencies: '@types/node': 18.11.11 + dev: false - '@types/node@12.20.55': {} - - '@types/node@13.13.52': {} + /@types/node@12.20.55: + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + dev: true - '@types/node@15.14.9': {} + /@types/node@13.13.52: + resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} + dev: false - '@types/node@18.11.11': {} + /@types/node@15.14.9: + resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} + dev: false - '@types/normalize-package-data@2.4.4': {} + /@types/node@18.11.11: + resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} - '@types/pg-pool@2.0.4': + /@types/pg-pool@2.0.4: + resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} dependencies: '@types/pg': 8.6.1 + dev: false - '@types/pg@8.6.1': + /@types/pg@8.6.1: + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} dependencies: '@types/node': 18.11.11 pg-protocol: 1.6.1 pg-types: 2.2.0 + dev: false - '@types/qs@6.9.15': {} - - '@types/range-parser@1.2.7': {} - - '@types/semver-utils@1.1.3': {} - - '@types/semver@7.5.8': {} - - '@types/send@0.17.4': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 18.11.11 + /@types/semver-utils@1.1.3: + resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} + dev: true - '@types/serve-static@1.15.7': - dependencies: - '@types/http-errors': 2.0.4 - '@types/node': 18.11.11 - '@types/send': 0.17.4 + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + dev: true - '@types/shimmer@1.0.5': {} + /@types/shimmer@1.2.0: + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + dev: false - '@types/strip-bom@3.0.0': {} + /@types/strip-bom@3.0.0: + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + dev: true - '@types/strip-json-comments@0.0.30': {} + /@types/strip-json-comments@0.0.30: + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + dev: true - '@types/triple-beam@1.3.5': {} + /@types/triple-beam@1.3.5: + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + dev: false - '@types/uuid@9.0.8': {} + /@types/uuid@9.0.8: + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + dev: true - '@types/webidl-conversions@7.0.3': {} + /@types/webidl-conversions@7.0.3: + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + dev: false - '@types/whatwg-url@11.0.5': + /@types/whatwg-url@11.0.5: + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} dependencies: '@types/webidl-conversions': 7.0.3 + dev: false - '@types/ws@8.2.3': + /@types/ws@8.2.3: + resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} dependencies: '@types/node': 18.11.11 + dev: true - '@vitest/expect@0.34.6': + /@vitest/expect@0.34.6: + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} dependencies: '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 chai: 4.4.1 + dev: true - '@vitest/runner@0.34.6': + /@vitest/runner@0.34.6: + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.2 + dev: true - '@vitest/snapshot@0.34.6': + /@vitest/snapshot@0.34.6: + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 + dev: true - '@vitest/spy@0.34.6': + /@vitest/spy@0.34.6: + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} dependencies: tinyspy: 2.2.1 + dev: true - '@vitest/utils@0.34.6': + /@vitest/utils@0.34.6: + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} dependencies: diff-sequences: 29.6.3 loupe: 2.3.7 pretty-format: 29.7.0 + dev: true - abbrev@1.1.1: {} + /abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: true - abort-controller@3.0.0: + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} dependencies: event-target-shim: 5.0.1 - abstract-logging@2.0.1: {} + /abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - acorn-import-assertions@1.9.0(acorn@8.11.3): + /acorn-import-assertions@1.9.0(acorn@8.12.1): + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + requiresBuild: true + peerDependencies: + acorn: ^8 dependencies: - acorn: 8.11.3 + acorn: 8.12.1 + dev: false optional: true - acorn-import-attributes@1.9.5(acorn@8.11.3): + /acorn-import-attributes@1.9.5(acorn@8.12.1): + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 dependencies: - acorn: 8.11.3 + acorn: 8.12.1 + dev: false - acorn-walk@8.3.2: {} + /acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + dependencies: + acorn: 8.12.1 + dev: true - acorn@8.11.3: {} + /acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true - agent-base@6.0.2: + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color + dev: true - agentkeepalive@4.5.0: + /agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} dependencies: humanize-ms: 1.2.1 + dev: true - aggregate-error@3.1.0: + /aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 + dev: true - ajv-formats@2.1.1(ajv@8.14.0): - optionalDependencies: - ajv: 8.14.0 + /ajv-formats@2.1.1(ajv@8.16.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.16.0 - ajv-formats@3.0.1(ajv@8.14.0): - optionalDependencies: - ajv: 8.14.0 + /ajv-formats@3.0.1(ajv@8.16.0): + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.16.0 - ajv@8.14.0: + /ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - ansi-align@3.0.1: + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} dependencies: string-width: 4.2.3 + dev: true - ansi-colors@4.1.3: {} + /ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + dev: true - ansi-escapes@4.3.2: + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} dependencies: type-fest: 0.21.3 + dev: true - ansi-regex@5.0.1: {} + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true - ansi-regex@6.0.1: {} + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true - ansi-styles@3.2.1: + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - ansi-styles@4.3.0: + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true - ansi-styles@6.2.1: {} + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true - anymatch@3.1.3: + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + dev: true - aproba@2.0.0: {} + /aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + dev: true - are-we-there-yet@3.0.1: + /are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. dependencies: delegates: 1.0.0 readable-stream: 3.6.2 + dev: true - arg@4.1.3: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - - array-union@2.1.0: {} + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true - array.prototype.flat@1.3.2: + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 + sprintf-js: 1.0.3 + dev: true - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true - arrify@1.0.1: {} + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true - assertion-error@1.1.0: {} + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true - async-mutex@0.5.0: + /async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} dependencies: - tslib: 2.6.2 - - async@3.2.5: {} + tslib: 2.6.3 + dev: false - atomic-sleep@1.0.0: {} + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 + /atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} - avvio@8.3.2: + /avvio@8.3.2: + resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} dependencies: '@fastify/error': 3.4.1 fastq: 1.17.1 - balanced-match@1.0.2: {} + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true - base64-js@1.5.1: {} + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - better-ajv-errors@1.2.0(ajv@8.14.0): + /better-ajv-errors@1.2.0(ajv@8.16.0): + resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + ajv: 4.11.8 - 8 dependencies: - '@babel/code-frame': 7.24.6 + '@babel/code-frame': 7.24.7 '@humanwhocodes/momoa': 2.0.4 - ajv: 8.14.0 + ajv: 8.16.0 chalk: 4.1.2 jsonpointer: 5.0.1 leven: 3.1.0 + dev: false - better-path-resolve@1.0.0: + /better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} dependencies: is-windows: 1.0.2 + dev: true - binary-extensions@2.3.0: {} + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + dev: true - bl@4.1.0: + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + dev: true - boxen@7.1.1: + /boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 @@ -5385,41 +2467,57 @@ snapshots: type-fest: 2.19.0 widest-line: 4.0.1 wrap-ansi: 8.1.0 + dev: true - brace-expansion@1.1.11: + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 + dev: true - brace-expansion@2.0.1: + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 + dev: true - braces@3.0.3: + /braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} dependencies: fill-range: 7.1.1 + dev: true - breakword@1.0.6: - dependencies: - wcwidth: 1.0.1 - - bson@6.7.0: {} + /bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} - buffer-from@1.1.2: {} + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true - buffer@5.7.1: + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + dev: true - buffer@6.0.3: + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - cac@6.7.14: {} + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true - cacache@16.1.3: + /cacache@16.1.3: + resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: '@npmcli/fs': 2.1.2 '@npmcli/move-file': 2.0.1 @@ -5441,12 +2539,15 @@ snapshots: unique-filename: 2.0.1 transitivePeerDependencies: - bluebird + dev: true - cacache@17.1.4: + /cacache@17.1.4: + resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@npmcli/fs': 3.1.1 fs-minipass: 3.0.3 - glob: 10.4.1 + glob: 10.4.5 lru-cache: 7.18.3 minipass: 7.1.2 minipass-collect: 1.0.2 @@ -5456,10 +2557,16 @@ snapshots: ssri: 10.0.6 tar: 6.2.1 unique-filename: 3.0.0 + dev: true - cacheable-lookup@7.0.0: {} + /cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + dev: true - cacheable-request@10.2.14: + /cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} dependencies: '@types/http-cache-semantics': 4.0.4 get-stream: 6.0.1 @@ -5468,55 +2575,59 @@ snapshots: mimic-response: 4.0.0 normalize-url: 8.0.1 responselike: 3.0.0 + dev: true - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - - camelcase-keys@6.2.2: - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - - camelcase@5.3.1: {} - - camelcase@7.0.1: {} + /camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + dev: true - chai@4.4.1: + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} dependencies: assertion-error: 1.1.0 check-error: 1.0.3 - deep-eql: 4.1.3 + deep-eql: 4.1.4 get-func-name: 2.0.2 loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 + dev: true - chalk@2.4.2: + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - chalk@4.1.2: + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.3.0: {} + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true - chardet@0.7.0: {} + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true - check-error@1.0.3: + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} dependencies: get-func-name: 2.0.2 + dev: true - chokidar@3.6.0: + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -5527,89 +2638,145 @@ snapshots: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 + dev: true - chownr@2.0.0: {} + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: true - ci-info@3.9.0: {} + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true - cjs-module-lexer@1.3.1: {} + /cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + dev: false - clean-stack@2.2.0: {} + /clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true - cli-boxes@3.0.0: {} + /cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + dev: true - cli-cursor@3.1.0: + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 + dev: true - cli-spinners@2.9.2: {} + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: true - cli-table3@0.6.5: + /cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} dependencies: string-width: 4.2.3 optionalDependencies: '@colors/colors': 1.5.0 + dev: true - cli-width@4.1.0: {} - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 + /cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + dev: true - cliui@7.0.4: + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + dev: true - cliui@8.0.1: + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + dev: true - clone@1.0.4: {} + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true - color-convert@1.9.3: + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - color-convert@2.0.1: + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - color-name@1.1.3: {} + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: {} + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 + dev: false - color-support@1.1.3: {} + /color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: true - color@3.2.1: + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} dependencies: color-convert: 1.9.3 color-string: 1.9.1 + dev: false - colorspace@1.1.4: + /colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} dependencies: color: 3.2.1 text-hex: 1.0.0 + dev: false - commander@10.0.1: {} + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true - commander@12.1.0: {} + /commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + dev: false - commander@2.20.3: {} + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: false - concat-map@0.0.1: {} + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true - concurrently@8.2.2: + /concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true dependencies: chalk: 4.1.2 date-fns: 2.30.0 @@ -5620,27 +2787,41 @@ snapshots: supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 + dev: true - confbox@0.1.7: {} + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true - config-chain@1.1.13: + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} dependencies: ini: 1.3.8 proto-list: 1.2.4 + dev: true - configstore@6.0.0: + /configstore@6.0.0: + resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} + engines: {node: '>=12'} dependencies: dot-prop: 6.0.1 graceful-fs: 4.2.11 unique-string: 3.0.0 write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 + dev: true - console-control-strings@1.1.0: {} + /console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + dev: true - cookie@0.6.0: {} + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} - copyfiles@2.4.1: + /copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true dependencies: glob: 7.2.3 minimatch: 3.1.2 @@ -5649,274 +2830,252 @@ snapshots: through2: 2.0.5 untildify: 4.0.0 yargs: 16.2.0 + dev: true - core-util-is@1.0.3: {} + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true - cors@2.8.5: + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} dependencies: object-assign: 4.1.1 vary: 1.1.2 + dev: false - create-require@1.1.1: {} + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true - cross-spawn@5.1.0: + /cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 + dev: true - cross-spawn@7.0.3: + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + dev: true - crypto-random-string@4.0.0: + /crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} dependencies: type-fest: 1.4.0 + dev: true - csv-generate@3.4.3: {} - - csv-parse@4.16.3: {} - - csv-stringify@5.6.5: {} - - csv@5.5.3: - dependencies: - csv-generate: 3.4.3 - csv-parse: 4.16.3 - csv-stringify: 5.6.5 - stream-transform: 2.1.3 - - data-uri-to-buffer@4.0.1: {} - - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 + /data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + dev: false - date-fns@2.30.0: + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} dependencies: - '@babel/runtime': 7.24.6 + '@babel/runtime': 7.24.8 + dev: true - date-fns@3.6.0: {} + /date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dev: false - debug@4.3.4(supports-color@5.5.0): + /debug@4.3.5(supports-color@5.5.0): + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true dependencies: ms: 2.1.2 - optionalDependencies: supports-color: 5.5.0 - decamelize-keys@1.1.1: - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - - decamelize@1.2.0: {} - - decompress-response@6.0.0: + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 + dev: true - deep-eql@4.1.3: + /deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} dependencies: type-detect: 4.0.8 + dev: true - deep-extend@0.6.0: {} + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: true - defaults@1.0.4: + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 + dev: true - defer-to-connect@2.0.1: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true - delegates@1.0.0: {} + /delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: true - detect-indent@6.1.0: {} + /detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true - diff-sequences@29.6.3: {} + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true - diff@4.0.2: {} + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true - dir-glob@3.0.1: + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} dependencies: path-type: 4.0.0 + dev: true - discontinuous-range@1.0.0: {} + /discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + dev: false - dot-prop@6.0.1: + /dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} dependencies: is-obj: 2.0.0 + dev: true - dotenv@16.4.5: {} + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + dev: false - duplexer@0.1.2: {} + /duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + dev: true - dynamic-dedupe@0.3.0: + /dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} dependencies: xtend: 4.0.2 + dev: true - eastasianwidth@0.2.0: {} + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true - emoji-regex@8.0.0: {} + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true - emoji-regex@9.2.2: {} + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true - enabled@2.0.0: {} + /enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: false - encoding@0.1.13: + /encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + requiresBuild: true dependencies: iconv-lite: 0.6.3 + dev: true optional: true - enquirer@2.4.1: + /enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + dev: true - env-paths@2.2.1: {} - - err-code@2.0.3: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - es-abstract@1.23.3: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-errors@1.3.0: {} - - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 + /env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: true - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 + /err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + dev: true - esbuild@0.20.2: + /esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 - - escalade@3.1.2: {} - - escape-goat@4.0.0: {} - - escape-string-regexp@1.0.5: {} - - esprima@4.0.1: {} - - event-stream@3.3.4: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + dev: true + + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + dev: true + + /escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} dependencies: duplexer: 0.1.2 from: 0.1.7 @@ -5925,172 +3084,246 @@ snapshots: split: 0.3.3 stream-combiner: 0.0.4 through: 2.3.8 + dev: true - event-target-shim@5.0.1: {} + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} - events@3.3.0: {} + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} - exponential-backoff@3.1.1: {} + /exponential-backoff@3.1.1: + resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + dev: true - extendable-error@0.1.7: {} + /extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + dev: true - external-editor@3.1.0: + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 + dev: true - fast-content-type-parse@1.1.0: {} + /fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} - fast-decode-uri-component@1.0.1: {} + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} - fast-deep-equal@3.1.3: {} + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.2: + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.7 + dev: true - fast-json-stringify@5.16.0: + /fast-json-stringify@5.16.1: + resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} dependencies: '@fastify/merge-json-schemas': 0.1.1 - ajv: 8.14.0 - ajv-formats: 3.0.1(ajv@8.14.0) + ajv: 8.16.0 + ajv-formats: 3.0.1(ajv@8.16.0) fast-deep-equal: 3.1.3 - fast-uri: 2.3.0 + fast-uri: 2.4.0 json-schema-ref-resolver: 1.0.1 - rfdc: 1.3.1 + rfdc: 1.4.1 - fast-memoize@2.5.2: {} + /fast-memoize@2.5.2: + resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + dev: true - fast-querystring@1.1.2: + /fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} dependencies: fast-decode-uri-component: 1.0.1 - fast-redact@3.5.0: {} + /fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} - fast-uri@2.3.0: {} + /fast-uri@2.4.0: + resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} - fastify-plugin@4.5.1: {} + /fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} - fastify@4.23.2: + /fastify@4.23.2: + resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} dependencies: - '@fastify/ajv-compiler': 3.5.0 + '@fastify/ajv-compiler': 3.6.0 '@fastify/error': 3.4.1 '@fastify/fast-json-stringify-compiler': 4.3.0 abstract-logging: 2.0.1 avvio: 8.3.2 fast-content-type-parse: 1.1.0 - fast-json-stringify: 5.16.0 + fast-json-stringify: 5.16.1 find-my-way: 7.7.0 light-my-request: 5.13.0 pino: 8.21.0 process-warning: 2.3.2 proxy-addr: 2.0.7 - rfdc: 1.3.1 + rfdc: 1.4.1 secure-json-parse: 2.7.0 semver: 7.6.2 toad-cache: 3.7.0 - fastq@1.17.1: + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 - fecha@4.2.3: {} + /fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + dev: false - fetch-blob@3.2.0: + /fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + dev: false - fill-range@7.1.1: + /fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 + dev: true - find-my-way@7.7.0: + /find-my-way@7.7.0: + resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} + engines: {node: '>=14'} dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 safe-regex2: 2.0.0 - find-up@4.1.0: + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} dependencies: locate-path: 5.0.0 path-exists: 4.0.0 + dev: true - find-up@5.0.0: + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} dependencies: locate-path: 6.0.0 path-exists: 4.0.0 + dev: true - find-yarn-workspace-root2@1.2.16: + /find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} dependencies: micromatch: 4.0.7 pkg-dir: 4.2.0 + dev: true - fn.name@1.1.0: {} - - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 + /fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: false - foreground-child@3.1.1: + /foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 + dev: true - form-data-encoder@2.1.4: {} + /form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + dev: true - formdata-polyfill@4.0.10: + /formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} dependencies: fetch-blob: 3.2.0 + dev: false - forwarded@0.2.0: {} + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} - fp-and-or@0.1.4: {} + /fp-and-or@0.1.4: + resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} + engines: {node: '>=10'} + dev: true - from@0.1.7: {} + /from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + dev: true - fs-extra@7.0.1: + /fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 + dev: true - fs-extra@8.1.0: + /fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 + dev: true - fs-minipass@2.1.0: + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 + dev: true - fs-minipass@3.0.3: + /fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: minipass: 7.1.2 + dev: true - fs.realpath@1.0.0: {} + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true - fsevents@2.3.3: + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true optional: true - function-bind@1.1.2: {} - - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - functions-have-names: 1.2.3 - - functions-have-names@1.2.3: {} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gauge@4.0.4: + /gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. dependencies: aproba: 2.0.0 color-support: 1.1.3 @@ -6100,42 +3333,49 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wide-align: 1.1.5 + dev: true - get-caller-file@2.0.5: {} - - get-func-name@2.0.2: {} - - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true - get-stdin@8.0.0: {} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true - get-stream@6.0.1: {} + /get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + dev: true - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true - glob-parent@5.1.2: + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 + dev: true - glob@10.4.1: + /glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true dependencies: - foreground-child: 3.1.1 - jackspeak: 3.1.2 - minimatch: 9.0.4 + foreground-child: 3.2.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 minipass: 7.1.2 + package-json-from-dist: 1.0.0 path-scurry: 1.11.1 + dev: true - glob@7.2.3: + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -6143,25 +3383,30 @@ snapshots: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 + dev: true - glob@8.1.0: + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 minimatch: 5.1.6 once: 1.4.0 + dev: true - global-dirs@3.0.1: + /global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} dependencies: ini: 2.0.0 + dev: true - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.0.1 - - globby@11.1.0: + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -6169,14 +3414,15 @@ snapshots: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 + dev: true - globrex@0.1.2: {} - - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 + /globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true - got@12.6.1: + /got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} dependencies: '@sindresorhus/is': 5.6.0 '@szmarczak/http-timer': 5.0.1 @@ -6189,151 +3435,203 @@ snapshots: lowercase-keys: 3.0.0 p-cancelable: 3.0.0 responselike: 3.0.0 + dev: true - graceful-fs@4.2.10: {} - - graceful-fs@4.2.11: {} - - grapheme-splitter@1.0.4: {} - - hard-rejection@2.1.0: {} - - has-bigints@1.0.2: {} - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 + /graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true - has-proto@1.0.3: {} + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true - has-symbols@1.0.3: {} + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} - has-unicode@2.0.1: {} + /has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + dev: true - has-yarn@3.0.0: {} + /has-yarn@3.0.0: + resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - hasown@2.0.2: + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 - hosted-git-info@2.8.9: {} - - hosted-git-info@5.2.1: + /hosted-git-info@5.2.1: + resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: lru-cache: 7.18.3 + dev: true - hosted-git-info@6.1.1: + /hosted-git-info@6.1.1: + resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: lru-cache: 7.18.3 + dev: true - http-cache-semantics@4.1.1: {} + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true - http-proxy-agent@5.0.0: + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color + dev: true - http2-wrapper@2.2.1: + /http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + dev: true - https-proxy-agent@5.0.1: + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color + dev: true - human-id@1.0.2: {} + /human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + dev: true - humanize-ms@1.2.1: + /humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} dependencies: ms: 2.1.3 + dev: true - iconv-lite@0.4.24: + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 + dev: true - iconv-lite@0.6.3: + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: safer-buffer: 2.1.2 + dev: true optional: true - ieee754@1.2.1: {} + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore-by-default@1.0.1: {} + /ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + dev: true - ignore-walk@6.0.5: + /ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - minimatch: 9.0.4 + minimatch: 9.0.5 + dev: true - ignore@5.3.1: {} + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true - import-in-the-middle@1.4.2: + /import-in-the-middle@1.7.1: + resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} + requiresBuild: true dependencies: - acorn: 8.11.3 - acorn-import-assertions: 1.9.0(acorn@8.11.3) + acorn: 8.12.1 + acorn-import-assertions: 1.9.0(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 + dev: false optional: true - import-in-the-middle@1.7.4: - dependencies: - acorn: 8.11.3 - acorn-import-attributes: 1.9.5(acorn@8.11.3) - cjs-module-lexer: 1.3.1 - module-details-from-path: 1.0.3 - - import-in-the-middle@1.8.0: + /import-in-the-middle@1.9.0: + resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} dependencies: - acorn: 8.11.3 - acorn-import-attributes: 1.9.5(acorn@8.11.3) + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 + dev: false - import-lazy@4.0.0: {} + /import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + dev: true - imurmurhash@0.1.4: {} + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true - indent-string@4.0.0: {} + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true - infer-owner@1.0.4: {} + /infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + dev: true - inflight@1.0.6: + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. dependencies: once: 1.4.0 wrappy: 1.0.2 + dev: true - inherits@2.0.4: {} + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ini@1.3.8: {} + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true - ini@2.0.0: {} + /ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + dev: true - ini@4.1.3: {} + /ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - inquirer@9.2.22: + /inquirer@9.3.5: + resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} + engines: {node: '>=18'} dependencies: - '@inquirer/figures': 1.0.2 - '@ljharb/through': 2.3.13 + '@inquirer/figures': 1.0.3 ansi-escapes: 4.3.2 - chalk: 5.3.0 - cli-cursor: 3.1.0 cli-width: 4.1.0 external-editor: 3.1.0 - lodash: 4.17.21 mute-stream: 1.0.0 ora: 5.4.1 run-async: 3.0.0 @@ -6341,251 +3639,327 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + dev: true - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.0.6 - - ip-address@9.0.5: + /ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 + dev: true - ipaddr.js@1.9.1: {} - - ipaddr.js@2.2.0: {} - - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-arrayish@0.2.1: {} + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} - is-arrayish@0.3.2: {} + /ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + dev: false - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false - is-binary-path@2.1.0: + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} dependencies: binary-extensions: 2.3.0 + dev: true - is-boolean-object@1.1.2: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-ci@3.0.1: + /is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true dependencies: ci-info: 3.9.0 + dev: true - is-core-module@2.13.1: + /is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} dependencies: hasown: 2.0.2 - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.2 - - is-extglob@2.1.1: {} + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true - is-fullwidth-code-point@3.0.0: {} + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true - is-glob@4.0.3: + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 + dev: true - is-installed-globally@0.4.0: + /is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} dependencies: global-dirs: 3.0.1 is-path-inside: 3.0.3 + dev: true - is-interactive@1.0.0: {} - - is-lambda@1.0.1: {} - - is-negative-zero@2.0.3: {} - - is-npm@6.0.0: {} - - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - - is-obj@2.0.0: {} + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true - is-path-inside@3.0.3: {} + /is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + dev: true - is-plain-obj@1.1.0: {} + /is-npm@6.0.0: + resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - is-regex@1.1.4: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 + /is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: true - is-stream@2.0.1: {} + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.2 + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: false - is-subdir@1.2.0: + /is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} dependencies: better-path-resolve: 1.0.0 + dev: true - is-symbol@1.0.4: - dependencies: - has-symbols: 1.0.3 - - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - - is-typedarray@1.0.0: {} - - is-unicode-supported@0.1.0: {} - - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.7 + /is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + dev: true - is-windows@1.0.2: {} + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true - is-yarn-global@0.4.1: {} + /is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: true - isarray@0.0.1: {} + /is-yarn-global@0.4.1: + resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} + engines: {node: '>=12'} + dev: true - isarray@1.0.0: {} + /isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + dev: true - isarray@2.0.5: {} + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true - isexe@2.0.0: {} + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true - ix@5.0.0: + /ix@5.0.0: + resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} dependencies: '@types/node': 13.13.52 - tslib: 2.6.2 + tslib: 2.6.3 + dev: false - jackspeak@3.1.2: + /jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 + dev: true - jju@1.4.0: {} + /jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + dev: true - jose@4.15.5: {} + /jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + dev: false - js-tokens@4.0.0: {} + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false - js-yaml@3.14.1: + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true dependencies: argparse: 1.0.10 esprima: 4.0.1 + dev: true - js-yaml@4.1.0: + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true dependencies: argparse: 2.0.1 + dev: true - jsbn@1.1.0: {} - - json-buffer@3.0.1: {} + /jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + dev: true - json-parse-even-better-errors@2.3.1: {} + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true - json-parse-even-better-errors@3.0.2: {} + /json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - json-parse-helpfulerror@1.0.3: + /json-parse-helpfulerror@1.0.3: + resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} dependencies: jju: 1.4.0 + dev: true - json-schema-ref-resolver@1.0.1: + /json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} dependencies: fast-deep-equal: 3.1.3 - json-schema-traverse@1.0.0: {} + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json5@2.2.3: {} + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true - jsonfile@4.0.0: + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.11 + dev: true - jsonlines@0.1.1: {} + /jsonlines@0.1.1: + resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} + dev: true - jsonparse@1.3.1: {} + /jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + dev: true - jsonpointer@5.0.1: {} + /jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + dev: false - keyv@4.5.4: + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: json-buffer: 3.0.1 + dev: true - kind-of@6.0.3: {} - - kleur@4.1.5: {} + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true - kuler@2.0.0: {} + /kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: false - latest-version@7.0.0: + /latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} dependencies: package-json: 8.1.1 + dev: true - leven@3.1.0: {} + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: false - light-my-request@5.13.0: + /light-my-request@5.13.0: + resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} dependencies: cookie: 0.6.0 process-warning: 3.0.0 set-cookie-parser: 2.6.0 - lines-and-columns@1.2.4: {} - - load-yaml-file@0.2.0: + /load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 + dev: true - local-pkg@0.4.3: {} + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true - locate-path@5.0.0: + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} dependencies: p-locate: 4.1.0 + dev: true - locate-path@6.0.0: + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} dependencies: p-locate: 5.0.0 + dev: true - lodash.merge@4.6.2: {} + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: false - lodash.startcase@4.4.0: {} + /lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + dev: true - lodash@4.17.21: {} + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - log-symbols@4.1.0: + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 + dev: true - logform@2.6.0: + /logform@2.6.1: + resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} + engines: {node: '>= 12.0.0'} dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -6593,31 +3967,51 @@ snapshots: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 + dev: false - lossless-json@2.0.11: {} + /lossless-json@2.0.11: + resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} + dev: false - loupe@2.3.7: + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: get-func-name: 2.0.2 + dev: true - lowercase-keys@3.0.0: {} + /lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - lru-cache@10.2.2: {} + /lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@4.1.5: + /lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: pseudomap: 1.0.2 yallist: 2.1.2 + dev: true - lru-cache@7.18.3: {} + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: true - magic-string@0.30.10: + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true - make-error@1.3.6: {} + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true - make-fetch-happen@10.2.1: + /make-fetch-happen@10.2.1: + resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: agentkeepalive: 4.5.0 cacache: 16.1.3 @@ -6638,8 +4032,11 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - make-fetch-happen@11.1.1: + /make-fetch-happen@11.1.1: + resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: agentkeepalive: 4.5.0 cacache: 17.1.4 @@ -6658,174 +4055,277 @@ snapshots: ssri: 10.0.6 transitivePeerDependencies: - supports-color + dev: true - map-obj@1.0.1: {} - - map-obj@4.3.0: {} - - map-stream@0.1.0: {} - - memory-pager@1.5.0: {} + /map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + dev: true - meow@6.1.1: - dependencies: - '@types/minimist': 1.2.5 - camelcase-keys: 6.2.2 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 2.5.0 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.13.1 - yargs-parser: 18.1.3 + /memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + dev: false - merge2@1.4.1: {} + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true - micromatch@4.0.7: + /micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} dependencies: braces: 3.0.3 picomatch: 2.3.1 + dev: true - mimic-fn@2.1.0: {} - - mimic-response@3.1.0: {} + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true - mimic-response@4.0.0: {} + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: true - min-indent@1.0.1: {} + /mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - minimatch@3.1.2: + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 + dev: true - minimatch@5.1.6: + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 + dev: true - minimatch@9.0.4: + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 + dev: true - minimist-options@4.1.0: - dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 - - minimist@1.2.8: {} + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true - minipass-collect@1.0.2: + /minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 + dev: true - minipass-fetch@2.1.2: + /minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: minipass: 3.3.6 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 + dev: true - minipass-fetch@3.0.5: + /minipass-fetch@3.0.5: + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 + dev: true - minipass-flush@1.0.5: + /minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 + dev: true - minipass-json-stream@1.0.1: + /minipass-json-stream@1.0.1: + resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} dependencies: jsonparse: 1.3.1 minipass: 3.3.6 + dev: true - minipass-pipeline@1.2.4: + /minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} dependencies: minipass: 3.3.6 + dev: true - minipass-sized@1.0.3: + /minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} dependencies: minipass: 3.3.6 + dev: true - minipass@3.3.6: + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} dependencies: yallist: 4.0.0 + dev: true - minipass@5.0.0: {} + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true - minipass@7.1.2: {} + /minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true - minizlib@2.1.2: + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 yallist: 4.0.0 + dev: true - mixme@0.5.10: {} - - mkdirp@1.0.4: {} + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true - mlly@1.7.0: + /mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} dependencies: - acorn: 8.11.3 + acorn: 8.12.1 pathe: 1.1.2 - pkg-types: 1.1.1 + pkg-types: 1.1.3 ufo: 1.5.3 + dev: true - mnemonist@0.39.5: + /mnemonist@0.39.5: + resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} dependencies: obliterator: 2.0.4 + dev: false - module-details-from-path@1.0.3: {} + /module-details-from-path@1.0.3: + resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + dev: false - mongodb-connection-string-url@3.0.1: + /mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 + dev: false - mongodb@6.7.0(socks@2.8.3): + /mongodb@6.8.0: + resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true dependencies: '@mongodb-js/saslprep': 1.1.7 - bson: 6.7.0 + bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - optionalDependencies: - socks: 2.8.3 + dev: false + + /moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + dev: false - moo@0.5.2: {} + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true - ms@2.1.2: {} + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: {} + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@1.0.0: {} + /mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - nanoid@3.3.7: {} + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true - nearley@2.20.1: + /nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true dependencies: commander: 2.20.3 moo: 0.5.2 railroad-diagrams: 1.0.0 randexp: 0.4.6 + dev: false - negotiator@0.6.3: {} + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: true - node-cleanup@2.1.2: {} + /node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + dev: true - node-domexception@1.0.0: {} + /node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + dev: false - node-fetch@3.3.2: + /node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + dev: false - node-gyp@9.4.1: + /node-gyp@9.4.1: + resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} + engines: {node: ^12.13 || ^14.13 || >=16} + hasBin: true dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.1 @@ -6841,11 +4341,15 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - nodemon@3.1.1: + /nodemon@3.1.4: + resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} + engines: {node: '>=10'} + hasBin: true dependencies: chokidar: 3.6.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 pstree.remy: 1.1.8 @@ -6854,39 +4358,54 @@ snapshots: supports-color: 5.5.0 touch: 3.1.1 undefsafe: 2.0.5 + dev: true - noms@0.0.0: + /noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} dependencies: inherits: 2.0.4 readable-stream: 1.0.34 + dev: true - nopt@6.0.0: + /nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true dependencies: abbrev: 1.1.1 + dev: true - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.8 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 - - normalize-package-data@5.0.0: + /normalize-package-data@5.0.0: + resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: hosted-git-info: 6.1.1 - is-core-module: 2.13.1 + is-core-module: 2.14.0 semver: 7.6.2 validate-npm-package-license: 3.0.4 + dev: true - normalize-path@3.0.0: {} + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true - normalize-url@8.0.1: {} + /normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + dev: true - npm-bundled@3.0.1: + /npm-bundled@3.0.1: + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: npm-normalize-package-bin: 3.0.1 + dev: true - npm-check-updates@16.14.20: + /npm-check-updates@16.14.20: + resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} + engines: {node: '>=14.14'} + hasBin: true dependencies: '@types/semver-utils': 1.1.3 chalk: 5.3.0 @@ -6904,15 +4423,15 @@ snapshots: jsonlines: 0.1.1 lodash: 4.17.21 make-fetch-happen: 11.1.1 - minimatch: 9.0.4 + minimatch: 9.0.5 p-map: 4.0.0 pacote: 15.2.0 - parse-github-url: 1.0.2 + parse-github-url: 1.0.3 progress: 2.0.3 prompts-ncu: 3.0.0 rc-config-loader: 4.1.3 remote-git-tags: 3.0.0 - rimraf: 5.0.7 + rimraf: 5.0.9 semver: 7.6.2 semver-utils: 1.1.4 source-map-support: 0.5.21 @@ -6924,32 +4443,50 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - npm-install-checks@6.3.0: + /npm-install-checks@6.3.0: + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: semver: 7.6.2 + dev: true - npm-normalize-package-bin@3.0.1: {} + /npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - npm-package-arg@10.1.0: + /npm-package-arg@10.1.0: + resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: hosted-git-info: 6.1.1 proc-log: 3.0.0 semver: 7.6.2 validate-npm-package-name: 5.0.1 + dev: true - npm-packlist@7.0.4: + /npm-packlist@7.0.4: + resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: ignore-walk: 6.0.5 + dev: true - npm-pick-manifest@8.0.2: + /npm-pick-manifest@8.0.2: + resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 10.1.0 semver: 7.6.2 + dev: true - npm-registry-fetch@14.0.5: + /npm-registry-fetch@14.0.5: + resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: make-fetch-happen: 11.1.1 minipass: 5.0.0 @@ -6960,53 +4497,69 @@ snapshots: proc-log: 3.0.0 transitivePeerDependencies: - supports-color + dev: true - npmlog@6.0.2: + /npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. dependencies: are-we-there-yet: 3.0.1 console-control-strings: 1.1.0 gauge: 4.0.4 set-blocking: 2.0.0 + dev: true - object-assign@4.1.1: {} - - object-inspect@1.13.1: {} - - object-keys@1.1.1: {} - - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false - obliterator@2.0.4: {} + /obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + dev: false - on-exit-leak-free@2.1.2: {} + /on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} - once@1.4.0: + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 + dev: true - one-time@1.0.0: + /one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} dependencies: fn.name: 1.1.0 + dev: false - onetime@5.1.2: + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 + dev: true - opentelemetry-instrumentation-fetch-node@1.2.0: + /opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} + engines: {node: '>18.0.0'} + requiresBuild: true + peerDependencies: + '@opentelemetry/api': ^1.6.0 dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false optional: true - ora@5.4.1: + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} dependencies: bl: 4.1.0 chalk: 4.1.2 @@ -7017,53 +4570,99 @@ snapshots: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 + dev: true - os-tmpdir@1.0.2: {} + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true - outdent@0.5.0: {} + /outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + dev: true - p-cancelable@3.0.0: {} + /p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + dev: true - p-filter@2.1.0: + /p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} dependencies: p-map: 2.1.0 + dev: true - p-limit@2.3.0: + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} dependencies: p-try: 2.2.0 + dev: true - p-limit@3.1.0: + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 + dev: true - p-limit@4.0.0: + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - yocto-queue: 1.0.0 + yocto-queue: 1.1.1 + dev: true - p-locate@4.1.0: + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} dependencies: p-limit: 2.3.0 + dev: true - p-locate@5.0.0: + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} dependencies: p-limit: 3.1.0 + dev: true - p-map@2.1.0: {} + /p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + dev: true - p-map@4.0.0: + /p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} dependencies: aggregate-error: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true - p-try@2.2.0: {} + /package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + dev: true - package-json@8.1.1: + /package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} dependencies: got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 semver: 7.6.2 + dev: true - pacote@15.2.0: + /pacote@15.2.0: + resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true dependencies: '@npmcli/git': 4.1.0 '@npmcli/installed-package-contents': 2.1.0 @@ -7086,72 +4685,111 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - parse-github-url@1.0.2: {} - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.24.6 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 + /parse-github-url@1.0.3: + resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} + engines: {node: '>= 0.10'} + hasBin: true + dev: true - path-exists@4.0.0: {} + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true - path-is-absolute@1.0.1: {} + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true - path-key@3.1.1: {} + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true - path-parse@1.0.7: {} + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: + /path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} dependencies: - lru-cache: 10.2.2 + lru-cache: 10.4.3 minipass: 7.1.2 + dev: true - path-type@4.0.0: {} + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true - pathe@1.1.2: {} + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true - pathval@1.1.1: {} + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true - pause-stream@0.0.11: + /pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} dependencies: through: 2.3.8 + dev: true - pg-int8@1.0.1: {} + /pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + dev: false - pg-protocol@1.6.1: {} + /pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + dev: false - pg-types@2.2.0: + /pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 postgres-bytea: 1.0.0 postgres-date: 1.0.7 postgres-interval: 1.2.0 + dev: false - pgsql-ast-parser@11.2.0: + /pgsql-ast-parser@11.2.0: + resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} dependencies: moo: 0.5.2 nearley: 2.20.1 + dev: false - pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: {} - - picocolors@1.0.1: {} + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - picomatch@2.3.1: {} + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true - pify@4.0.1: {} + /pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: true - pino-abstract-transport@1.2.0: + /pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} dependencies: readable-stream: 4.5.2 split2: 4.2.0 - pino-std-serializers@6.2.2: {} + /pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} - pino@8.21.0: + /pino@8.21.0: + resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} + hasBin: true dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 @@ -7165,167 +4803,253 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 - pkg-dir@4.2.0: + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} dependencies: find-up: 4.1.0 + dev: true - pkg-types@1.1.1: + /pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} dependencies: confbox: 0.1.7 - mlly: 1.7.0 + mlly: 1.7.1 pathe: 1.1.2 + dev: true - possible-typed-array-names@1.0.0: {} - - postcss@8.4.38: + /postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 + dev: true - postgres-array@2.0.0: {} + /postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + dev: false - postgres-bytea@1.0.0: {} + /postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + dev: false - postgres-date@1.0.7: {} + /postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + dev: false - postgres-interval@1.2.0: + /postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} dependencies: xtend: 4.0.2 + dev: false - preferred-pm@3.1.3: + /preferred-pm@3.1.4: + resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} + engines: {node: '>=10'} dependencies: find-up: 5.0.0 find-yarn-workspace-root2: 1.2.16 path-exists: 4.0.0 - which-pm: 2.0.0 + which-pm: 2.2.0 + dev: true - prettier@2.8.8: {} + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true - pretty-format@29.7.0: + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 + dev: true - proc-log@3.0.0: {} + /proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - process-nextick-args@2.0.1: {} + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true - process-warning@2.3.2: {} + /process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} - process-warning@3.0.0: {} + /process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} - process@0.11.10: {} + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} - progress@2.0.3: {} + /progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true - promise-inflight@1.0.1: {} + /promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + dev: true - promise-retry@2.0.1: + /promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} dependencies: err-code: 2.0.3 retry: 0.12.0 + dev: true - prompts-ncu@3.0.0: + /prompts-ncu@3.0.0: + resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} + engines: {node: '>= 14'} dependencies: kleur: 4.1.5 sisteransi: 1.0.5 + dev: true - proto-list@1.2.4: {} + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true - proxy-addr@2.0.7: + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - ps-tree@1.2.0: + /ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true dependencies: event-stream: 3.3.4 + dev: true - pseudomap@1.0.2: {} + /pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true - pstree.remy@1.1.8: {} + /pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + dev: true - punycode@2.3.1: {} + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} - pupa@3.1.0: + /pupa@3.1.0: + resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + engines: {node: '>=12.20'} dependencies: escape-goat: 4.0.0 + dev: true - queue-microtask@1.2.3: {} - - quick-format-unescaped@4.0.4: {} + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true - quick-lru@4.0.1: {} + /quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - quick-lru@5.1.1: {} + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true - railroad-diagrams@1.0.0: {} + /railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + dev: false - randexp@0.4.6: + /randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} dependencies: discontinuous-range: 1.0.0 ret: 0.1.15 + dev: false - rc-config-loader@4.1.3: + /rc-config-loader@4.1.3: + resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) js-yaml: 4.1.0 json5: 2.2.3 require-from-string: 2.0.2 transitivePeerDependencies: - supports-color + dev: true - rc@1.2.8: + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true dependencies: deep-extend: 0.6.0 ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 + dev: true - react-is@18.3.1: {} + /react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + dev: true - read-package-json-fast@3.0.2: + /read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: json-parse-even-better-errors: 3.0.2 npm-normalize-package-bin: 3.0.1 + dev: true - read-package-json@6.0.4: + /read-package-json@6.0.4: + resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. dependencies: - glob: 10.4.1 + glob: 10.4.5 json-parse-even-better-errors: 3.0.2 normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 + dev: true - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - read-yaml-file@1.1.0: + /read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 + dev: true - readable-stream@1.0.34: + /readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} dependencies: core-util-is: 1.0.3 inherits: 2.0.4 isarray: 0.0.1 string_decoder: 0.10.31 + dev: true - readable-stream@2.3.8: + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -7334,14 +5058,19 @@ snapshots: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 + dev: true - readable-stream@3.6.2: + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.5.2: + /readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: abort-controller: 3.0.0 buffer: 6.0.3 @@ -7349,216 +5078,284 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 - readdirp@3.6.0: + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 + dev: true - real-require@0.2.0: {} - - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - - regenerator-runtime@0.14.1: {} + /real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: true - registry-auth-token@5.0.2: + /registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} dependencies: '@pnpm/npm-conf': 2.2.2 + dev: true - registry-url@6.0.1: + /registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} dependencies: rc: 1.2.8 + dev: true - remote-git-tags@3.0.0: {} + /remote-git-tags@3.0.0: + resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} + engines: {node: '>=8'} + dev: true - require-directory@2.1.1: {} + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true - require-from-string@2.0.2: {} + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} - require-in-the-middle@7.3.0: + /require-in-the-middle@7.3.0: + resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} + engines: {node: '>=8.6.0'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: - supports-color + dev: false - require-main-filename@2.0.0: {} - - resolve-alpn@1.2.1: {} + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: true - resolve-from@5.0.0: {} + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true - resolve@1.22.8: + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true dependencies: - is-core-module: 2.13.1 + is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - responselike@3.0.0: + /responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} dependencies: lowercase-keys: 3.0.0 + dev: true - restore-cursor@3.1.0: + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} dependencies: onetime: 5.1.2 signal-exit: 3.0.7 + dev: true - ret@0.1.15: {} + /ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + dev: false - ret@0.2.2: {} + /ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} - retry@0.12.0: {} + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: true - reusify@1.0.4: {} + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.3.1: {} + /rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@2.7.1: + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true dependencies: glob: 7.2.3 + dev: true - rimraf@3.0.2: + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true dependencies: glob: 7.2.3 + dev: true - rimraf@5.0.7: + /rimraf@5.0.9: + resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} + engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} + hasBin: true dependencies: - glob: 10.4.1 + glob: 10.4.5 + dev: true - rollup@4.18.0: + /rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.0 - '@rollup/rollup-android-arm64': 4.18.0 - '@rollup/rollup-darwin-arm64': 4.18.0 - '@rollup/rollup-darwin-x64': 4.18.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 - '@rollup/rollup-linux-arm-musleabihf': 4.18.0 - '@rollup/rollup-linux-arm64-gnu': 4.18.0 - '@rollup/rollup-linux-arm64-musl': 4.18.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 - '@rollup/rollup-linux-riscv64-gnu': 4.18.0 - '@rollup/rollup-linux-s390x-gnu': 4.18.0 - '@rollup/rollup-linux-x64-gnu': 4.18.0 - '@rollup/rollup-linux-x64-musl': 4.18.0 - '@rollup/rollup-win32-arm64-msvc': 4.18.0 - '@rollup/rollup-win32-ia32-msvc': 4.18.0 - '@rollup/rollup-win32-x64-msvc': 4.18.0 + '@rollup/rollup-android-arm-eabi': 4.18.1 + '@rollup/rollup-android-arm64': 4.18.1 + '@rollup/rollup-darwin-arm64': 4.18.1 + '@rollup/rollup-darwin-x64': 4.18.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.18.1 + '@rollup/rollup-linux-arm-musleabihf': 4.18.1 + '@rollup/rollup-linux-arm64-gnu': 4.18.1 + '@rollup/rollup-linux-arm64-musl': 4.18.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.18.1 + '@rollup/rollup-linux-riscv64-gnu': 4.18.1 + '@rollup/rollup-linux-s390x-gnu': 4.18.1 + '@rollup/rollup-linux-x64-gnu': 4.18.1 + '@rollup/rollup-linux-x64-musl': 4.18.1 + '@rollup/rollup-win32-arm64-msvc': 4.18.1 + '@rollup/rollup-win32-ia32-msvc': 4.18.1 + '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 + dev: true - rsocket-core@1.0.0-alpha.3: {} + /rsocket-core@1.0.0-alpha.3: + resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} - rsocket-websocket-client@1.0.0-alpha.3: + /rsocket-websocket-client@1.0.0-alpha.3: + resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} dependencies: rsocket-core: 1.0.0-alpha.3 + dev: true - run-async@3.0.0: {} + /run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + dev: true - run-parallel@1.2.0: + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 + dev: true - rxjs@7.8.1: - dependencies: - tslib: 2.6.2 - - safe-array-concat@1.1.2: + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 - - safe-buffer@5.1.2: {} + tslib: 2.6.3 + dev: true - safe-buffer@5.2.1: {} + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true - safe-regex-test@1.0.3: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex2@2.0.0: + /safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} dependencies: ret: 0.2.2 - safe-stable-stringify@2.4.3: {} + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} - safer-buffer@2.1.2: {} + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true - secure-json-parse@2.7.0: {} + /secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - semver-diff@4.0.0: + /semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} dependencies: semver: 7.6.2 + dev: true - semver-utils@1.1.4: {} - - semver@5.7.2: {} - - semver@7.6.2: {} - - set-blocking@2.0.0: {} + /semver-utils@1.1.4: + resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} + dev: true - set-cookie-parser@2.6.0: {} + /semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - shebang-command@1.2.0: + /shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} dependencies: shebang-regex: 1.0.0 + dev: true - shebang-command@2.0.0: + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + dev: true - shebang-regex@1.0.0: {} - - shebang-regex@3.0.0: {} + /shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + dev: true - shell-quote@1.8.1: {} + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true - shimmer@1.2.1: {} + /shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + dev: true - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + /shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + dev: false - siginfo@2.0.0: {} + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true - signal-exit@3.0.7: {} + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true - signal-exit@4.1.0: {} + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true - sigstore@1.9.0: + /sigstore@1.9.0: + resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 @@ -7567,197 +5364,271 @@ snapshots: make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color + dev: true - simple-swizzle@0.2.2: + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: is-arrayish: 0.3.2 + dev: false - simple-update-notifier@2.0.0: + /simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} dependencies: semver: 7.6.2 + dev: true - sisteransi@1.0.5: {} - - slash@3.0.0: {} + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true - smart-buffer@4.2.0: {} + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true - smartwrap@2.0.2: - dependencies: - array.prototype.flat: 1.3.2 - breakword: 1.0.6 - grapheme-splitter: 1.0.4 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - yargs: 15.4.1 + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: true - socks-proxy-agent@7.0.0: + /socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color + dev: true - socks@2.8.3: + /socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 + dev: true - sonic-boom@3.8.1: + /sonic-boom@3.8.1: + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} dependencies: atomic-sleep: 1.0.0 - source-map-js@1.2.0: {} + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true - source-map-support@0.5.21: + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + dev: true - source-map@0.6.1: {} + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true - sparse-bitfield@3.0.3: + /sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} dependencies: memory-pager: 1.5.0 + dev: false - spawn-command@0.0.2: {} + /spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + dev: true - spawn-please@2.0.2: + /spawn-please@2.0.2: + resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} + engines: {node: '>=14'} dependencies: cross-spawn: 7.0.3 + dev: true - spawndamnit@2.0.0: + /spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} dependencies: cross-spawn: 5.1.0 signal-exit: 3.0.7 + dev: true - spdx-correct@3.2.0: + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.18 + dev: true - spdx-exceptions@2.5.0: {} + /spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + dev: true - spdx-expression-parse@3.0.1: + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 + dev: true - spdx-license-ids@3.0.18: {} + /spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + dev: true - split2@4.2.0: {} + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} - split@0.3.3: + /split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} dependencies: through: 2.3.8 + dev: true - sprintf-js@1.0.3: {} + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true - sprintf-js@1.1.3: {} + /sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + dev: true - ssri@10.0.6: + /ssri@10.0.6: + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: minipass: 7.1.2 + dev: true - ssri@9.0.1: + /ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: minipass: 3.3.6 + dev: true - stack-trace@0.0.10: {} + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + dev: false - stackback@0.0.2: {} + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true - std-env@3.7.0: {} + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true - stream-combiner@0.0.4: + /stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} dependencies: duplexer: 0.1.2 + dev: true - stream-transform@2.1.3: - dependencies: - mixme: 0.5.10 - - string-argv@0.3.2: {} + /string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + dev: true - string-width@4.2.3: + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + dev: true - string-width@5.1.2: + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 + dev: true - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string_decoder@0.10.31: {} + /string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + dev: true - string_decoder@1.1.1: + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 + dev: true - string_decoder@1.3.0: + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 - strip-ansi@6.0.1: + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 + dev: true - strip-ansi@7.1.0: + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 + dev: true - strip-bom@3.0.0: {} - - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true - strip-json-comments@2.0.1: {} + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true - strip-json-comments@5.0.1: {} + /strip-json-comments@5.0.1: + resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} + engines: {node: '>=14.16'} + dev: true - strip-literal@1.3.0: + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: - acorn: 8.11.3 + acorn: 8.12.1 + dev: true - supports-color@5.5.0: + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - supports-color@7.2.0: + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - supports-color@8.1.1: + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} dependencies: has-flag: 4.0.0 + dev: true - supports-preserve-symlinks-flag@1.0.0: {} + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} - tar@6.2.1: + /tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 @@ -7765,53 +5636,101 @@ snapshots: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 + dev: true - term-size@2.2.1: {} + /term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: true - text-hex@1.0.0: {} + /text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: false - thread-stream@2.7.0: + /thread-stream@2.7.0: + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} dependencies: real-require: 0.2.0 - through2@2.0.5: + /through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} dependencies: readable-stream: 2.3.8 xtend: 4.0.2 + dev: true - through@2.3.8: {} + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true - tinybench@2.8.0: {} + /tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + dev: true - tinypool@0.7.0: {} + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + dev: true - tinyspy@2.2.1: {} + /tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + dev: true - tmp@0.0.33: + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} dependencies: os-tmpdir: 1.0.2 + dev: true - to-regex-range@5.0.1: + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 + dev: true - toad-cache@3.7.0: {} + /toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} - touch@3.1.1: {} + /touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + dev: true - tr46@4.1.1: + /tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} dependencies: punycode: 2.3.1 + dev: false - tree-kill@1.2.2: {} - - trim-newlines@3.0.1: {} + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true - triple-beam@1.4.1: {} + /triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + dev: false - ts-codec@1.2.2: {} + /ts-codec@1.2.2: + resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} + dev: false - ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): + /ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true dependencies: chokidar: 3.6.0 dynamic-dedupe: 0.3.0 @@ -7828,8 +5747,21 @@ snapshots: - '@swc/core' - '@swc/wasm' - '@types/node' + dev: true - ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): + /ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -7837,8 +5769,8 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 18.11.11 - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 @@ -7846,136 +5778,146 @@ snapshots: typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: true - tsc-watch@6.2.0(typescript@5.2.2): + /tsc-watch@6.2.0(typescript@5.2.2): + resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' dependencies: cross-spawn: 7.0.3 node-cleanup: 2.1.2 ps-tree: 1.2.0 string-argv: 0.3.2 typescript: 5.2.2 + dev: true - tsconfck@3.0.3(typescript@5.4.5): - optionalDependencies: - typescript: 5.4.5 + /tsconfck@3.1.1(typescript@5.2.2): + resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.2.2 + dev: true - tsconfig@7.0.0: + /tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} dependencies: '@types/strip-bom': 3.0.0 '@types/strip-json-comments': 0.0.30 strip-bom: 3.0.0 strip-json-comments: 2.0.1 + dev: true - tslib@2.6.2: {} - - tty-table@4.2.3: - dependencies: - chalk: 4.1.2 - csv: 5.5.3 - kleur: 4.1.5 - smartwrap: 2.0.2 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - yargs: 17.7.2 + /tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - tuf-js@1.1.7: + /tuf-js@1.1.7: + resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color + dev: true - type-detect@4.0.8: {} - - type-fest@0.13.1: {} - - type-fest@0.21.3: {} - - type-fest@0.6.0: {} - - type-fest@0.8.1: {} - - type-fest@1.4.0: {} - - type-fest@2.19.0: {} - - typed-array-buffer@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + /type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + dev: true - typed-array-length@1.0.6: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: true - typedarray-to-buffer@3.1.5: + /typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: is-typedarray: 1.0.0 + dev: true - typescript@5.2.2: {} - - typescript@5.4.5: {} - - ufo@1.5.3: {} + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + /ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + dev: true - undefsafe@2.0.5: {} + /undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + dev: true - unique-filename@2.0.1: + /unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: unique-slug: 3.0.0 + dev: true - unique-filename@3.0.0: + /unique-filename@3.0.0: + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: unique-slug: 4.0.0 + dev: true - unique-slug@3.0.0: + /unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: imurmurhash: 0.1.4 + dev: true - unique-slug@4.0.0: + /unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: imurmurhash: 0.1.4 + dev: true - unique-string@3.0.0: + /unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} dependencies: crypto-random-string: 4.0.0 + dev: true - universalify@0.1.2: {} + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true - untildify@4.0.0: {} + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true - update-notifier@6.0.2: + /update-notifier@6.0.2: + resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} + engines: {node: '>=14.16'} dependencies: boxen: 7.1.1 chalk: 5.3.0 @@ -7991,34 +5933,53 @@ snapshots: semver: 7.6.2 semver-diff: 4.0.0 xdg-basedir: 5.1.0 + dev: true - uri-js@4.4.1: + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.1 - util-deprecate@1.0.2: {} + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@9.0.1: {} + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false - v8-compile-cache-lib@3.0.1: {} + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true - validate-npm-package-license@3.0.4: + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 + dev: true - validate-npm-package-name@5.0.1: {} + /validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - vary@1.1.2: {} + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false - vite-node@0.34.6(@types/node@18.11.11): + /vite-node@0.34.6(@types/node@18.11.11): + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@5.5.0) - mlly: 1.7.0 + debug: 4.3.5(supports-color@5.5.0) + mlly: 1.7.1 pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.2.11(@types/node@18.11.11) + vite: 5.3.3(@types/node@18.11.11) transitivePeerDependencies: - '@types/node' - less @@ -8028,28 +5989,90 @@ snapshots: - sugarss - supports-color - terser + dev: true - vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@18.11.11)): + /vite-tsconfig-paths@4.3.2(typescript@5.2.2): + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) globrex: 0.1.2 - tsconfck: 3.0.3(typescript@5.4.5) - optionalDependencies: - vite: 5.2.11(@types/node@18.11.11) + tsconfck: 3.1.1(typescript@5.2.2) transitivePeerDependencies: - supports-color - typescript + dev: true - vite@5.2.11(@types/node@18.11.11): + /vite@5.3.3(@types/node@18.11.11): + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 - optionalDependencies: '@types/node': 18.11.11 + esbuild: 0.21.5 + postcss: 8.4.39 + rollup: 4.18.1 + optionalDependencies: fsevents: 2.3.3 + dev: true - vitest@0.34.6: + /vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true dependencies: '@types/chai': 4.3.16 '@types/chai-subset': 1.3.5 @@ -8059,11 +6082,11 @@ snapshots: '@vitest/snapshot': 0.34.6 '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.3 cac: 6.7.14 chai: 4.4.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) local-pkg: 0.4.3 magic-string: 0.30.10 pathe: 1.1.2 @@ -8072,9 +6095,9 @@ snapshots: strip-literal: 1.3.0 tinybench: 2.8.0 tinypool: 0.7.0 - vite: 5.2.11(@types/node@18.11.11) + vite: 5.3.3(@types/node@18.11.11) vite-node: 0.34.6(@types/node@18.11.11) - why-is-node-running: 2.2.2 + why-is-node-running: 2.3.0 transitivePeerDependencies: - less - lightningcss @@ -8083,157 +6106,204 @@ snapshots: - sugarss - supports-color - terser + dev: true - wcwidth@1.0.1: + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.4 + dev: true - web-streams-polyfill@3.3.3: {} + /web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + dev: false - webidl-conversions@7.0.0: {} + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: false - whatwg-url@13.0.0: + /whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 + dev: false - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - which-module@2.0.1: {} - - which-pm@2.0.0: + /which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} dependencies: load-yaml-file: 0.2.0 path-exists: 4.0.0 + dev: true - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - - which@1.3.1: + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true dependencies: isexe: 2.0.0 + dev: true - which@2.0.2: + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true dependencies: isexe: 2.0.0 + dev: true - which@3.0.1: + /which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true dependencies: isexe: 2.0.0 + dev: true - why-is-node-running@2.2.2: + /why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true dependencies: siginfo: 2.0.0 stackback: 0.0.2 + dev: true - wide-align@1.1.5: + /wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: string-width: 4.2.3 + dev: true - widest-line@4.0.1: + /widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} dependencies: string-width: 5.1.2 + dev: true - winston-transport@4.7.0: + /winston-transport@4.7.1: + resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} + engines: {node: '>= 12.0.0'} dependencies: - logform: 2.6.0 + logform: 2.6.1 readable-stream: 3.6.2 triple-beam: 1.4.1 + dev: false - winston@3.13.0: + /winston@3.13.1: + resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} + engines: {node: '>= 12.0.0'} dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 async: 3.2.5 is-stream: 2.0.1 - logform: 2.6.0 + logform: 2.6.1 one-time: 1.0.0 readable-stream: 3.6.2 safe-stable-stringify: 2.4.3 stack-trace: 0.0.10 triple-beam: 1.4.1 - winston-transport: 4.7.0 + winston-transport: 4.7.1 + dev: false - wrap-ansi@6.2.0: + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + dev: true - wrap-ansi@7.0.0: + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + dev: true - wrap-ansi@8.1.0: + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 + dev: true - wrappy@1.0.2: {} + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true - write-file-atomic@3.0.3: + /write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} dependencies: imurmurhash: 0.1.4 is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 + dev: true - ws@8.17.0: {} - - ws@8.2.3: {} - - xdg-basedir@5.1.0: {} - - xtend@4.0.2: {} - - y18n@4.0.3: {} + /ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true - y18n@5.0.8: {} + /xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + dev: true - yallist@2.1.2: {} + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} - yallist@4.0.0: {} + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true - yaml@2.4.2: {} + /yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: true - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true - yargs-parser@20.2.9: {} + /yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + dev: false - yargs-parser@21.1.1: {} + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true - yargs@16.2.0: + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} dependencies: cliui: 7.0.4 escalade: 3.1.2 @@ -8242,8 +6312,11 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 + dev: true - yargs@17.7.2: + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -8252,11 +6325,35 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true - yn@3.1.1: {} + /yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + dev: true - yocto-queue@0.1.0: {} + /yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + dev: true - yocto-queue@1.0.0: {} + /zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + dev: false - zod@3.23.8: {} + github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: + resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} + name: pgwire + version: 0.7.0 + engines: {node: '>=14.18.0'} + dev: false From a049709628bddfcb62a79176bd098dff0ad00ba3 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 15 Jul 2024 17:18:04 +0200 Subject: [PATCH 008/298] scaffolding for postgres module --- modules/module-postgres/CHANGELOG.md | 1 + modules/module-postgres/LICENSE | 67 + modules/module-postgres/README.md | 3 + modules/module-postgres/package.json | 32 + .../connection/PostgresConnectionManager.ts | 0 .../connection/PostgresConnectionManager2.ts | 22 + modules/module-postgres/src/index.ts | 0 .../src/module/PostgresModule.ts | 30 + .../replication/PostgresReplicationAdapter.ts | 35 + .../src/replication/PostgresSyncAPIAdapter.ts | 37 + modules/module-postgres/src/types/types.ts | 39 + .../test/src/placeholder.test.ts | 5 + modules/module-postgres/test/tsconfig.json | 20 + modules/module-postgres/tsconfig.json | 31 + modules/module-postgres/vitest.config.ts | 9 + packages/service-core/src/index.ts | 3 + .../service-core/src/modules/modules-index.ts | 2 + .../replication/core/ReplicationAdapter.ts | 2 +- .../src/replication/core/ReplicationModule.ts | 12 +- .../src/replication/replication-index.ts | 6 + packages/types/src/config/PowerSyncConfig.ts | 39 +- packages/types/src/index.ts | 1 - pnpm-lock.yaml | 7461 +++++++++-------- pnpm-workspace.yaml | 1 + 24 files changed, 4481 insertions(+), 3377 deletions(-) create mode 100644 modules/module-postgres/CHANGELOG.md create mode 100644 modules/module-postgres/LICENSE create mode 100644 modules/module-postgres/README.md create mode 100644 modules/module-postgres/package.json rename packages/service-core/src/util/PgManager.ts => modules/module-postgres/src/connection/PostgresConnectionManager.ts (100%) create mode 100644 modules/module-postgres/src/connection/PostgresConnectionManager2.ts create mode 100644 modules/module-postgres/src/index.ts create mode 100644 modules/module-postgres/src/module/PostgresModule.ts create mode 100644 modules/module-postgres/src/replication/PostgresReplicationAdapter.ts create mode 100644 modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts create mode 100644 modules/module-postgres/src/types/types.ts create mode 100644 modules/module-postgres/test/src/placeholder.test.ts create mode 100644 modules/module-postgres/test/tsconfig.json create mode 100644 modules/module-postgres/tsconfig.json create mode 100644 modules/module-postgres/vitest.config.ts create mode 100644 packages/service-core/src/modules/modules-index.ts diff --git a/modules/module-postgres/CHANGELOG.md b/modules/module-postgres/CHANGELOG.md new file mode 100644 index 000000000..01e900aa0 --- /dev/null +++ b/modules/module-postgres/CHANGELOG.md @@ -0,0 +1 @@ +# @powersync/service-module-postgres diff --git a/modules/module-postgres/LICENSE b/modules/module-postgres/LICENSE new file mode 100644 index 000000000..c8efd46cc --- /dev/null +++ b/modules/module-postgres/LICENSE @@ -0,0 +1,67 @@ +# Functional Source License, Version 1.1, Apache 2.0 Future License + +## Abbreviation + +FSL-1.1-Apache-2.0 + +## Notice + +Copyright 2023-2024 Journey Mobile, Inc. + +## Terms and Conditions + +### Licensor ("We") + +The party offering the Software under these Terms and Conditions. + +### The Software + +The "Software" is each version of the software that we make available under these Terms and Conditions, as indicated by our inclusion of these Terms and Conditions with the Software. + +### License Grant + +Subject to your compliance with this License Grant and the Patents, Redistribution and Trademark clauses below, we hereby grant you the right to use, copy, modify, create derivative works, publicly perform, publicly display and redistribute the Software for any Permitted Purpose identified below. + +### Permitted Purpose + +A Permitted Purpose is any purpose other than a Competing Use. A Competing Use means making the Software available to others in a commercial product or service that: + +1. substitutes for the Software; +2. substitutes for any other product or service we offer using the Software that exists as of the date we make the Software available; or +3. offers the same or substantially similar functionality as the Software. + +Permitted Purposes specifically include using the Software: + +1. for your internal use and access; +2. for non-commercial education; +3. for non-commercial research; and +4. in connection with professional services that you provide to a licensee using the Software in accordance with these Terms and Conditions. + +### Patents + +To the extent your use for a Permitted Purpose would necessarily infringe our patents, the license grant above includes a license under our patents. If you make a claim against any party that the Software infringes or contributes to the infringement of any patent, then your patent license to the Software ends immediately. + +### Redistribution + +The Terms and Conditions apply to all copies, modifications and derivatives of the Software. +If you redistribute any copies, modifications or derivatives of the Software, you must include a copy of or a link to these Terms and Conditions and not remove any copyright notices provided in or with the Software. + +### Disclaimer + +THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT. +IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE. + +### Trademarks + +Except for displaying the License Details and identifying us as the origin of the Software, you have no right under these Terms and Conditions to use our trademarks, trade names, service marks or product names. + +## Grant of Future License + +We hereby irrevocably grant you an additional license to use the Software under the Apache License, Version 2.0 that is effective on the second anniversary of the date we make the Software available. On or after that date, you may use the Software under the Apache License, Version 2.0, in which case the following will apply: + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/modules/module-postgres/README.md b/modules/module-postgres/README.md new file mode 100644 index 000000000..78377396a --- /dev/null +++ b/modules/module-postgres/README.md @@ -0,0 +1,3 @@ +# PowerSync Service Module Postgres + +Postgres replication module for PowerSync diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json new file mode 100644 index 000000000..5497d72e4 --- /dev/null +++ b/modules/module-postgres/package.json @@ -0,0 +1,32 @@ +{ + "name": "@powersync/service-module-postgres", + "repository": "https://github.com/powersync-ja/powersync-service", + "types": "dist/index.d.ts", + "publishConfig": { + "access": "public" + }, + "version": "0.0.1", + "main": "dist/index.js", + "license": "FSL-1.1-Apache-2.0", + "type": "module", + "scripts": { + "build": "tsc -b", + "build:tests": "tsc -b test/tsconfig.json", + "test": "vitest --no-threads", + "clean": "rm -rf ./lib && tsc -b --clean" + }, + "dependencies": { + "@powersync/lib-services-framework": "workspace:*", + "@powersync/service-core": "workspace:*", + "@powersync/service-jpgwire": "workspace:*", + "@powersync/service-jsonbig": "workspace:*", + "@powersync/service-sync-rules": "workspace:*", + "@powersync/service-types": "workspace:*", + "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87", + "ts-codec": "^1.2.2" + }, + "devDependencies": { + "typescript": "^5.2.2", + "vitest": "^0.34.6" + } +} diff --git a/packages/service-core/src/util/PgManager.ts b/modules/module-postgres/src/connection/PostgresConnectionManager.ts similarity index 100% rename from packages/service-core/src/util/PgManager.ts rename to modules/module-postgres/src/connection/PostgresConnectionManager.ts diff --git a/modules/module-postgres/src/connection/PostgresConnectionManager2.ts b/modules/module-postgres/src/connection/PostgresConnectionManager2.ts new file mode 100644 index 000000000..242162c78 --- /dev/null +++ b/modules/module-postgres/src/connection/PostgresConnectionManager2.ts @@ -0,0 +1,22 @@ +import * as pgwire from '@powersync/service-jpgwire'; + +import { replication } from '@powersync/service-core'; +import { PostgresConnectionConfig } from '../types/types.js'; + +export type PostgresConnection = pgwire.PgClient; + +export class PostgresConnectionManager implements replication.ConnectionManager { + constructor(protected config: PostgresConnectionConfig) {} + + createReplicationConnection(): Promise { + throw new Error('Method not implemented.'); + } + + createConnection(): Promise { + throw new Error('Method not implemented.'); + } + + mapError(error: Error): replication.ConnectionError { + throw new Error('Method not implemented.'); + } +} diff --git a/modules/module-postgres/src/index.ts b/modules/module-postgres/src/index.ts new file mode 100644 index 000000000..e69de29bb diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts new file mode 100644 index 000000000..fe1666944 --- /dev/null +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -0,0 +1,30 @@ +import * as t from 'ts-codec'; + +import { api, replication } from '@powersync/service-core'; + +import { PostgresConnectionConfig } from '../types/types.js'; +import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; +import { PostgresSyncAPIAdapter } from '../replication/PostgresSyncAPIAdapter.js'; + +export class PostgresModule extends replication.ReplicationModule { + protected configSchema(): t.AnyCodec { + // Intersection types have some limitations in codec typing + return PostgresConnectionConfig; + } + + protected createSyncAPIAdapter(config: PostgresConnectionConfig): api.SyncAPI { + throw new PostgresSyncAPIAdapter(config); + } + + protected createReplicationAdapter(config: PostgresConnectionConfig): PostgresReplicationAdapter { + return new PostgresReplicationAdapter(config); + } + + public shutdown(): Promise { + throw new Error('Method not implemented.'); + } + + public teardown(): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts new file mode 100644 index 000000000..98a092127 --- /dev/null +++ b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts @@ -0,0 +1,35 @@ +import { replication, storage } from '@powersync/service-core'; +import * as sync_rules from '@powersync/service-sync-rules'; +import { PostgresConnection, PostgresConnectionManager } from '../connection/PostgresConnectionManager.js'; +import { PostgresConnectionConfig } from '../types/types.js'; + +export class PostgresReplicationAdapter implements replication.ReplicationAdapter { + constructor(protected config: PostgresConnectionConfig) {} + + name(): string { + return 'postgres'; + } + + createConnectionManager(): PostgresConnectionManager { + return new PostgresConnectionManager(this.config); + } + + validateConfiguration(connection: PostgresConnection): void { + throw new Error('Method not implemented.'); + } + + toReplicationEntities( + connection: PostgresConnection, + pattern: sync_rules.TablePattern + ): Promise[]> { + throw new Error('Method not implemented.'); + } + + startReplication(connection: PostgresConnection, changeListener: (change: storage.SaveOptions) => {}): Promise { + throw new Error('Method not implemented.'); + } + + terminateReplication(): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts new file mode 100644 index 000000000..c9e1dd3fb --- /dev/null +++ b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts @@ -0,0 +1,37 @@ +import { api, replication } from '@powersync/service-core'; + +import { TablePattern } from '@powersync/service-sync-rules'; +import { DatabaseSchema } from '@powersync/service-types'; + +// TODO +import { ExecuteSqlResponse } from '@powersync/service-types/src/routes.js'; + +import { PostgresConnectionConfig } from '../types/types.js'; + +export class PostgresSyncAPIAdapter implements api.SyncAPI { + constructor(protected config: PostgresConnectionConfig) {} + + getDiagnostics(): Promise<{ connected: boolean; errors?: Array<{ level: string; message: string }> }> { + throw new Error('Method not implemented.'); + } + + getDebugTablesInfo(tablePatterns: TablePattern[]): Promise { + throw new Error('Method not implemented.'); + } + + getReplicationLag(): Promise { + throw new Error('Method not implemented.'); + } + + getCheckpoint(): Promise { + throw new Error('Method not implemented.'); + } + + getConnectionSchema(): Promise { + throw new Error('Method not implemented.'); + } + + executeSQL(sql: string, params: any[]): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/modules/module-postgres/src/types/types.ts b/modules/module-postgres/src/types/types.ts new file mode 100644 index 000000000..031a5910b --- /dev/null +++ b/modules/module-postgres/src/types/types.ts @@ -0,0 +1,39 @@ +import * as t from 'ts-codec'; + +import * as service_types from '@powersync/service-types'; + +export const PostgresConnectionConfig = service_types.configFile.dataSourceConfig.and( + t.object({ + type: t.literal('postgresql'), + /** Unique identifier for the connection - optional when a single connection is present. */ + id: t.string.optional(), + /** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */ + tag: t.string.optional(), + uri: t.string.optional(), + hostname: t.string.optional(), + port: service_types.configFile.portCodec.optional(), + username: t.string.optional(), + password: t.string.optional(), + database: t.string.optional(), + + /** Defaults to verify-full */ + sslmode: t.literal('verify-full').or(t.literal('verify-ca')).or(t.literal('disable')).optional(), + /** Required for verify-ca, optional for verify-full */ + cacert: t.string.optional(), + + client_certificate: t.string.optional(), + client_private_key: t.string.optional(), + + /** Expose database credentials */ + demo_database: t.boolean.optional(), + /** Expose "execute-sql" */ + debug_api: t.boolean.optional(), + + /** + * Prefix for the slot name. Defaults to "powersync_" + */ + slot_name_prefix: t.string.optional() + }) +); + +export type PostgresConnectionConfig = t.Decoded; diff --git a/modules/module-postgres/test/src/placeholder.test.ts b/modules/module-postgres/test/src/placeholder.test.ts new file mode 100644 index 000000000..289c3e48d --- /dev/null +++ b/modules/module-postgres/test/src/placeholder.test.ts @@ -0,0 +1,5 @@ +import { describe } from 'vitest'; + +describe('Tests', () => { + // TODO move tests +}); diff --git a/modules/module-postgres/test/tsconfig.json b/modules/module-postgres/test/tsconfig.json new file mode 100644 index 000000000..4e77a4239 --- /dev/null +++ b/modules/module-postgres/test/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "noEmit": true, + "baseUrl": "./", + "esModuleInterop": true, + "skipLibCheck": true, + "sourceMap": true, + "paths": { + "@/*": ["../src/*"] + } + }, + "include": ["src"], + "references": [ + { + "path": "../" + } + ] +} diff --git a/modules/module-postgres/tsconfig.json b/modules/module-postgres/tsconfig.json new file mode 100644 index 000000000..9ceadec40 --- /dev/null +++ b/modules/module-postgres/tsconfig.json @@ -0,0 +1,31 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "esModuleInterop": true, + "skipLibCheck": true, + "sourceMap": true + }, + "include": ["src"], + "references": [ + { + "path": "../../packages/types" + }, + { + "path": "../../packages/jsonbig" + }, + { + "path": "../../packages/jpgwire" + }, + { + "path": "../../packages/sync-rules" + }, + { + "path": "../../packages/service-core" + }, + { + "path": "../../libs/lib-services" + } + ] +} diff --git a/modules/module-postgres/vitest.config.ts b/modules/module-postgres/vitest.config.ts new file mode 100644 index 000000000..b392696b7 --- /dev/null +++ b/modules/module-postgres/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [tsconfigPaths()], + test: { + setupFiles: './test/src/setup.ts' + } +}); diff --git a/packages/service-core/src/index.ts b/packages/service-core/src/index.ts index b82d5d731..7ef5f7276 100644 --- a/packages/service-core/src/index.ts +++ b/packages/service-core/src/index.ts @@ -21,6 +21,9 @@ export * as metrics from './metrics/Metrics.js'; export * from './migrations/migrations.js'; export * as migrations from './migrations/migrations-index.js'; +export * from './modules/modules-index.js'; +export * as modules from './modules/modules-index.js'; + export * from './replication/replication-index.js'; export * as replication from './replication/replication-index.js'; diff --git a/packages/service-core/src/modules/modules-index.ts b/packages/service-core/src/modules/modules-index.ts new file mode 100644 index 000000000..1b32990e8 --- /dev/null +++ b/packages/service-core/src/modules/modules-index.ts @@ -0,0 +1,2 @@ +export * from './ModuleManager.js'; +export * from './AbstractModule.js'; diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 6485926bd..e0a208702 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -6,7 +6,7 @@ import { SaveOptions } from '../../storage/BucketStorage.js'; * Manage the creation and termination of connections for a datasource. * For some data sources there will be no difference between snapshot and replication connections */ -interface ConnectionManager { +export interface ConnectionManager { /** * Create a connection to use for replication. */ diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 70b93f338..12f0de7b7 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -27,17 +27,17 @@ export abstract class ReplicationModule extends AbstractModule { * Create the API adapter for the DataSource required by the sync API * endpoints. */ - protected abstract createSyncAPIAdapter(): SyncAPI; + protected abstract createSyncAPIAdapter(config: DataSourceConfig): SyncAPI; /** * Create the ReplicationAdapter to be used by PowerSync replicator. */ - protected abstract createReplicationAdapter(): ReplicationAdapter; + protected abstract createReplicationAdapter(config: DataSourceConfig): ReplicationAdapter; /** * Return the TS codec schema describing the required configuration values for this module. */ - protected abstract configSchema(): t.ObjectCodec; + protected abstract configSchema(): t.AnyCodec; /** * Register this module's replication adapters and sync API providers if the required configuration is present. @@ -58,10 +58,12 @@ export abstract class ReplicationModule extends AbstractModule { } try { + const baseMatchingConfig = matchingConfig[0]; + const decodedConfig = this.configSchema().decode(baseMatchingConfig); // If validation fails, log the error and continue, no replication will happen for this data source this.validateConfig(matchingConfig[0]); - context.replicationEngine.register(this.createReplicationAdapter()); - context.syncAPIProvider.register(this.createSyncAPIAdapter()); + context.replicationEngine.register(this.createReplicationAdapter(decodedConfig)); + context.syncAPIProvider.register(this.createSyncAPIAdapter(decodedConfig)); } catch (e) { logger.error(e); } diff --git a/packages/service-core/src/replication/replication-index.ts b/packages/service-core/src/replication/replication-index.ts index 967ea71ce..603327fc2 100644 --- a/packages/service-core/src/replication/replication-index.ts +++ b/packages/service-core/src/replication/replication-index.ts @@ -5,3 +5,9 @@ export * from './WalConnection.js'; export * from './WalStream.js'; export * from './WalStreamManager.js'; export * from './WalStreamRunner.js'; + +export * from './core/ReplicationAdapter.js'; +export * from './core/ReplicationEngine.js'; +export * from './core/ReplicationEntity.js'; +export * from './core/Replicator.js'; +export * from './core/ReplicationModule.js'; diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index bf7467ed3..c1d80a6b0 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -3,7 +3,7 @@ import * as t from 'ts-codec'; /** * Users might specify ports as strings if using YAML custom tag environment substitutions */ -const portCodec = t.codec( +export const portCodec = t.codec( 'Port', (value) => value, (value) => (typeof value == 'number' ? value : parseInt(value)) @@ -23,45 +23,11 @@ export const dataSourceConfig = t.object({ // Unique string identifier for the data source type: t.string, /** Unique identifier for the connection - optional when a single connection is present. */ - id: t.string.optional(), + id: t.string.optional() }); export type DataSourceConfig = t.Decoded; -export const postgresConnection = dataSourceConfig.and(t.object({ - type: t.literal('postgresql'), - /** Unique identifier for the connection - optional when a single connection is present. */ - id: t.string.optional(), - /** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */ - tag: t.string.optional(), - uri: t.string.optional(), - hostname: t.string.optional(), - port: portCodec.optional(), - username: t.string.optional(), - password: t.string.optional(), - database: t.string.optional(), - - /** Defaults to verify-full */ - sslmode: t.literal('verify-full').or(t.literal('verify-ca')).or(t.literal('disable')).optional(), - /** Required for verify-ca, optional for verify-full */ - cacert: t.string.optional(), - - client_certificate: t.string.optional(), - client_private_key: t.string.optional(), - - /** Expose database credentials */ - demo_database: t.boolean.optional(), - /** Expose "execute-sql" */ - debug_api: t.boolean.optional(), - - /** - * Prefix for the slot name. Defaults to "powersync_" - */ - slot_name_prefix: t.string.optional() -})); - -export type PostgresConnection = t.Decoded; - export const jwkRSA = t.object({ kty: t.literal('RSA'), kid: t.string, @@ -104,7 +70,6 @@ export type StorageConfig = t.Decoded; export const powerSyncConfig = t.object({ replication: t .object({ - connections: t.array(postgresConnection).optional(), data_sources: t.array(dataSourceConfig).optional() }) .optional(), diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 6ce5aa153..f57c90eb3 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,5 +1,4 @@ export * as configFile from './config/PowerSyncConfig.js'; -export { PostgresConnection } from './config/PowerSyncConfig.js'; export * from './definitions.js'; export * from './config/normalize.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c250d75f..18a731f32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -88,6 +88,40 @@ importers: specifier: ^0.34.6 version: 0.34.6 + modules/module-postgres: + dependencies: + '@powersync/lib-services-framework': + specifier: workspace:* + version: link:../../libs/lib-services + '@powersync/service-core': + specifier: workspace:* + version: link:../../packages/service-core + '@powersync/service-jpgwire': + specifier: workspace:* + version: link:../../packages/jpgwire + '@powersync/service-jsonbig': + specifier: workspace:* + version: link:../../packages/jsonbig + '@powersync/service-sync-rules': + specifier: workspace:* + version: link:../../packages/sync-rules + '@powersync/service-types': + specifier: workspace:* + version: link:../../packages/types + pgwire: + specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + ts-codec: + specifier: ^1.2.2 + version: 1.2.2 + devDependencies: + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vitest: + specifier: ^0.34.6 + version: 0.34.6 + packages/jpgwire: dependencies: '@powersync/service-jsonbig': @@ -104,7 +138,7 @@ importers: version: 3.6.0 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 packages/jsonbig: dependencies: @@ -219,13 +253,13 @@ importers: version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.8.0 + version: 6.8.0(socks@2.8.3) node-fetch: specifier: ^3.3.2 version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -259,7 +293,7 @@ importers: version: 5.2.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.2.2) + version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -365,13 +399,13 @@ importers: version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.8.0 + version: 6.8.0(socks@2.8.3) node-fetch: specifier: ^3.3.2 version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -412,778 +446,3648 @@ importers: packages: - /@babel/code-frame@7.24.7: + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.0.1 - dev: false - /@babel/helper-validator-identifier@7.24.7: + '@babel/helper-validator-identifier@7.24.7': resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - dev: false - /@babel/highlight@7.24.7: + '@babel/highlight@7.24.7': resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 - dev: false - /@babel/runtime@7.24.8: + '@babel/runtime@7.24.8': resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - dev: true - /@changesets/apply-release-plan@7.0.4: + '@changesets/apply-release-plan@7.0.4': resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/config': 3.0.2 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.0 - '@changesets/should-skip-package': 0.1.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.6.2 - dev: true - /@changesets/assemble-release-plan@6.0.3: + '@changesets/assemble-release-plan@6.0.3': resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.1 - '@changesets/should-skip-package': 0.1.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.6.2 - dev: true - /@changesets/changelog-git@0.2.0: + '@changesets/changelog-git@0.2.0': resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} - dependencies: - '@changesets/types': 6.0.0 - dev: true - /@changesets/cli@2.27.7: + '@changesets/cli@2.27.7': resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} hasBin: true - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/apply-release-plan': 7.0.4 - '@changesets/assemble-release-plan': 6.0.3 - '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.2 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.1 - '@changesets/get-release-plan': 4.0.3 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 - '@changesets/should-skip-package': 0.1.0 - '@changesets/types': 6.0.0 - '@changesets/write': 0.3.1 - '@manypkg/get-packages': 1.1.3 - '@types/semver': 7.5.8 - ansi-colors: 4.1.3 - chalk: 2.4.2 - ci-info: 3.9.0 - enquirer: 2.4.1 - external-editor: 3.1.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - mri: 1.2.0 - outdent: 0.5.0 - p-limit: 2.3.0 - preferred-pm: 3.1.4 - resolve-from: 5.0.0 - semver: 7.6.2 - spawndamnit: 2.0.0 - term-size: 2.2.1 - dev: true - /@changesets/config@3.0.2: + '@changesets/config@3.0.2': resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.1 - '@changesets/logger': 0.1.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.7 - dev: true - /@changesets/errors@0.2.0: + '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - dependencies: - extendable-error: 0.1.7 - dev: true - /@changesets/get-dependents-graph@2.1.1: + '@changesets/get-dependents-graph@2.1.1': resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} - dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - chalk: 2.4.2 - fs-extra: 7.0.1 - semver: 7.6.2 - dev: true - /@changesets/get-release-plan@4.0.3: + '@changesets/get-release-plan@4.0.3': resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/assemble-release-plan': 6.0.3 - '@changesets/config': 3.0.2 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/get-version-range-type@0.4.0: + '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - dev: true - /@changesets/git@3.0.0: + '@changesets/git@3.0.0': resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.7 - spawndamnit: 2.0.0 - dev: true - /@changesets/logger@0.1.0: + '@changesets/logger@0.1.0': resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} - dependencies: - chalk: 2.4.2 - dev: true - /@changesets/parse@0.4.0: + '@changesets/parse@0.4.0': resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} - dependencies: - '@changesets/types': 6.0.0 - js-yaml: 3.14.1 - dev: true - /@changesets/pre@2.0.0: + '@changesets/pre@2.0.0': resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - dev: true - /@changesets/read@0.6.0: + '@changesets/read@0.6.0': resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 - '@changesets/parse': 0.4.0 - '@changesets/types': 6.0.0 - chalk: 2.4.2 - fs-extra: 7.0.1 - p-filter: 2.1.0 - dev: true - /@changesets/should-skip-package@0.1.0: + '@changesets/should-skip-package@0.1.0': resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/types@4.1.0: + '@changesets/types@4.1.0': resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - dev: true - /@changesets/types@6.0.0: + '@changesets/types@6.0.0': resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - dev: true - /@changesets/write@0.3.1: + '@changesets/write@0.3.1': resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - prettier: 2.8.8 - dev: true - /@colors/colors@1.5.0: + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} - requiresBuild: true - dev: true - optional: true - /@colors/colors@1.6.0: + '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} - dev: false - /@cspotcode/source-map-support@0.8.1: + '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - dev: true - /@dabh/diagnostics@2.0.3: + '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} - dependencies: - colorspace: 1.1.4 - enabled: 2.0.0 - kuler: 2.0.0 - dev: false - /@esbuild/aix-ppc64@0.21.5: + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm64@0.21.5: + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.21.5: + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.21.5: + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.21.5: + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.21.5: + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.21.5: + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.21.5: + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.21.5: + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.21.5: + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.21.5: + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.21.5: + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.21.5: + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.21.5: + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.21.5: + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.21.5: + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.21.5: + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.21.5: + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.21.5: + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.21.5: + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.21.5: + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.21.5: + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.21.5: + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@fastify/ajv-compiler@3.6.0: + '@fastify/ajv-compiler@3.6.0': resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} - dependencies: - ajv: 8.16.0 - ajv-formats: 2.1.1(ajv@8.16.0) - fast-uri: 2.4.0 - /@fastify/cors@8.4.1: + '@fastify/cors@8.4.1': resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} - dependencies: - fastify-plugin: 4.5.1 - mnemonist: 0.39.5 - dev: false - /@fastify/error@3.4.1: + '@fastify/error@3.4.1': resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} - /@fastify/fast-json-stringify-compiler@4.3.0: + '@fastify/fast-json-stringify-compiler@4.3.0': resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} - dependencies: - fast-json-stringify: 5.16.1 - /@fastify/merge-json-schemas@0.1.1: + '@fastify/merge-json-schemas@0.1.1': resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} - dependencies: - fast-deep-equal: 3.1.3 - /@gar/promisify@1.1.3: + '@gar/promisify@1.1.3': resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - dev: true - /@humanwhocodes/momoa@2.0.4: + '@humanwhocodes/momoa@2.0.4': resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} engines: {node: '>=10.10.0'} - dev: false - /@inquirer/figures@1.0.3: + '@inquirer/figures@1.0.3': resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} engines: {node: '>=18'} - dev: true - /@isaacs/cliui@8.0.2: + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - dependencies: - string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true - /@jest/schemas@29.6.3: + '@jest/schemas@29.6.3': resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@sinclair/typebox': 0.27.8 - dev: true - /@jridgewell/resolve-uri@3.1.2: + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - dev: true - /@jridgewell/sourcemap-codec@1.5.0: + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - dev: true - /@jridgewell/trace-mapping@0.3.9: + '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /@js-sdsl/ordered-set@4.4.2: + '@js-sdsl/ordered-set@4.4.2': resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} - dev: false - /@manypkg/find-root@1.1.0: + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} - dependencies: - '@babel/runtime': 7.24.8 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - dev: true - /@manypkg/get-packages@1.1.3: + '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - dev: true - /@mongodb-js/saslprep@1.1.7: + '@mongodb-js/saslprep@1.1.7': resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} - dependencies: - sparse-bitfield: 3.0.3 - dev: false - /@nodelib/fs.scandir@2.1.5: + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - dev: true - /@nodelib/fs.stat@2.0.5: + '@nodelib/fs.stat@2.0.5': resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true - /@nodelib/fs.walk@1.2.8: + '@nodelib/fs.walk@1.2.8': resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - dev: true - /@npmcli/fs@2.1.2: + '@npmcli/fs@2.1.2': resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.6.2 - dev: true - /@npmcli/fs@3.1.1: + '@npmcli/fs@3.1.1': resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - semver: 7.6.2 - dev: true - /@npmcli/git@4.1.0: + '@npmcli/git@4.1.0': resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - '@npmcli/promise-spawn': 6.0.2 - lru-cache: 7.18.3 - npm-pick-manifest: 8.0.2 - proc-log: 3.0.0 - promise-inflight: 1.0.1 - promise-retry: 2.0.1 - semver: 7.6.2 - which: 3.0.1 - transitivePeerDependencies: - - bluebird - dev: true - /@npmcli/installed-package-contents@2.1.0: + '@npmcli/installed-package-contents@2.1.0': resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true - dependencies: - npm-bundled: 3.0.1 - npm-normalize-package-bin: 3.0.1 - dev: true - /@npmcli/move-file@2.0.1: + '@npmcli/move-file@2.0.1': resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - dev: true - /@npmcli/node-gyp@3.0.0: + '@npmcli/node-gyp@3.0.0': resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true - /@npmcli/promise-spawn@6.0.2: + '@npmcli/promise-spawn@6.0.2': resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - which: 3.0.1 - dev: true - /@npmcli/run-script@6.0.2: + '@npmcli/run-script@6.0.2': resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - '@npmcli/node-gyp': 3.0.0 - '@npmcli/promise-spawn': 6.0.2 - node-gyp: 9.4.1 - read-package-json-fast: 3.0.2 - which: 3.0.1 - transitivePeerDependencies: - - bluebird - - supports-color - dev: true - /@opentelemetry/api-logs@0.51.1: + '@opentelemetry/api-logs@0.51.1': resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} engines: {node: '>=14'} - dependencies: - '@opentelemetry/api': 1.8.0 - dev: false - /@opentelemetry/api-logs@0.52.1: + '@opentelemetry/api-logs@0.52.1': resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} engines: {node: '>=14'} - dependencies: - '@opentelemetry/api': 1.6.0 - dev: false - /@opentelemetry/api@1.6.0: + '@opentelemetry/api@1.6.0': resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} engines: {node: '>=8.0.0'} - dev: false - /@opentelemetry/api@1.8.0: + '@opentelemetry/api@1.8.0': resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} engines: {node: '>=8.0.0'} - dev: false - /@opentelemetry/api@1.9.0: + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - dev: false - /@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0): + '@opentelemetry/context-async-hooks@1.25.1': resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.9.0 - dev: false - /@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0): + '@opentelemetry/core@1.17.0': resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.7.0' + + '@opentelemetry/core@1.24.1': + resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/core@1.25.1': + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-metrics-otlp-http@0.51.1': + resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.43.0': + resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.51.1': + resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.38.0': + resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.41.0': + resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.38.0': + resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.42.0': + resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.40.0': + resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.52.1': + resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.42.0': + resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.42.0': + resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.46.0': + resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.40.0': + resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.40.0': + resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.40.0': + resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.39.0': + resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.43.0': + resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis-4@0.41.0': + resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.46.0': + resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.52.1': + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.51.1': + resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/otlp-transformer@0.51.1': + resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' + + '@opentelemetry/redis-common@0.36.2': + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + + '@opentelemetry/resources@1.17.0': + resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.7.0' + + '@opentelemetry/resources@1.24.1': + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/resources@1.25.1': + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.51.1': + resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.9.0' + '@opentelemetry/api-logs': '>=0.39.1' + + '@opentelemetry/sdk-metrics@1.17.0': + resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.7.0' + + '@opentelemetry/sdk-metrics@1.24.1': + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' + + '@opentelemetry/sdk-trace-base@1.24.1': + resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/sdk-trace-base@1.25.1': + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.17.0': + resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.24.1': + resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.25.1': + resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} + engines: {node: '>=14'} + + '@opentelemetry/sql-common@0.40.1': + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.2.2': + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} + + '@prisma/instrumentation@5.16.1': + resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} + + '@rollup/rollup-android-arm-eabi@4.18.1': + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.1': + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.1': + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.1': + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.1': + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.1': + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.1': + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.1': + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.1': + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.1': + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.1': + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.1': + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.1': + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.1': + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + + '@sentry/core@8.17.0': + resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} + engines: {node: '>=14.18'} + + '@sentry/node@8.17.0': + resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} + engines: {node: '>=14.18'} + + '@sentry/opentelemetry@8.17.0': + resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} + engines: {node: '>=14.18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.25.1 + '@opentelemetry/instrumentation': ^0.52.1 + '@opentelemetry/sdk-trace-base': ^1.25.1 + '@opentelemetry/semantic-conventions': ^1.25.1 + + '@sentry/types@8.17.0': + resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} + engines: {node: '>=14.18'} + + '@sentry/utils@8.17.0': + resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} + engines: {node: '>=14.18'} + + '@sigstore/bundle@1.1.0': + resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/protobuf-specs@0.2.1': + resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/sign@1.0.0': + resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/tuf@1.0.3': + resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + + '@syncpoint/wkx@0.5.2': + resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} + + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@tufjs/canonical-json@1.0.0': + resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@tufjs/models@1.0.4': + resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@types/async@3.2.24': + resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} + + '@types/chai-subset@1.3.5': + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + + '@types/chai@4.3.16': + resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + + '@types/connect@3.4.36': + resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + + '@types/mysql@2.15.22': + resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@13.13.52': + resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} + + '@types/node@15.14.9': + resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} + + '@types/node@18.11.11': + resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} + + '@types/pg-pool@2.0.4': + resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + + '@types/pg@8.6.1': + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + + '@types/semver-utils@1.1.3': + resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + + '@types/strip-bom@3.0.0': + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + + '@types/strip-json-comments@0.0.30': + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + + '@types/ws@8.2.3': + resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + + '@vitest/expect@0.34.6': + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + + '@vitest/runner@0.34.6': + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + + '@vitest/snapshot@0.34.6': + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + + '@vitest/spy@0.34.6': + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + + '@vitest/utils@0.34.6': + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + + acorn-import-assertions@1.9.0: + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + + async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + avvio@8.3.2: + resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-ajv-errors@1.2.0: + resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + ajv: 4.11.8 - 8 + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@16.1.3: + resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + cacache@17.1.4: + resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@6.0.0: + resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} + engines: {node: '>=12'} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exponential-backoff@3.1.1: + resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stringify@5.16.1: + resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} + + fast-memoize@2.5.2: + resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-uri@2.4.0: + resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} + + fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + + fastify@4.23.2: + resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-my-way@7.7.0: + resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} + engines: {node: '>=14'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fp-and-or@0.1.4: + resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} + engines: {node: '>=10'} + + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + has-yarn@3.0.0: + resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hosted-git-info@5.2.1: + resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + hosted-git-info@6.1.1: + resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-in-the-middle@1.7.1: + resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} + + import-in-the-middle@1.9.0: + resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@9.3.5: + resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} + engines: {node: '>=18'} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + + is-npm@6.0.0: + resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-yarn-global@0.4.1: + resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} + engines: {node: '>=12'} + + isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + ix@5.0.0: + resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + json-parse-helpfulerror@1.0.3: + resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} + + json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonlines@0.1.1: + resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + light-my-request@5.13.0: + resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + + load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + logform@2.6.1: + resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} + engines: {node: '>= 12.0.0'} + + lossless-json@2.0.11: + resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + make-fetch-happen@10.2.1: + resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + make-fetch-happen@11.1.1: + resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + minipass-fetch@3.0.5: + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-json-stream@1.0.1: + resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mnemonist@0.39.5: + resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} + + module-details-from-path@1.0.3: + resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.8.0: + resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-gyp@9.4.1: + resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} + engines: {node: ^12.13 || ^14.13 || >=16} + hasBin: true + + nodemon@3.1.4: + resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} + engines: {node: '>=10'} + hasBin: true + + noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + + nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + normalize-package-data@5.0.0: + resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + + npm-bundled@3.0.1: + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-check-updates@16.14.20: + resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} + engines: {node: '>=14.14'} + hasBin: true + + npm-install-checks@6.3.0: + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-package-arg@10.1.0: + resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-packlist@7.0.4: + resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-pick-manifest@8.0.2: + resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-registry-fetch@14.0.5: + resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + opentelemetry-instrumentation-fetch-node@1.2.3: + resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} + engines: {node: '>18.0.0'} + peerDependencies: + '@opentelemetry/api': ^1.6.0 + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + + pacote@15.2.0: + resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + parse-github-url@1.0.3: + resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} + engines: {node: '>= 0.10'} + hasBin: true + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pgsql-ast-parser@11.2.0: + resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} + + pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: + resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} + version: 0.7.0 + engines: {node: '>=14.18.0'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + + pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + + pino@8.21.0: + resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} + hasBin: true + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + preferred-pm@3.1.4: + resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} + engines: {node: '>=10'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + + process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + prompts-ncu@3.0.0: + resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} + engines: {node: '>= 14'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@3.1.0: + resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + engines: {node: '>=12.20'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + + randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + + rc-config-loader@4.1.3: + resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + read-package-json@6.0.4: + resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + remote-git-tags@3.0.0: + resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} + engines: {node: '>=8'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@7.3.0: + resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} + engines: {node: '>=8.6.0'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + + ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@5.0.9: + resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} + engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} + hasBin: true + + rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rsocket-core@1.0.0-alpha.3: + resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} + + rsocket-websocket-client@1.0.0-alpha.3: + resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} + + semver-utils@1.1.4: + resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sigstore@1.9.0: + resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sonic-boom@3.8.1: + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + + spawn-please@2.0.2: + resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} + engines: {node: '>=14'} + + spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssri@10.0.6: + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@5.0.1: + resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} + engines: {node: '>=14.16'} + + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + thread-stream@2.7.0: + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + + tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-codec@1.2.2: + resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} + + ts-node-dev@2.0.0: + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsc-watch@6.2.0: + resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' + + tsconfck@3.1.1: + resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tuf-js@1.1.7: + resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-filename@3.0.0: + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + + update-notifier@6.0.2: + resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} + engines: {node: '>=14.16'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@0.34.6: + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + + vite-tsconfig-paths@4.3.2: + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + winston-transport@4.7.1: + resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} + engines: {node: '>= 12.0.0'} + + winston@3.13.1: + resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} + engines: {node: '>= 12.0.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/runtime@7.24.8': + dependencies: + regenerator-runtime: 0.14.1 + + '@changesets/apply-release-plan@7.0.4': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/config': 3.0.2 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.6.2 + + '@changesets/assemble-release-plan@6.0.3': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.6.2 + + '@changesets/changelog-git@0.2.0': + dependencies: + '@changesets/types': 6.0.0 + + '@changesets/cli@2.27.7': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/apply-release-plan': 7.0.4 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.2 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/get-release-plan': 4.0.3 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.1 + '@manypkg/get-packages': 1.1.3 + '@types/semver': 7.5.8 + ansi-colors: 4.1.3 + chalk: 2.4.2 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + mri: 1.2.0 + outdent: 0.5.0 + p-limit: 2.3.0 + preferred-pm: 3.1.4 + resolve-from: 5.0.0 + semver: 7.6.2 + spawndamnit: 2.0.0 + term-size: 2.2.1 + + '@changesets/config@3.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/logger': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.7 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.1': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + chalk: 2.4.2 + fs-extra: 7.0.1 + semver: 7.6.2 + + '@changesets/get-release-plan@4.0.3': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/config': 3.0.2 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.0': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.7 + spawndamnit: 2.0.0 + + '@changesets/logger@0.1.0': + dependencies: + chalk: 2.4.2 + + '@changesets/parse@0.4.0': + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + + '@changesets/pre@2.0.0': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.0': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + chalk: 2.4.2 + fs-extra: 7.0.1 + p-filter: 2.1.0 + + '@changesets/should-skip-package@0.1.0': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.0.0': {} + + '@changesets/write@0.3.1': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + + '@colors/colors@1.5.0': + optional: true + + '@colors/colors@1.6.0': {} + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@dabh/diagnostics@2.0.3': + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + + '@fastify/ajv-compiler@3.6.0': + dependencies: + ajv: 8.16.0 + ajv-formats: 2.1.1(ajv@8.16.0) + fast-uri: 2.4.0 + + '@fastify/cors@8.4.1': + dependencies: + fastify-plugin: 4.5.1 + mnemonist: 0.39.5 + + '@fastify/error@3.4.1': {} + + '@fastify/fast-json-stringify-compiler@4.3.0': + dependencies: + fast-json-stringify: 5.16.1 + + '@fastify/merge-json-schemas@0.1.1': + dependencies: + fast-deep-equal: 3.1.3 + + '@gar/promisify@1.1.3': {} + + '@humanwhocodes/momoa@2.0.4': {} + + '@inquirer/figures@1.0.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@js-sdsl/ordered-set@4.4.2': {} + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.24.8 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@mongodb-js/saslprep@1.1.7': + dependencies: + sparse-bitfield: 3.0.3 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@npmcli/fs@2.1.2': + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.6.2 + + '@npmcli/fs@3.1.1': + dependencies: + semver: 7.6.2 + + '@npmcli/git@4.1.0': + dependencies: + '@npmcli/promise-spawn': 6.0.2 + lru-cache: 7.18.3 + npm-pick-manifest: 8.0.2 + proc-log: 3.0.0 + promise-inflight: 1.0.1 + promise-retry: 2.0.1 + semver: 7.6.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + + '@npmcli/installed-package-contents@2.1.0': + dependencies: + npm-bundled: 3.0.1 + npm-normalize-package-bin: 3.0.1 + + '@npmcli/move-file@2.0.1': + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + + '@npmcli/node-gyp@3.0.0': {} + + '@npmcli/promise-spawn@6.0.2': + dependencies: + which: 3.0.1 + + '@npmcli/run-script@6.0.2': + dependencies: + '@npmcli/node-gyp': 3.0.0 + '@npmcli/promise-spawn': 6.0.2 + node-gyp: 9.4.1 + read-package-json-fast: 3.0.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + + '@opentelemetry/api-logs@0.51.1': + dependencies: + '@opentelemetry/api': 1.8.0 + + '@opentelemetry/api-logs@0.52.1': + dependencies: + '@opentelemetry/api': 1.6.0 + + '@opentelemetry/api@1.6.0': {} + + '@opentelemetry/api@1.8.0': {} + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + + '@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.17.0 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) @@ -1191,37 +4095,22 @@ packages: '@opentelemetry/otlp-transformer': 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/sdk-metrics': 1.17.0(@opentelemetry/api@1.6.0) - dev: false - /@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1230,13 +4119,8 @@ packages: '@types/connect': 3.4.36 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1244,13 +4128,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1258,25 +4137,15 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1284,13 +4153,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1299,13 +4163,8 @@ packages: semver: 7.6.2 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1313,13 +4172,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1327,13 +4181,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1341,13 +4190,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1355,13 +4199,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1369,13 +4208,8 @@ packages: '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1383,26 +4217,16 @@ packages: '@types/mysql': 2.15.22 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1412,13 +4236,8 @@ packages: '@types/pg-pool': 2.0.4 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1426,14 +4245,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} - engines: {node: '>=14'} - requiresBuild: true - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@types/shimmer': 1.2.0 @@ -1443,31 +4256,9 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false optional: true - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/api-logs': 0.52.1 - '@types/shimmer': 1.2.0 - import-in-the-middle: 1.9.0 - require-in-the-middle: 7.3.0 - semver: 7.6.2 - shimmer: 1.2.1 - transitivePeerDependencies: - - supports-color - dev: false - - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.52.1 @@ -1478,13 +4269,8 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.52.1 @@ -1495,23 +4281,13 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.0.0 + '@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 @@ -1520,394 +4296,194 @@ packages: '@opentelemetry/sdk-logs': 0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/redis-common@0.36.2: - resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/redis-common@0.36.2': {} - /@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.7.0' + '@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.17.0 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.9.0' - '@opentelemetry/api-logs': '>=0.39.1' + '@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.7.0' + '@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.9.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - - /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/semantic-conventions@1.17.0: - resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 - /@opentelemetry/semantic-conventions@1.24.1: - resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.17.0': {} - /@opentelemetry/semantic-conventions@1.25.1: - resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.24.1': {} - /@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.1.0 + '@opentelemetry/semantic-conventions@1.25.1': {} + + '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - dev: false - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: true + '@pkgjs/parseargs@0.11.0': optional: true - /@pnpm/config.env-replace@1.1.0: - resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} - engines: {node: '>=12.22.0'} - dev: true + '@pnpm/config.env-replace@1.1.0': {} - /@pnpm/network.ca-file@1.0.2: - resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} - engines: {node: '>=12.22.0'} + '@pnpm/network.ca-file@1.0.2': dependencies: graceful-fs: 4.2.10 - dev: true - /@pnpm/npm-conf@2.2.2: - resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} - engines: {node: '>=12'} + '@pnpm/npm-conf@2.2.2': dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - dev: true - /@prisma/instrumentation@5.16.1: - resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} + '@prisma/instrumentation@5.16.1': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.8.0) transitivePeerDependencies: - supports-color - dev: false - /@rollup/rollup-android-arm-eabi@4.18.1: - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true - /@rollup/rollup-android-arm64@4.18.1: - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-arm64@4.18.1: - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-x64@4.18.1: - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-x64@4.18.1': optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.18.1: - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm-musleabihf@4.18.1: - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.18.1': optional: true - - /@rollup/rollup-linux-arm64-gnu@4.18.1: - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + + '@rollup/rollup-linux-arm64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-arm64-musl@4.18.1: - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-musl@4.18.1': optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': optional: true - /@rollup/rollup-linux-riscv64-gnu@4.18.1: - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-riscv64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-s390x-gnu@4.18.1: - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-s390x-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-gnu@4.18.1: - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-musl@4.18.1: - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-musl@4.18.1': optional: true - /@rollup/rollup-win32-arm64-msvc@4.18.1: - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-arm64-msvc@4.18.1': optional: true - /@rollup/rollup-win32-ia32-msvc@4.18.1: - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-ia32-msvc@4.18.1': optional: true - /@rollup/rollup-win32-x64-msvc@4.18.1: - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-x64-msvc@4.18.1': optional: true - /@sentry/core@8.17.0: - resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} - engines: {node: '>=14.18'} + '@sentry/core@8.17.0': dependencies: '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 - dev: false - /@sentry/node@8.17.0: - resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} - engines: {node: '>=14.18'} + '@sentry/node@8.17.0': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) @@ -1924,508 +4500,298 @@ packages: '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1): - resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} - engines: {node: '>=14.18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/core': ^1.25.1 - '@opentelemetry/instrumentation': ^0.52.1 - '@opentelemetry/sdk-trace-base': ^1.25.1 - '@opentelemetry/semantic-conventions': ^1.25.1 + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) - '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 '@sentry/core': 8.17.0 '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 - dev: false - /@sentry/types@8.17.0: - resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} - engines: {node: '>=14.18'} + '@sentry/types@8.17.0': {} - /@sentry/utils@8.17.0: - resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} - engines: {node: '>=14.18'} + '@sentry/utils@8.17.0': dependencies: '@sentry/types': 8.17.0 - dev: false - /@sigstore/bundle@1.1.0: - resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/bundle@1.1.0': dependencies: '@sigstore/protobuf-specs': 0.2.1 - dev: true - /@sigstore/protobuf-specs@0.2.1: - resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@sigstore/protobuf-specs@0.2.1': {} - /@sigstore/sign@1.0.0: - resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/sign@1.0.0': dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /@sigstore/tuf@1.0.3: - resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/tuf@1.0.3': dependencies: '@sigstore/protobuf-specs': 0.2.1 tuf-js: 1.1.7 transitivePeerDependencies: - supports-color - dev: true - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true + '@sinclair/typebox@0.27.8': {} - /@sindresorhus/is@5.6.0: - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} - dev: true + '@sindresorhus/is@5.6.0': {} - /@syncpoint/wkx@0.5.2: - resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} + '@syncpoint/wkx@0.5.2': dependencies: '@types/node': 15.14.9 - dev: false - /@szmarczak/http-timer@5.0.1: - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} + '@szmarczak/http-timer@5.0.1': dependencies: defer-to-connect: 2.0.1 - dev: true - /@tootallnate/once@2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - dev: true + '@tootallnate/once@2.0.0': {} - /@tsconfig/node10@1.0.11: - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - dev: true + '@tsconfig/node10@1.0.11': {} - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true + '@tsconfig/node12@1.0.11': {} - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true + '@tsconfig/node14@1.0.3': {} - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true + '@tsconfig/node16@1.0.4': {} - /@tufjs/canonical-json@1.0.0: - resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@tufjs/canonical-json@1.0.0': {} - /@tufjs/models@1.0.4: - resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@tufjs/models@1.0.4': dependencies: '@tufjs/canonical-json': 1.0.0 minimatch: 9.0.5 - dev: true - /@types/async@3.2.24: - resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - dev: true + '@types/async@3.2.24': {} - /@types/chai-subset@1.3.5: - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + '@types/chai-subset@1.3.5': dependencies: '@types/chai': 4.3.16 - dev: true - /@types/chai@4.3.16: - resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} - dev: true + '@types/chai@4.3.16': {} - /@types/connect@3.4.36: - resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + '@types/connect@3.4.36': dependencies: '@types/node': 18.11.11 - dev: false - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true + '@types/estree@1.0.5': {} - /@types/http-cache-semantics@4.0.4: - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - dev: true + '@types/http-cache-semantics@4.0.4': {} - /@types/lodash@4.17.6: - resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} - dev: true + '@types/lodash@4.17.6': {} - /@types/mysql@2.15.22: - resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + '@types/mysql@2.15.22': dependencies: '@types/node': 18.11.11 - dev: false - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: true + '@types/node@12.20.55': {} - /@types/node@13.13.52: - resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} - dev: false + '@types/node@13.13.52': {} - /@types/node@15.14.9: - resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - dev: false + '@types/node@15.14.9': {} - /@types/node@18.11.11: - resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} + '@types/node@18.11.11': {} - /@types/pg-pool@2.0.4: - resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + '@types/pg-pool@2.0.4': dependencies: '@types/pg': 8.6.1 - dev: false - /@types/pg@8.6.1: - resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + '@types/pg@8.6.1': dependencies: '@types/node': 18.11.11 pg-protocol: 1.6.1 pg-types: 2.2.0 - dev: false - /@types/semver-utils@1.1.3: - resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} - dev: true + '@types/semver-utils@1.1.3': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true + '@types/semver@7.5.8': {} - /@types/shimmer@1.2.0: - resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} - dev: false + '@types/shimmer@1.2.0': {} - /@types/strip-bom@3.0.0: - resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - dev: true + '@types/strip-bom@3.0.0': {} - /@types/strip-json-comments@0.0.30: - resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - dev: true + '@types/strip-json-comments@0.0.30': {} - /@types/triple-beam@1.3.5: - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - dev: false + '@types/triple-beam@1.3.5': {} - /@types/uuid@9.0.8: - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - dev: true + '@types/uuid@9.0.8': {} - /@types/webidl-conversions@7.0.3: - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - dev: false + '@types/webidl-conversions@7.0.3': {} - /@types/whatwg-url@11.0.5: - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/whatwg-url@11.0.5': dependencies: '@types/webidl-conversions': 7.0.3 - dev: false - /@types/ws@8.2.3: - resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + '@types/ws@8.2.3': dependencies: '@types/node': 18.11.11 - dev: true - /@vitest/expect@0.34.6: - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + '@vitest/expect@0.34.6': dependencies: '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 chai: 4.4.1 - dev: true - /@vitest/runner@0.34.6: - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/runner@0.34.6': dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.2 - dev: true - /@vitest/snapshot@0.34.6: - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/snapshot@0.34.6': dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 - dev: true - /@vitest/spy@0.34.6: - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/spy@0.34.6': dependencies: tinyspy: 2.2.1 - dev: true - /@vitest/utils@0.34.6: - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/utils@0.34.6': dependencies: diff-sequences: 29.6.3 loupe: 2.3.7 pretty-format: 29.7.0 - dev: true - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true + abbrev@1.1.1: {} - /abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 - /abstract-logging@2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + abstract-logging@2.0.1: {} - /acorn-import-assertions@1.9.0(acorn@8.12.1): - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} - requiresBuild: true - peerDependencies: - acorn: ^8 + acorn-import-assertions@1.9.0(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: false optional: true - /acorn-import-attributes@1.9.5(acorn@8.12.1): - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: false - /acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} - engines: {node: '>=0.4.0'} + acorn-walk@8.3.3: dependencies: acorn: 8.12.1 - dev: true - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true + acorn@8.12.1: {} - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} + agent-base@6.0.2: dependencies: debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} + agentkeepalive@4.5.0: dependencies: humanize-ms: 1.2.1 - dev: true - /aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - dev: true - /ajv-formats@2.1.1(ajv@8.16.0): - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - dependencies: + ajv-formats@2.1.1(ajv@8.16.0): + optionalDependencies: ajv: 8.16.0 - /ajv-formats@3.0.1(ajv@8.16.0): - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - dependencies: + ajv-formats@3.0.1(ajv@8.16.0): + optionalDependencies: ajv: 8.16.0 - /ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + ajv@8.16.0: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - /ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + ansi-align@3.0.1: dependencies: string-width: 4.2.3 - dev: true - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: true + ansi-colors@4.1.3: {} - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - dev: true - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true + ansi-styles@5.2.0: {} - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true + ansi-styles@6.2.1: {} - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true - /aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: true + aproba@2.0.0: {} - /are-we-there-yet@3.0.1: - resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + are-we-there-yet@3.0.1: dependencies: delegates: 1.0.0 readable-stream: 3.6.2 - dev: true - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true + arg@4.1.3: {} - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true + argparse@2.0.1: {} - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true + array-union@2.1.0: {} - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true + assertion-error@1.1.0: {} - /async-mutex@0.5.0: - resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async-mutex@0.5.0: dependencies: tslib: 2.6.3 - dev: false - /async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.5: {} - /atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} + atomic-sleep@1.0.0: {} - /avvio@8.3.2: - resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + avvio@8.3.2: dependencies: '@fastify/error': 3.4.1 fastq: 1.17.1 - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + balanced-match@1.0.2: {} - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64-js@1.5.1: {} - /better-ajv-errors@1.2.0(ajv@8.16.0): - resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - ajv: 4.11.8 - 8 + better-ajv-errors@1.2.0(ajv@8.16.0): dependencies: '@babel/code-frame': 7.24.7 '@humanwhocodes/momoa': 2.0.4 @@ -2433,31 +4799,20 @@ packages: chalk: 4.1.2 jsonpointer: 5.0.1 leven: 3.1.0 - dev: false - /better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 - dev: true - /binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - dev: true + binary-extensions@2.3.0: {} - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /boxen@7.1.1: - resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} - engines: {node: '>=14.16'} + boxen@7.1.1: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 @@ -2467,57 +4822,37 @@ packages: type-fest: 2.19.0 widest-line: 4.0.1 wrap-ansi: 8.1.0 - dev: true - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - dev: true - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - dev: true - /bson@6.8.0: - resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} - engines: {node: '>=16.20.1'} + bson@6.8.0: {} - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true + buffer-from@1.1.2: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - dev: true + cac@6.7.14: {} - /cacache@16.1.3: - resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + cacache@16.1.3: dependencies: '@npmcli/fs': 2.1.2 '@npmcli/move-file': 2.0.1 @@ -2539,11 +4874,8 @@ packages: unique-filename: 2.0.1 transitivePeerDependencies: - bluebird - dev: true - /cacache@17.1.4: - resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + cacache@17.1.4: dependencies: '@npmcli/fs': 3.1.1 fs-minipass: 3.0.3 @@ -2557,16 +4889,10 @@ packages: ssri: 10.0.6 tar: 6.2.1 unique-filename: 3.0.0 - dev: true - /cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - dev: true + cacheable-lookup@7.0.0: {} - /cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} + cacheable-request@10.2.14: dependencies: '@types/http-cache-semantics': 4.0.4 get-stream: 6.0.1 @@ -2575,16 +4901,10 @@ packages: mimic-response: 4.0.0 normalize-url: 8.0.1 responselike: 3.0.0 - dev: true - /camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - dev: true + camelcase@7.0.1: {} - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} + chai@4.4.1: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -2593,41 +4913,27 @@ packages: loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 - dev: true - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: true + chalk@5.3.0: {} - /chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: true + chardet@0.7.0: {} - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 - dev: true - /chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -2638,145 +4944,83 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - dev: true + chownr@2.0.0: {} - /ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - dev: true + ci-info@3.9.0: {} - /cjs-module-lexer@1.3.1: - resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} - dev: false + cjs-module-lexer@1.3.1: {} - /clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: true + clean-stack@2.2.0: {} - /cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - dev: true + cli-boxes@3.0.0: {} - /cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 - dev: true - /cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - dev: true + cli-spinners@2.9.2: {} - /cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} + cli-table3@0.6.5: dependencies: string-width: 4.2.3 optionalDependencies: '@colors/colors': 1.5.0 - dev: true - /cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - dev: true + cli-width@4.1.0: {} - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@7.0.4: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - dev: true + clone@1.0.4: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false - /color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - dev: true + color-support@1.1.3: {} - /color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@3.2.1: dependencies: color-convert: 1.9.3 color-string: 1.9.1 - dev: false - /colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + colorspace@1.1.4: dependencies: color: 3.2.1 text-hex: 1.0.0 - dev: false - - /commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - dev: true - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - dev: false + commander@10.0.1: {} - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false + commander@12.1.0: {} - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + commander@2.20.3: {} - /concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} - hasBin: true + concat-map@0.0.1: {} + + concurrently@8.2.2: dependencies: chalk: 4.1.2 date-fns: 2.30.0 @@ -2787,41 +5031,27 @@ packages: supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 - dev: true - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true + confbox@0.1.7: {} - /config-chain@1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + config-chain@1.1.13: dependencies: ini: 1.3.8 proto-list: 1.2.4 - dev: true - /configstore@6.0.0: - resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} - engines: {node: '>=12'} + configstore@6.0.0: dependencies: dot-prop: 6.0.1 graceful-fs: 4.2.11 unique-string: 3.0.0 write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 - dev: true - /console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: true + console-control-strings@1.1.0: {} - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} + cookie@0.6.0: {} - /copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true + copyfiles@2.4.1: dependencies: glob: 7.2.3 minimatch: 3.1.2 @@ -2830,204 +5060,111 @@ packages: through2: 2.0.5 untildify: 4.0.0 yargs: 16.2.0 - dev: true - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true + core-util-is@1.0.3: {} - /cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} + cors@2.8.5: dependencies: object-assign: 4.1.1 vary: 1.1.2 - dev: false - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true + create-require@1.1.1: {} - /cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + cross-spawn@5.1.0: dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 - dev: true - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true - /crypto-random-string@4.0.0: - resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} - engines: {node: '>=12'} + crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 - dev: true - /data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - dev: false + data-uri-to-buffer@4.0.1: {} - /date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} + date-fns@2.30.0: dependencies: '@babel/runtime': 7.24.8 - dev: true - /date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - dev: false + date-fns@3.6.0: {} - /debug@4.3.5(supports-color@5.5.0): - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.5(supports-color@5.5.0): dependencies: ms: 2.1.2 + optionalDependencies: supports-color: 5.5.0 - /decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 - dev: true - /deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} + deep-eql@4.1.4: dependencies: type-detect: 4.0.8 - dev: true - /deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - dev: true + deep-extend@0.6.0: {} - /defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defaults@1.0.4: dependencies: clone: 1.0.4 - dev: true - /defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - dev: true + defer-to-connect@2.0.1: {} - /delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: true + delegates@1.0.0: {} - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true + detect-indent@6.1.0: {} - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true + diff-sequences@29.6.3: {} - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true + diff@4.0.2: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dev: true - /discontinuous-range@1.0.0: - resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} - dev: false + discontinuous-range@1.0.0: {} - /dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} + dot-prop@6.0.1: dependencies: is-obj: 2.0.0 - dev: true - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - dev: false + dotenv@16.4.5: {} - /duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - dev: true + duplexer@0.1.2: {} - /dynamic-dedupe@0.3.0: - resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + dynamic-dedupe@0.3.0: dependencies: xtend: 4.0.2 - dev: true - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true + emoji-regex@9.2.2: {} - /enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: false + enabled@2.0.0: {} - /encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true + encoding@0.1.13: dependencies: iconv-lite: 0.6.3 - dev: true optional: true - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - dev: true - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: true + env-paths@2.2.1: {} - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: true + err-code@2.0.3: {} - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 '@esbuild/android-arm': 0.21.5 @@ -3052,30 +5189,16 @@ packages: '@esbuild/win32-arm64': 0.21.5 '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - dev: true - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - dev: true + escalade@3.1.2: {} - /escape-goat@4.0.0: - resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} - engines: {node: '>=12'} - dev: true + escape-goat@4.0.0: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true + esprima@4.0.1: {} - /event-stream@3.3.4: - resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + event-stream@3.3.4: dependencies: duplexer: 0.1.2 from: 0.1.7 @@ -3084,55 +5207,36 @@ packages: split: 0.3.3 stream-combiner: 0.0.4 through: 2.3.8 - dev: true - /event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} + event-target-shim@5.0.1: {} - /events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} + events@3.3.0: {} - /exponential-backoff@3.1.1: - resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} - dev: true + exponential-backoff@3.1.1: {} - /extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - dev: true + extendable-error@0.1.7: {} - /external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} + external-editor@3.1.0: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 - dev: true - /fast-content-type-parse@1.1.0: - resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + fast-content-type-parse@1.1.0: {} - /fast-decode-uri-component@1.0.1: - resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-decode-uri-component@1.0.1: {} - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.7 - dev: true - /fast-json-stringify@5.16.1: - resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} + fast-json-stringify@5.16.1: dependencies: '@fastify/merge-json-schemas': 0.1.1 ajv: 8.16.0 @@ -3142,27 +5246,19 @@ packages: json-schema-ref-resolver: 1.0.1 rfdc: 1.4.1 - /fast-memoize@2.5.2: - resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} - dev: true + fast-memoize@2.5.2: {} - /fast-querystring@1.1.2: - resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fast-querystring@1.1.2: dependencies: fast-decode-uri-component: 1.0.1 - /fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} + fast-redact@3.5.0: {} - /fast-uri@2.4.0: - resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} + fast-uri@2.4.0: {} - /fastify-plugin@4.5.1: - resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + fastify-plugin@4.5.1: {} - /fastify@4.23.2: - resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} + fastify@4.23.2: dependencies: '@fastify/ajv-compiler': 3.6.0 '@fastify/error': 3.4.1 @@ -3181,149 +5277,89 @@ packages: semver: 7.6.2 toad-cache: 3.7.0 - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - /fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - dev: false + fecha@4.2.3: {} - /fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 - dev: false - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /find-my-way@7.7.0: - resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} - engines: {node: '>=14'} + find-my-way@7.7.0: dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 safe-regex2: 2.0.0 - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /find-yarn-workspace-root2@1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + find-yarn-workspace-root2@1.2.16: dependencies: micromatch: 4.0.7 pkg-dir: 4.2.0 - dev: true - /fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: false + fn.name@1.1.0: {} - /foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - dev: true + form-data-encoder@2.1.4: {} - /formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 - dev: false - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} + forwarded@0.2.0: {} - /fp-and-or@0.1.4: - resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} - engines: {node: '>=10'} - dev: true + fp-and-or@0.1.4: {} - /from@0.1.7: - resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} - dev: true + from@0.1.7: {} - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 - dev: true - /fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fs-minipass@3.0.3: dependencies: minipass: 7.1.2 - dev: true - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /gauge@4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + gauge@4.0.4: dependencies: aproba: 2.0.0 color-support: 1.1.3 @@ -3333,37 +5369,20 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wide-align: 1.1.5 - dev: true - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + get-caller-file@2.0.5: {} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true + get-func-name@2.0.2: {} - /get-stdin@8.0.0: - resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} - engines: {node: '>=10'} - dev: true + get-stdin@8.0.0: {} - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: true + get-stream@6.0.1: {} - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true + glob@10.4.5: dependencies: foreground-child: 3.2.1 jackspeak: 3.4.3 @@ -3371,11 +5390,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -3383,30 +5399,20 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true - /glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + glob@8.1.0: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 minimatch: 5.1.6 once: 1.4.0 - dev: true - /global-dirs@3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} + global-dirs@3.0.1: dependencies: ini: 2.0.0 - dev: true - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -3414,15 +5420,10 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - dev: true - /globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - dev: true + globrex@0.1.2: {} - /got@12.6.1: - resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} - engines: {node: '>=14.16'} + got@12.6.1: dependencies: '@sindresorhus/is': 5.6.0 '@szmarczak/http-timer': 5.0.1 @@ -3435,198 +5436,115 @@ packages: lowercase-keys: 3.0.0 p-cancelable: 3.0.0 responselike: 3.0.0 - dev: true - /graceful-fs@4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: true + graceful-fs@4.2.10: {} - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true + graceful-fs@4.2.11: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: true + has-unicode@2.0.1: {} - /has-yarn@3.0.0: - resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + has-yarn@3.0.0: {} - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /hosted-git-info@5.2.1: - resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hosted-git-info@5.2.1: dependencies: lru-cache: 7.18.3 - dev: true - /hosted-git-info@6.1.1: - resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hosted-git-info@6.1.1: dependencies: lru-cache: 7.18.3 - dev: true - /http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: true + http-cache-semantics@4.1.1: {} - /http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} + http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - dev: true - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - dev: true + human-id@1.0.2: {} - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + humanize-ms@1.2.1: dependencies: ms: 2.1.3 - dev: true - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: true - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - requiresBuild: true + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - dev: true optional: true - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ieee754@1.2.1: {} - /ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - dev: true + ignore-by-default@1.0.1: {} - /ignore-walk@6.0.5: - resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ignore-walk@6.0.5: dependencies: minimatch: 9.0.5 - dev: true - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: true + ignore@5.3.1: {} - /import-in-the-middle@1.7.1: - resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} - requiresBuild: true + import-in-the-middle@1.7.1: dependencies: acorn: 8.12.1 acorn-import-assertions: 1.9.0(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 - dev: false optional: true - /import-in-the-middle@1.9.0: - resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} + import-in-the-middle@1.9.0: dependencies: acorn: 8.12.1 acorn-import-attributes: 1.9.5(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 - dev: false - /import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - dev: true + import-lazy@4.0.0: {} - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true + imurmurhash@0.1.4: {} - /indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - dev: true + indent-string@4.0.0: {} - /infer-owner@1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - dev: true + infer-owner@1.0.4: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true + ini@1.3.8: {} - /ini@2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - dev: true + ini@2.0.0: {} - /ini@4.1.3: - resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + ini@4.1.3: {} - /inquirer@9.3.5: - resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} - engines: {node: '>=18'} + inquirer@9.3.5: dependencies: '@inquirer/figures': 1.0.3 ansi-escapes: 4.3.2 @@ -3640,326 +5558,178 @@ packages: strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 - dev: true - /ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} + ip-address@9.0.5: dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 - dev: true - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} + ipaddr.js@1.9.1: {} - /ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} - engines: {node: '>= 10'} - dev: false + ipaddr.js@2.2.0: {} - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + is-arrayish@0.3.2: {} - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - dev: true - /is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true + is-ci@3.0.1: dependencies: ci-info: 3.9.0 - dev: true - /is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} - engines: {node: '>= 0.4'} + is-core-module@2.14.0: dependencies: hasown: 2.0.2 - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true + is-fullwidth-code-point@3.0.0: {} - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - /is-installed-globally@0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} + is-installed-globally@0.4.0: dependencies: global-dirs: 3.0.1 is-path-inside: 3.0.3 - dev: true - /is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - dev: true - - /is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: true + is-interactive@1.0.0: {} - /is-npm@6.0.0: - resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-lambda@1.0.1: {} - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + is-npm@6.0.0: {} - /is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - dev: true + is-number@7.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true + is-obj@2.0.0: {} - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false + is-path-inside@3.0.3: {} - /is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} + is-stream@2.0.1: {} + + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 - dev: true - /is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: true + is-typedarray@1.0.0: {} - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true + is-unicode-supported@0.1.0: {} - /is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - dev: true + is-windows@1.0.2: {} - /is-yarn-global@0.4.1: - resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} - engines: {node: '>=12'} - dev: true + is-yarn-global@0.4.1: {} - /isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - dev: true + isarray@0.0.1: {} - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true + isarray@1.0.0: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + isexe@2.0.0: {} - /ix@5.0.0: - resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} + ix@5.0.0: dependencies: '@types/node': 13.13.52 tslib: 2.6.3 - dev: false - /jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - dev: true + jju@1.4.0: {} - /jose@4.15.9: - resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - dev: false + jose@4.15.9: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false + js-tokens@4.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - dev: true - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - dev: true + jsbn@1.1.0: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + json-buffer@3.0.1: {} - /json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + json-parse-even-better-errors@3.0.2: {} - /json-parse-helpfulerror@1.0.3: - resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} + json-parse-helpfulerror@1.0.3: dependencies: jju: 1.4.0 - dev: true - /json-schema-ref-resolver@1.0.1: - resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + json-schema-ref-resolver@1.0.1: dependencies: fast-deep-equal: 3.1.3 - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true + json5@2.2.3: {} - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - dev: true - /jsonlines@0.1.1: - resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} - dev: true + jsonlines@0.1.1: {} - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: true + jsonparse@1.3.1: {} - /jsonpointer@5.0.1: - resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} - engines: {node: '>=0.10.0'} - dev: false + jsonpointer@5.0.1: {} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - dev: true - /kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - dev: true + kleur@4.1.5: {} - /kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: false + kuler@2.0.0: {} - /latest-version@7.0.0: - resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} - engines: {node: '>=14.16'} + latest-version@7.0.0: dependencies: package-json: 8.1.1 - dev: true - /leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - dev: false + leven@3.1.0: {} - /light-my-request@5.13.0: - resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + light-my-request@5.13.0: dependencies: cookie: 0.6.0 process-warning: 3.0.0 set-cookie-parser: 2.6.0 - /load-yaml-file@0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} + load-yaml-file@0.2.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - dev: true + local-pkg@0.4.3: {} - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: false + lodash.merge@4.6.2: {} - /lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - dev: true + lodash.startcase@4.4.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.21: {} - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - /logform@2.6.1: - resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} - engines: {node: '>= 12.0.0'} + logform@2.6.1: dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -3967,51 +5737,31 @@ packages: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 - dev: false - /lossless-json@2.0.11: - resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} - dev: false + lossless-json@2.0.11: {} - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 - dev: true - /lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + lowercase-keys@3.0.0: {} - /lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@10.4.3: {} - /lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: true - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - dev: true + lru-cache@7.18.3: {} - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true + make-error@1.3.6: {} - /make-fetch-happen@10.2.1: - resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + make-fetch-happen@10.2.1: dependencies: agentkeepalive: 4.5.0 cacache: 16.1.3 @@ -4032,11 +5782,8 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /make-fetch-happen@11.1.1: - resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + make-fetch-happen@11.1.1: dependencies: agentkeepalive: 4.5.0 cacache: 17.1.4 @@ -4055,277 +5802,148 @@ packages: ssri: 10.0.6 transitivePeerDependencies: - supports-color - dev: true - /map-stream@0.1.0: - resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} - dev: true + map-stream@0.1.0: {} - /memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - dev: false + memory-pager@1.5.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true + merge2@1.4.1: {} - /micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} + micromatch@4.0.7: dependencies: braces: 3.0.3 picomatch: 2.3.1 - dev: true - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true + mimic-fn@2.1.0: {} - /mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - dev: true + mimic-response@3.1.0: {} - /mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + mimic-response@4.0.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - dev: true - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true + minimist@1.2.8: {} - /minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} + minipass-collect@1.0.2: dependencies: minipass: 3.3.6 - dev: true - /minipass-fetch@2.1.2: - resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + minipass-fetch@2.1.2: dependencies: minipass: 3.3.6 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 - dev: true - /minipass-fetch@3.0.5: - resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + minipass-fetch@3.0.5: dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 - dev: true - /minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} + minipass-flush@1.0.5: dependencies: minipass: 3.3.6 - dev: true - /minipass-json-stream@1.0.1: - resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} + minipass-json-stream@1.0.1: dependencies: jsonparse: 1.3.1 minipass: 3.3.6 - dev: true - /minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} + minipass-pipeline@1.2.4: dependencies: minipass: 3.3.6 - dev: true - /minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} + minipass-sized@1.0.3: dependencies: minipass: 3.3.6 - dev: true - /minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} + minipass@3.3.6: dependencies: yallist: 4.0.0 - dev: true - /minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - dev: true + minipass@5.0.0: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + minizlib@2.1.2: dependencies: minipass: 3.3.6 yallist: 4.0.0 - dev: true - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@1.0.4: {} - /mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.1: dependencies: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.3 ufo: 1.5.3 - dev: true - /mnemonist@0.39.5: - resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} + mnemonist@0.39.5: dependencies: obliterator: 2.0.4 - dev: false - /module-details-from-path@1.0.3: - resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} - dev: false + module-details-from-path@1.0.3: {} - /mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + mongodb-connection-string-url@3.0.1: dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 - dev: false - /mongodb@6.8.0: - resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true + mongodb@6.8.0(socks@2.8.3): dependencies: '@mongodb-js/saslprep': 1.1.7 bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - dev: false + optionalDependencies: + socks: 2.8.3 - /moo@0.5.2: - resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} - dev: false + moo@0.5.2: {} - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true + mri@1.2.0: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.2: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + mute-stream@1.0.0: {} - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + nanoid@3.3.7: {} - /nearley@2.20.1: - resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} - hasBin: true + nearley@2.20.1: dependencies: commander: 2.20.3 moo: 0.5.2 railroad-diagrams: 1.0.0 randexp: 0.4.6 - dev: false - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: true + negotiator@0.6.3: {} - /node-cleanup@2.1.2: - resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} - dev: true + node-cleanup@2.1.2: {} - /node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - dev: false + node-domexception@1.0.0: {} - /node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - dev: false - /node-gyp@9.4.1: - resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} - engines: {node: ^12.13 || ^14.13 || >=16} - hasBin: true + node-gyp@9.4.1: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.1 @@ -4341,12 +5959,8 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /nodemon@3.1.4: - resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} - engines: {node: '>=10'} - hasBin: true + nodemon@3.1.4: dependencies: chokidar: 3.6.0 debug: 4.3.5(supports-color@5.5.0) @@ -4358,54 +5972,32 @@ packages: supports-color: 5.5.0 touch: 3.1.1 undefsafe: 2.0.5 - dev: true - /noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + noms@0.0.0: dependencies: inherits: 2.0.4 readable-stream: 1.0.34 - dev: true - /nopt@6.0.0: - resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true + nopt@6.0.0: dependencies: abbrev: 1.1.1 - dev: true - /normalize-package-data@5.0.0: - resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + normalize-package-data@5.0.0: dependencies: hosted-git-info: 6.1.1 is-core-module: 2.14.0 semver: 7.6.2 validate-npm-package-license: 3.0.4 - dev: true - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} - engines: {node: '>=14.16'} - dev: true + normalize-url@8.0.1: {} - /npm-bundled@3.0.1: - resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-bundled@3.0.1: dependencies: npm-normalize-package-bin: 3.0.1 - dev: true - /npm-check-updates@16.14.20: - resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} - engines: {node: '>=14.14'} - hasBin: true + npm-check-updates@16.14.20: dependencies: '@types/semver-utils': 1.1.3 chalk: 5.3.0 @@ -4443,50 +6035,32 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /npm-install-checks@6.3.0: - resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-install-checks@6.3.0: dependencies: semver: 7.6.2 - dev: true - /npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + npm-normalize-package-bin@3.0.1: {} - /npm-package-arg@10.1.0: - resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-package-arg@10.1.0: dependencies: hosted-git-info: 6.1.1 proc-log: 3.0.0 semver: 7.6.2 validate-npm-package-name: 5.0.1 - dev: true - /npm-packlist@7.0.4: - resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-packlist@7.0.4: dependencies: ignore-walk: 6.0.5 - dev: true - /npm-pick-manifest@8.0.2: - resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-pick-manifest@8.0.2: dependencies: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 10.1.0 semver: 7.6.2 - dev: true - /npm-registry-fetch@14.0.5: - resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-registry-fetch@14.0.5: dependencies: make-fetch-happen: 11.1.1 minipass: 5.0.0 @@ -4497,69 +6071,42 @@ packages: proc-log: 3.0.0 transitivePeerDependencies: - supports-color - dev: true - /npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + npmlog@6.0.2: dependencies: are-we-there-yet: 3.0.1 console-control-strings: 1.1.0 gauge: 4.0.4 set-blocking: 2.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - dev: false + object-assign@4.1.1: {} - /obliterator@2.0.4: - resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} - dev: false + obliterator@2.0.4: {} - /on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} + on-exit-leak-free@2.1.2: {} - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - dev: true - /one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + one-time@1.0.0: dependencies: fn.name: 1.1.0 - dev: false - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - dev: true - /opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} - engines: {node: '>18.0.0'} - requiresBuild: true - peerDependencies: - '@opentelemetry/api': ^1.6.0 + opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false optional: true - /ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} + ora@5.4.1: dependencies: bl: 4.1.0 chalk: 4.1.2 @@ -4570,99 +6117,55 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - dev: true - /os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: true + os-tmpdir@1.0.2: {} - /outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - dev: true + outdent@0.5.0: {} - /p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - dev: true + p-cancelable@3.0.0: {} - /p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} + p-filter@2.1.0: dependencies: p-map: 2.1.0 - dev: true - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + p-limit@2.3.0: dependencies: p-try: 2.2.0 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-limit@4.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + p-locate@4.1.0: dependencies: p-limit: 2.3.0 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true + p-map@2.1.0: {} - /p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} + p-map@4.0.0: dependencies: aggregate-error: 3.1.0 - dev: true - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true + p-try@2.2.0: {} - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /package-json@8.1.1: - resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} - engines: {node: '>=14.16'} + package-json@8.1.1: dependencies: got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 semver: 7.6.2 - dev: true - /pacote@15.2.0: - resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + pacote@15.2.0: dependencies: '@npmcli/git': 4.1.0 '@npmcli/installed-package-contents': 2.1.0 @@ -4685,111 +6188,65 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - - /parse-github-url@1.0.3: - resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} - engines: {node: '>= 0.10'} - hasBin: true - dev: true - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + parse-github-url@1.0.3: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-exists@4.0.0: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true + path-is-absolute@1.0.1: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-key@3.1.1: {} - /path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-parse@1.0.7: {} + + path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 minipass: 7.1.2 - dev: true - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + path-type@4.0.0: {} - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true + pathe@1.1.2: {} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true + pathval@1.1.1: {} - /pause-stream@0.0.11: - resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pause-stream@0.0.11: dependencies: through: 2.3.8 - dev: true - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - dev: false + pg-int8@1.0.1: {} - /pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} - dev: false + pg-protocol@1.6.1: {} - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 postgres-bytea: 1.0.0 postgres-date: 1.0.7 postgres-interval: 1.2.0 - dev: false - /pgsql-ast-parser@11.2.0: - resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} + pgsql-ast-parser@11.2.0: dependencies: moo: 0.5.2 nearley: 2.20.1 - dev: false - /picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + picocolors@1.0.1: {} - /pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - dev: true + picomatch@2.3.1: {} - /pino-abstract-transport@1.2.0: - resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + pify@4.0.1: {} + + pino-abstract-transport@1.2.0: dependencies: readable-stream: 4.5.2 split2: 4.2.0 - /pino-std-serializers@6.2.2: - resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino-std-serializers@6.2.2: {} - /pino@8.21.0: - resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} - hasBin: true + pino@8.21.0: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 @@ -4803,190 +6260,106 @@ packages: sonic-boom: 3.8.1 thread-stream: 2.7.0 - /pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - dev: true - /pkg-types@1.1.3: - resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pkg-types@1.1.3: dependencies: confbox: 0.1.7 mlly: 1.7.1 pathe: 1.1.2 - dev: true - /postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - dev: true - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - dev: false + postgres-array@2.0.0: {} - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - dev: false + postgres-bytea@1.0.0: {} - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - dev: false + postgres-date@1.0.7: {} - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} + postgres-interval@1.2.0: dependencies: xtend: 4.0.2 - dev: false - /preferred-pm@3.1.4: - resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} - engines: {node: '>=10'} + preferred-pm@3.1.4: dependencies: find-up: 5.0.0 find-yarn-workspace-root2: 1.2.16 path-exists: 4.0.0 which-pm: 2.2.0 - dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true + prettier@2.8.8: {} - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - dev: true - /proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + proc-log@3.0.0: {} - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true + process-nextick-args@2.0.1: {} - /process-warning@2.3.2: - resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + process-warning@2.3.2: {} - /process-warning@3.0.0: - resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + process-warning@3.0.0: {} - /process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} + process@0.11.10: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true + progress@2.0.3: {} - /promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - dev: true + promise-inflight@1.0.1: {} - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} + promise-retry@2.0.1: dependencies: err-code: 2.0.3 retry: 0.12.0 - dev: true - /prompts-ncu@3.0.0: - resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} - engines: {node: '>= 14'} + prompts-ncu@3.0.0: dependencies: kleur: 4.1.5 sisteransi: 1.0.5 - dev: true - /proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - dev: true + proto-list@1.2.4: {} - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - /ps-tree@1.2.0: - resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} - engines: {node: '>= 0.10'} - hasBin: true + ps-tree@1.2.0: dependencies: event-stream: 3.3.4 - dev: true - /pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: true + pseudomap@1.0.2: {} - /pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - dev: true + pstree.remy@1.1.8: {} - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /pupa@3.1.0: - resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} - engines: {node: '>=12.20'} + pupa@3.1.0: dependencies: escape-goat: 4.0.0 - dev: true - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + queue-microtask@1.2.3: {} - /quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quick-format-unescaped@4.0.4: {} - /quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - dev: true + quick-lru@5.1.1: {} - /railroad-diagrams@1.0.0: - resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} - dev: false + railroad-diagrams@1.0.0: {} - /randexp@0.4.6: - resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} - engines: {node: '>=0.12'} + randexp@0.4.6: dependencies: discontinuous-range: 1.0.0 ret: 0.1.15 - dev: false - /rc-config-loader@4.1.3: - resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} + rc-config-loader@4.1.3: dependencies: debug: 4.3.5(supports-color@5.5.0) js-yaml: 4.1.0 @@ -4994,62 +6367,43 @@ packages: require-from-string: 2.0.2 transitivePeerDependencies: - supports-color - dev: true - /rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true + rc@1.2.8: dependencies: deep-extend: 0.6.0 ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - dev: true - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true + react-is@18.3.1: {} - /read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 npm-normalize-package-bin: 3.0.1 - dev: true - /read-package-json@6.0.4: - resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + read-package-json@6.0.4: dependencies: glob: 10.4.5 json-parse-even-better-errors: 3.0.2 normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 - dev: true - /read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + readable-stream@1.0.34: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 isarray: 0.0.1 string_decoder: 0.10.31 - dev: true - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -5058,19 +6412,14 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readable-stream@4.5.2: dependencies: abort-controller: 3.0.0 buffer: 6.0.3 @@ -5078,141 +6427,78 @@ packages: process: 0.11.10 string_decoder: 1.3.0 - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - dev: true - /real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} + real-require@0.2.0: {} - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: true + regenerator-runtime@0.14.1: {} - /registry-auth-token@5.0.2: - resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} - engines: {node: '>=14'} + registry-auth-token@5.0.2: dependencies: '@pnpm/npm-conf': 2.2.2 - dev: true - /registry-url@6.0.1: - resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} - engines: {node: '>=12'} + registry-url@6.0.1: dependencies: rc: 1.2.8 - dev: true - /remote-git-tags@3.0.0: - resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} - engines: {node: '>=8'} - dev: true + remote-git-tags@3.0.0: {} - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /require-in-the-middle@7.3.0: - resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} - engines: {node: '>=8.6.0'} + require-in-the-middle@7.3.0: dependencies: debug: 4.3.5(supports-color@5.5.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: false - /resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - dev: true + resolve-alpn@1.2.1: {} - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true + resolve-from@5.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} + responselike@3.0.0: dependencies: lowercase-keys: 3.0.0 - dev: true - /restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: true - /ret@0.1.15: - resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} - engines: {node: '>=0.12'} - dev: false + ret@0.1.15: {} - /ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} + ret@0.2.2: {} - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: true + retry@0.12.0: {} - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + reusify@1.0.4: {} - /rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rfdc@1.4.1: {} - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@2.7.1: dependencies: glob: 7.2.3 - dev: true - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - dev: true - /rimraf@5.0.9: - resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} - engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} - hasBin: true + rimraf@5.0.9: dependencies: glob: 10.4.5 - dev: true - /rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -5233,129 +6519,72 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.18.1 '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 - dev: true - /rsocket-core@1.0.0-alpha.3: - resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} + rsocket-core@1.0.0-alpha.3: {} - /rsocket-websocket-client@1.0.0-alpha.3: - resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} + rsocket-websocket-client@1.0.0-alpha.3: dependencies: rsocket-core: 1.0.0-alpha.3 - dev: true - /run-async@3.0.0: - resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} - engines: {node: '>=0.12.0'} - dev: true + run-async@3.0.0: {} - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - dev: true - /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.1: dependencies: tslib: 2.6.3 - dev: true - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-regex2@2.0.0: - resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + safe-regex2@2.0.0: dependencies: ret: 0.2.2 - /safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} - engines: {node: '>=10'} + safe-stable-stringify@2.4.3: {} - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true + safer-buffer@2.1.2: {} - /secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + secure-json-parse@2.7.0: {} - /semver-diff@4.0.0: - resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} - engines: {node: '>=12'} + semver-diff@4.0.0: dependencies: semver: 7.6.2 - dev: true - /semver-utils@1.1.4: - resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} - dev: true + semver-utils@1.1.4: {} - /semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.2: {} - /set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: true + set-blocking@2.0.0: {} - /set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + set-cookie-parser@2.6.0: {} - /shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 - dev: true - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - dev: true - /shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - dev: true + shebang-regex@1.0.0: {} - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - dev: false + shimmer@1.2.1: {} - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true + siginfo@2.0.0: {} - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: true + signal-exit@4.1.0: {} - /sigstore@1.9.0: - resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + sigstore@1.9.0: dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 @@ -5364,271 +6593,161 @@ packages: make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - dev: false - /simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} + simple-update-notifier@2.0.0: dependencies: semver: 7.6.2 - dev: true - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true + sisteransi@1.0.5: {} - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + slash@3.0.0: {} - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: true + smart-buffer@4.2.0: {} - /socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} + socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color - dev: true - /socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.3: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 - dev: true - /sonic-boom@3.8.1: - resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@3.8.1: dependencies: atomic-sleep: 1.0.0 - /source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - dev: true + source-map-js@1.2.0: {} - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true + source-map@0.6.1: {} - /sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 - dev: false - /spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - dev: true + spawn-command@0.0.2: {} - /spawn-please@2.0.2: - resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} - engines: {node: '>=14'} + spawn-please@2.0.2: dependencies: cross-spawn: 7.0.3 - dev: true - /spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + spawndamnit@2.0.0: dependencies: cross-spawn: 5.1.0 signal-exit: 3.0.7 - dev: true - /spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.18 - dev: true - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - dev: true + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 - dev: true - /spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - dev: true + spdx-license-ids@3.0.18: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + split2@4.2.0: {} - /split@0.3.3: - resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + split@0.3.3: dependencies: through: 2.3.8 - dev: true - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true + sprintf-js@1.0.3: {} - /sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - dev: true + sprintf-js@1.1.3: {} - /ssri@10.0.6: - resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ssri@10.0.6: dependencies: minipass: 7.1.2 - dev: true - /ssri@9.0.1: - resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + ssri@9.0.1: dependencies: minipass: 3.3.6 - dev: true - /stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - dev: false + stack-trace@0.0.10: {} - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true + stackback@0.0.2: {} - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true + std-env@3.7.0: {} - /stream-combiner@0.0.4: - resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-combiner@0.0.4: dependencies: duplexer: 0.1.2 - dev: true - /string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - dev: true + string-argv@0.3.2: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - /string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - dev: true + string_decoder@0.10.31: {} - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - dev: true - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - dev: true - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true + strip-bom@3.0.0: {} - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - dev: true + strip-json-comments@2.0.1: {} - /strip-json-comments@5.0.1: - resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} - engines: {node: '>=14.16'} - dev: true + strip-json-comments@5.0.1: {} - /strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + strip-literal@1.3.0: dependencies: acorn: 8.12.1 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} + tar@6.2.1: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 @@ -5636,101 +6755,51 @@ packages: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - dev: true - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: true + term-size@2.2.1: {} - /text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: false + text-hex@1.0.0: {} - /thread-stream@2.7.0: - resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@2.7.0: dependencies: real-require: 0.2.0 - /through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + through2@2.0.5: dependencies: readable-stream: 2.3.8 xtend: 4.0.2 - dev: true - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true + through@2.3.8: {} - /tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - dev: true + tinybench@2.8.0: {} - /tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} - dev: true + tinypool@0.7.0: {} - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true + tinyspy@2.2.1: {} - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - dev: true - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toad-cache@3.7.0: - resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} - engines: {node: '>=12'} + toad-cache@3.7.0: {} - /touch@3.1.1: - resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} - hasBin: true - dev: true + touch@3.1.1: {} - /tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} + tr46@4.1.1: dependencies: punycode: 2.3.1 - dev: false - /tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - dev: true + tree-kill@1.2.2: {} - /triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - dev: false + triple-beam@1.4.1: {} - /ts-codec@1.2.2: - resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} - dev: false + ts-codec@1.2.2: {} - /ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): - resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} - engines: {node: '>=0.8.0'} - hasBin: true - peerDependencies: - node-notifier: '*' - typescript: '*' - peerDependenciesMeta: - node-notifier: - optional: true + ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): dependencies: chokidar: 3.6.0 dynamic-dedupe: 0.3.0 @@ -5747,21 +6816,8 @@ packages: - '@swc/core' - '@swc/wasm' - '@types/node' - dev: true - /ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true + ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -5778,146 +6834,79 @@ packages: typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true - /tsc-watch@6.2.0(typescript@5.2.2): - resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} - engines: {node: '>=12.12.0'} - hasBin: true - peerDependencies: - typescript: '*' + tsc-watch@6.2.0(typescript@5.2.2): dependencies: cross-spawn: 7.0.3 node-cleanup: 2.1.2 ps-tree: 1.2.0 string-argv: 0.3.2 typescript: 5.2.2 - dev: true - /tsconfck@3.1.1(typescript@5.2.2): - resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - dependencies: + tsconfck@3.1.1(typescript@5.2.2): + optionalDependencies: typescript: 5.2.2 - dev: true - /tsconfig@7.0.0: - resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + tsconfig@7.0.0: dependencies: '@types/strip-bom': 3.0.0 '@types/strip-json-comments': 0.0.30 strip-bom: 3.0.0 strip-json-comments: 2.0.1 - dev: true - /tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.6.3: {} - /tuf-js@1.1.7: - resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 debug: 4.3.5(supports-color@5.5.0) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true + type-detect@4.0.8: {} - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true + type-fest@0.21.3: {} - /type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - dev: true + type-fest@1.4.0: {} - /type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - dev: true + type-fest@2.19.0: {} - /typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + typedarray-to-buffer@3.1.5: dependencies: is-typedarray: 1.0.0 - dev: true - /typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} - engines: {node: '>=14.17'} - hasBin: true - dev: true + typescript@5.2.2: {} - /ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - dev: true + ufo@1.5.3: {} - /undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - dev: true + undefsafe@2.0.5: {} - /unique-filename@2.0.1: - resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-filename@2.0.1: dependencies: unique-slug: 3.0.0 - dev: true - /unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 - dev: true - /unique-slug@3.0.0: - resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-slug@3.0.0: dependencies: imurmurhash: 0.1.4 - dev: true - /unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-slug@4.0.0: dependencies: imurmurhash: 0.1.4 - dev: true - /unique-string@3.0.0: - resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} - engines: {node: '>=12'} + unique-string@3.0.0: dependencies: crypto-random-string: 4.0.0 - dev: true - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true + universalify@0.1.2: {} - /untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - dev: true + untildify@4.0.0: {} - /update-notifier@6.0.2: - resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} - engines: {node: '>=14.16'} + update-notifier@6.0.2: dependencies: boxen: 7.1.1 chalk: 5.3.0 @@ -5933,46 +6922,27 @@ packages: semver: 7.6.2 semver-diff: 4.0.0 xdg-basedir: 5.1.0 - dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - dev: false + uuid@9.0.1: {} - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true + v8-compile-cache-lib@3.0.1: {} - /validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - dev: true - /validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + validate-npm-package-name@5.0.1: {} - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false + vary@1.1.2: {} - /vite-node@0.34.6(@types/node@18.11.11): - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true + vite-node@0.34.6(@types/node@18.11.11): dependencies: cac: 6.7.14 debug: 4.3.5(supports-color@5.5.0) @@ -5989,90 +6959,28 @@ packages: - sugarss - supports-color - terser - dev: true - /vite-tsconfig-paths@4.3.2(typescript@5.2.2): - resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true + vite-tsconfig-paths@4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)): dependencies: debug: 4.3.5(supports-color@5.5.0) globrex: 0.1.2 tsconfck: 3.1.1(typescript@5.2.2) + optionalDependencies: + vite: 5.3.3(@types/node@18.11.11) transitivePeerDependencies: - supports-color - typescript - dev: true - /vite@5.3.3(@types/node@18.11.11): - resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.3.3(@types/node@18.11.11): dependencies: - '@types/node': 18.11.11 esbuild: 0.21.5 postcss: 8.4.39 rollup: 4.18.1 optionalDependencies: + '@types/node': 18.11.11 fsevents: 2.3.3 - dev: true - /vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true + vitest@0.34.6: dependencies: '@types/chai': 4.3.16 '@types/chai-subset': 1.3.5 @@ -6106,97 +7014,57 @@ packages: - sugarss - supports-color - terser - dev: true - /wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 - dev: true - /web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - dev: false + web-streams-polyfill@3.3.3: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} - /whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} + whatwg-url@13.0.0: dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 - dev: false - /which-pm@2.2.0: - resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} - engines: {node: '>=8.15'} + which-pm@2.2.0: dependencies: load-yaml-file: 0.2.0 path-exists: 4.0.0 - dev: true - /which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + which@1.3.1: dependencies: isexe: 2.0.0 - dev: true - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - dev: true - /which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + which@3.0.1: dependencies: isexe: 2.0.0 - dev: true - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - dev: true - /wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + wide-align@1.1.5: dependencies: string-width: 4.2.3 - dev: true - /widest-line@4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} + widest-line@4.0.1: dependencies: string-width: 5.1.2 - dev: true - /winston-transport@4.7.1: - resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} - engines: {node: '>= 12.0.0'} + winston-transport@4.7.1: dependencies: logform: 2.6.1 readable-stream: 3.6.2 triple-beam: 1.4.1 - dev: false - /winston@3.13.1: - resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} - engines: {node: '>= 12.0.0'} + winston@3.13.1: dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 @@ -6209,101 +7077,53 @@ packages: stack-trace: 0.0.10 triple-beam: 1.4.1 winston-transport: 4.7.1 - dev: false - /wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true + wrappy@1.0.2: {} - /write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + write-file-atomic@3.0.3: dependencies: imurmurhash: 0.1.4 is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - dev: true - /ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@8.18.0: {} - /xdg-basedir@5.1.0: - resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} - engines: {node: '>=12'} - dev: true + xdg-basedir@5.1.0: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: true + yallist@2.1.2: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true + yallist@4.0.0: {} - /yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} - engines: {node: '>= 14'} - hasBin: true - dev: false + yaml@2.4.5: {} - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - dev: true + yargs-parser@20.2.9: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true + yargs-parser@21.1.1: {} - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + yargs@16.2.0: dependencies: cliui: 7.0.4 escalade: 3.1.2 @@ -6312,11 +7132,8 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - dev: true - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -6325,35 +7142,13 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + yn@3.1.1: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@0.1.0: {} - /yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - dev: true + yocto-queue@1.1.1: {} - /zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - dev: false + yoctocolors-cjs@2.1.2: {} - github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: - resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} - name: pgwire - version: 0.7.0 - engines: {node: '>=14.18.0'} - dev: false + zod@3.23.8: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6324ebdd5..35bf9ea5c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: - 'packages/*' - 'libs/*' + - 'modules/*' - 'service' # exclude packages that are inside test directories - '!**/test/**' From 0e55bcd9fc6392556ea35451684a05c377820f41 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 15 Jul 2024 17:39:07 +0200 Subject: [PATCH 009/298] wip: not really sure what is meant to be going on here --- modules/module-postgres/package.json | 3 +- .../connection/PostgresConnectionManager.ts | 24 +++- .../connection/PostgresConnectionManager2.ts | 22 ---- .../replication/PostgresReplicationAdapter.ts | 15 ++- modules/module-postgres/src/types/types.ts | 114 +++++++++++++++++- packages/types/src/config/normalize.ts | 114 ------------------ pnpm-lock.yaml | 3 + 7 files changed, 148 insertions(+), 147 deletions(-) delete mode 100644 modules/module-postgres/src/connection/PostgresConnectionManager2.ts diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index 5497d72e4..2c01b3a60 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -23,7 +23,8 @@ "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87", - "ts-codec": "^1.2.2" + "ts-codec": "^1.2.2", + "uri-js": "^4.4.1" }, "devDependencies": { "typescript": "^5.2.2", diff --git a/modules/module-postgres/src/connection/PostgresConnectionManager.ts b/modules/module-postgres/src/connection/PostgresConnectionManager.ts index 3c499f5ac..8bdb2bd26 100644 --- a/modules/module-postgres/src/connection/PostgresConnectionManager.ts +++ b/modules/module-postgres/src/connection/PostgresConnectionManager.ts @@ -1,7 +1,11 @@ import * as pgwire from '@powersync/service-jpgwire'; -import { NormalizedPostgresConnection } from '@powersync/service-types'; -export class PgManager { +import { replication } from '@powersync/service-core'; +import { NormalizedPostgresConnectionConfig } from '../types/types.js'; + +export type PostgresConnection = pgwire.PgClient; + +export class PostgresConnectionManager implements replication.ConnectionManager { /** * Do not use this for any transactions. */ @@ -9,15 +13,15 @@ export class PgManager { private connectionPromises: Promise[] = []; - constructor(public options: NormalizedPostgresConnection, public poolOptions: pgwire.PgPoolOptions) { + constructor(public options: NormalizedPostgresConnectionConfig) { // The pool is lazy - no connections are opened until a query is performed. - this.pool = pgwire.connectPgWirePool(this.options, poolOptions); + this.pool = pgwire.connectPgWirePool(this.options, {}); } /** * Create a new replication connection. */ - async replicationConnection(): Promise { + async createReplicationConnection(): Promise { const p = pgwire.connectPgWire(this.options, { type: 'replication' }); this.connectionPromises.push(p); return await p; @@ -28,12 +32,20 @@ export class PgManager { * * This connection must not be shared between multiple async contexts. */ - async snapshotConnection(): Promise { + async createConnection(): Promise { const p = pgwire.connectPgWire(this.options, { type: 'standard' }); this.connectionPromises.push(p); return await p; } + mapError(error: Error): replication.ConnectionError { + throw new Error('Method not implemented.'); + } + + // TODO need some way to end connections + // The generics don't cater for this + // Can't register an automatic termination callback without service context + async end() { for (let result of await Promise.allSettled([ this.pool.end(), diff --git a/modules/module-postgres/src/connection/PostgresConnectionManager2.ts b/modules/module-postgres/src/connection/PostgresConnectionManager2.ts deleted file mode 100644 index 242162c78..000000000 --- a/modules/module-postgres/src/connection/PostgresConnectionManager2.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as pgwire from '@powersync/service-jpgwire'; - -import { replication } from '@powersync/service-core'; -import { PostgresConnectionConfig } from '../types/types.js'; - -export type PostgresConnection = pgwire.PgClient; - -export class PostgresConnectionManager implements replication.ConnectionManager { - constructor(protected config: PostgresConnectionConfig) {} - - createReplicationConnection(): Promise { - throw new Error('Method not implemented.'); - } - - createConnection(): Promise { - throw new Error('Method not implemented.'); - } - - mapError(error: Error): replication.ConnectionError { - throw new Error('Method not implemented.'); - } -} diff --git a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts index 98a092127..90a4d8d2d 100644 --- a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts @@ -1,17 +1,26 @@ import { replication, storage } from '@powersync/service-core'; import * as sync_rules from '@powersync/service-sync-rules'; + import { PostgresConnection, PostgresConnectionManager } from '../connection/PostgresConnectionManager.js'; -import { PostgresConnectionConfig } from '../types/types.js'; +import { + normalizeConnectionConfig, + NormalizedPostgresConnectionConfig, + PostgresConnectionConfig +} from '../types/types.js'; export class PostgresReplicationAdapter implements replication.ReplicationAdapter { - constructor(protected config: PostgresConnectionConfig) {} + protected normalizedConfig: NormalizedPostgresConnectionConfig; + + constructor(config: PostgresConnectionConfig) { + this.normalizedConfig = normalizeConnectionConfig(config); + } name(): string { return 'postgres'; } createConnectionManager(): PostgresConnectionManager { - return new PostgresConnectionManager(this.config); + return new PostgresConnectionManager(this.normalizedConfig); } validateConfiguration(connection: PostgresConnection): void { diff --git a/modules/module-postgres/src/types/types.ts b/modules/module-postgres/src/types/types.ts index 031a5910b..678295a91 100644 --- a/modules/module-postgres/src/types/types.ts +++ b/modules/module-postgres/src/types/types.ts @@ -1,7 +1,25 @@ import * as t from 'ts-codec'; - +import * as urijs from 'uri-js'; import * as service_types from '@powersync/service-types'; +export interface NormalizedPostgresConnectionConfig { + id: string; + tag: string; + + hostname: string; + port: number; + database: string; + + username: string; + password: string; + + sslmode: 'verify-full' | 'verify-ca' | 'disable'; + cacert: string | undefined; + + client_certificate: string | undefined; + client_private_key: string | undefined; +} + export const PostgresConnectionConfig = service_types.configFile.dataSourceConfig.and( t.object({ type: t.literal('postgresql'), @@ -37,3 +55,97 @@ export const PostgresConnectionConfig = service_types.configFile.dataSourceConfi ); export type PostgresConnectionConfig = t.Decoded; + +/** + * Validate and normalize connection options. + * + * Returns destructured options. + */ +export function normalizeConnectionConfig(options: PostgresConnectionConfig): NormalizedPostgresConnectionConfig { + let uri: urijs.URIComponents; + if (options.uri) { + uri = urijs.parse(options.uri); + if (uri.scheme != 'postgresql' && uri.scheme != 'postgres') { + `Invalid URI - protocol must be postgresql, got ${uri.scheme}`; + } else if (uri.scheme != 'postgresql') { + uri.scheme = 'postgresql'; + } + } else { + uri = urijs.parse('postgresql:///'); + } + + const hostname = options.hostname ?? uri.host ?? ''; + const port = validatePort(options.port ?? uri.port ?? 5432); + + const database = options.database ?? uri.path?.substring(1) ?? ''; + + const [uri_username, uri_password] = (uri.userinfo ?? '').split(':'); + + const username = options.username ?? uri_username ?? ''; + const password = options.password ?? uri_password ?? ''; + + const sslmode = options.sslmode ?? 'verify-full'; // Configuration not supported via URI + const cacert = options.cacert; + + if (sslmode == 'verify-ca' && cacert == null) { + throw new Error('Explicit cacert is required for sslmode=verify-ca'); + } + + if (hostname == '') { + throw new Error(`hostname required`); + } + + if (username == '') { + throw new Error(`username required`); + } + + if (password == '') { + throw new Error(`password required`); + } + + if (database == '') { + throw new Error(`database required`); + } + + return { + id: options.id ?? 'default', + tag: options.tag ?? 'default', + + hostname, + port, + database, + + username, + password, + sslmode, + cacert, + + client_certificate: options.client_certificate ?? undefined, + client_private_key: options.client_private_key ?? undefined + }; +} + +/** + * Check whether the port is in a "safe" range. + * + * We do not support connecting to "privileged" ports. + */ +export function validatePort(port: string | number): number { + if (typeof port == 'string') { + port = parseInt(port); + } + if (port >= 1024 && port <= 49151) { + return port; + } else { + throw new Error(`Port ${port} not supported`); + } +} + +/** + * Construct a postgres URI, without username, password or ssl options. + * + * Only contains hostname, port, database. + */ +export function baseUri(options: NormalizedPostgresConnectionConfig) { + return `postgresql://${options.hostname}:${options.port}/${options.database}`; +} diff --git a/packages/types/src/config/normalize.ts b/packages/types/src/config/normalize.ts index 76e7bf49a..e69de29bb 100644 --- a/packages/types/src/config/normalize.ts +++ b/packages/types/src/config/normalize.ts @@ -1,114 +0,0 @@ -import { PostgresConnection } from './PowerSyncConfig.js'; -import * as urijs from 'uri-js'; - -/** - * Validate and normalize connection options. - * - * Returns destructured options. - */ -export function normalizeConnection(options: PostgresConnection): NormalizedPostgresConnection { - let uri: urijs.URIComponents; - if (options.uri) { - uri = urijs.parse(options.uri); - if (uri.scheme != 'postgresql' && uri.scheme != 'postgres') { - `Invalid URI - protocol must be postgresql, got ${uri.scheme}`; - } else if (uri.scheme != 'postgresql') { - uri.scheme = 'postgresql'; - } - } else { - uri = urijs.parse('postgresql:///'); - } - - const hostname = options.hostname ?? uri.host ?? ''; - const port = validatePort(options.port ?? uri.port ?? 5432); - - const database = options.database ?? uri.path?.substring(1) ?? ''; - - const [uri_username, uri_password] = (uri.userinfo ?? '').split(':'); - - const username = options.username ?? uri_username ?? ''; - const password = options.password ?? uri_password ?? ''; - - const sslmode = options.sslmode ?? 'verify-full'; // Configuration not supported via URI - const cacert = options.cacert; - - if (sslmode == 'verify-ca' && cacert == null) { - throw new Error('Explicit cacert is required for sslmode=verify-ca'); - } - - if (hostname == '') { - throw new Error(`hostname required`); - } - - if (username == '') { - throw new Error(`username required`); - } - - if (password == '') { - throw new Error(`password required`); - } - - if (database == '') { - throw new Error(`database required`); - } - - return { - id: options.id ?? 'default', - tag: options.tag ?? 'default', - - hostname, - port, - database, - - username, - password, - sslmode, - cacert, - - client_certificate: options.client_certificate ?? undefined, - client_private_key: options.client_private_key ?? undefined - }; -} - -export interface NormalizedPostgresConnection { - id: string; - tag: string; - - hostname: string; - port: number; - database: string; - - username: string; - password: string; - - sslmode: 'verify-full' | 'verify-ca' | 'disable'; - cacert: string | undefined; - - client_certificate: string | undefined; - client_private_key: string | undefined; -} - -/** - * Check whether the port is in a "safe" range. - * - * We do not support connecting to "privileged" ports. - */ -export function validatePort(port: string | number): number { - if (typeof port == 'string') { - port = parseInt(port); - } - if (port >= 1024 && port <= 49151) { - return port; - } else { - throw new Error(`Port ${port} not supported`); - } -} - -/** - * Construct a postgres URI, without username, password or ssl options. - * - * Only contains hostname, port, database. - */ -export function baseUri(options: NormalizedPostgresConnection) { - return `postgresql://${options.hostname}:${options.port}/${options.database}`; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18a731f32..1542f431b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -114,6 +114,9 @@ importers: ts-codec: specifier: ^1.2.2 version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 devDependencies: typescript: specifier: ^5.2.2 From 271e0550e0e1742ec886f29d3075fdd7177f4447 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 12:31:13 +0200 Subject: [PATCH 010/298] SyncAPI tweaks based on feedback ReplicationAdapter enum fixes --- packages/service-core/src/api/SyncAPI.ts | 27 +++++++++++++------ .../replication/core/ReplicationAdapter.ts | 12 ++++----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index c0771b9f0..ac63c8720 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -1,19 +1,16 @@ import { TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; +import * as t from 'ts-codec'; /** * Describes all the methods currently required to service the sync API endpoints. - * TODO: This interface needs to be cleaned up and made more generic. It describes the current functionality required by our API routes */ export interface SyncAPI { /** - * Performs diagnostics on the "connection" + * Checks the current connection status of the datasource. * This is usually some test query to verify the source can be reached. */ - getDiagnostics(): Promise<{ - connected: boolean; - errors?: Array<{ level: string; message: string }>; - }>; + getConnectionStatus(): Promise; /** * Generates replication table information from a given pattern of tables. @@ -49,10 +46,10 @@ export interface SyncAPI { getConnectionSchema(): Promise; /** - * Executes a SQL statement and returns the result. This is currently used in the + * Executes a query and return the result from the data source. This is currently used in the * admin API which is exposed in Collide. */ - executeSQL(sql: string, params: any[]): Promise; + executeQuery(query: string, params: any[]): Promise; //CRUD API : I don't think this is used besides maybe niche dev use cases } @@ -65,3 +62,17 @@ interface PatternResult { tables?: types.TableInfo[]; table?: types.TableInfo; } + +interface ErrorDescription { + level: string; + message: string; +} +interface StatusResponse { + connected: boolean; + errors?: ErrorDescription[]; +} + +interface QueryResults { + columns: string[]; + rows: (string | number | boolean | null)[][]; +} diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 6485926bd..83063aa62 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -69,10 +69,10 @@ export interface ReplicationAdapter { } export enum ConnectionError { - INCORRECT_CREDENTIALS, - HOST_INACCESSIBLE, - CONNECTION_REFUSED, - CONNECTION_CLOSED, - RETRIEVAL_FAILED, - GENERAL + INCORRECT_CREDENTIALS = 'INCORRECT_CREDENTIALS', + HOST_INACCESSIBLE = 'HOST_INACCESSIBLE', + CONNECTION_REFUSED = 'CONNECTION_REFUSED', + CONNECTION_CLOSED = 'CONNECTION_CLOSED', + RETRIEVAL_FAILED = 'RETRIEVAL_FAILED', + GENERAL = 'GENERAL' } From 4bc8f3b495884c29afbce7730c926c712c5a0ad3 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 13:05:28 +0200 Subject: [PATCH 011/298] Removed unused imports --- packages/service-core/src/api/SyncAPI.ts | 1 - .../src/replication/core/ReplicationEntity.ts | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index ac63c8720..68f9ae7d4 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -1,6 +1,5 @@ import { TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; -import * as t from 'ts-codec'; /** * Describes all the methods currently required to service the sync API endpoints. diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts index 01d8f7b8f..0f4d52af5 100644 --- a/packages/service-core/src/replication/core/ReplicationEntity.ts +++ b/packages/service-core/src/replication/core/ReplicationEntity.ts @@ -1,4 +1,3 @@ -import { SourceTable } from '../../storage/SourceTable.js'; import { SqliteRow } from '@powersync/service-sync-rules'; /** @@ -58,8 +57,8 @@ export abstract class ReplicationEntity { } /** - * Get the number of entries for this Entity - * @param connection + * Get the number of entries for this Entity + * @param connection */ public abstract count(connection: TConnection): Promise; @@ -70,5 +69,8 @@ export abstract class ReplicationEntity { * @param connection * @param entryConsumer */ - public abstract getSnapshot(connection: TConnection, entryConsumer: (batch: SqliteRow[]) => {}): Promise; + public abstract getSnapshot( + connection: TConnection, + entryConsumer: (batch: SqliteRow[]) => {} + ): Promise; } From 282052ed518994516b80dc36f97de89bd05ef7ca Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 16 Jul 2024 13:31:13 +0200 Subject: [PATCH 012/298] normalized config --- packages/jpgwire/src/util.ts | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/jpgwire/src/util.ts b/packages/jpgwire/src/util.ts index 61df4f91c..309d89717 100644 --- a/packages/jpgwire/src/util.ts +++ b/packages/jpgwire/src/util.ts @@ -1,15 +1,33 @@ -import { NormalizedPostgresConnection } from '@powersync/service-types'; import * as tls from 'tls'; import { DEFAULT_CERTS } from './certs.js'; import * as pgwire from './pgwire.js'; import { PgType } from './pgwire_types.js'; import * as datefns from 'date-fns'; +// TODO this is duplicated, but maybe that is ok +export interface NormalizedConnectionConfig { + id: string; + tag: string; + + hostname: string; + port: number; + database: string; + + username: string; + password: string; + + sslmode: 'verify-full' | 'verify-ca' | 'disable'; + cacert: string | undefined; + + client_certificate: string | undefined; + client_private_key: string | undefined; +} + type Mutable = { -readonly [P in keyof T]: T[P]; }; -export interface PgWireConnectionOptions extends NormalizedPostgresConnection { +export interface PgWireConnectionOptions extends NormalizedConnectionConfig { resolved_ip?: string; } From c644251295e859b1130a64a94391ea2e9a6152cc Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 13:48:18 +0200 Subject: [PATCH 013/298] Added interfaces for constructor parameters --- .../service-core/src/modules/AbstractModule.ts | 10 ++++++++-- .../src/replication/core/ReplicationEngine.ts | 12 ++++++++---- .../src/replication/core/ReplicationModule.ts | 15 +++++++++------ .../src/replication/core/Replicator.ts | 11 ++++++++--- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index dfad51f97..554c866bd 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -1,10 +1,16 @@ import { ServiceContext } from '../system/ServiceContext.js'; +export interface AbstractModuleOptions { + name: string; +} + export abstract class AbstractModule { public name: string; + protected options: AbstractModuleOptions; - protected constructor(name: string) { - this.name = name; + protected constructor(options: AbstractModuleOptions) { + this.options = options; + this.name = options.name; } /** diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index 56c392120..b0fea21df 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -2,12 +2,16 @@ import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; import { Replicator } from './Replicator.js'; +export interface ReplicationEngineOptions { + storage: BucketStorageFactory; +} + export class ReplicationEngine { - private readonly storage: BucketStorageFactory; + private readonly options: ReplicationEngineOptions; private readonly replicators: Map, Replicator> = new Map(); - constructor(storage: BucketStorageFactory) { - this.storage = storage; + constructor(options: ReplicationEngineOptions) { + this.options = options; } /** @@ -20,7 +24,7 @@ export class ReplicationEngine { if (this.replicators.has(adapter)) { throw new Error(`Replicator for type ${adapter.name} already registered`); } - this.replicators.set(adapter, new Replicator(this.storage, adapter)); + this.replicators.set(adapter, new Replicator(adapter, this.options.storage)); } /** diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 70b93f338..751155e9b 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -3,9 +3,13 @@ import * as t from 'ts-codec'; import { logger, schema } from '@powersync/lib-services-framework'; import { ReplicationAdapter } from './ReplicationAdapter.js'; import { SyncAPI } from '../../api/SyncAPI.js'; -import { AbstractModule } from '../../modules/AbstractModule.js'; +import { AbstractModule, AbstractModuleOptions } from '../../modules/AbstractModule.js'; import { ServiceContext } from '../../system/ServiceContext.js'; +export interface ReplicationModuleOptions extends AbstractModuleOptions { + type: string; +} + /** * A replication module describes all the functionality that PowerSync requires to * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. @@ -14,13 +18,12 @@ export abstract class ReplicationModule extends AbstractModule { protected type: string; /** - * @param name - * @param type The type of the data source * @protected + * @param options */ - protected constructor(name: string, type: string) { - super(name); - this.type = type; + protected constructor(options: ReplicationModuleOptions) { + super(options); + this.type = options.type; } /** diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts index 12b05f062..047f9b688 100644 --- a/packages/service-core/src/replication/core/Replicator.ts +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -1,6 +1,11 @@ import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +export interface ReplicatorOptions { + adapter: ReplicationAdapter; + storage: BucketStorageFactory; +} + /** * A replicator manages the mechanics for replicating data from a data source to a storage bucket. * This includes copying across the original data set and then keeping it in sync with the data source. @@ -10,9 +15,9 @@ export class Replicator { private readonly adapter: ReplicationAdapter; private storage: BucketStorageFactory; - constructor(storage: BucketStorageFactory, adapter: ReplicationAdapter) { - this.adapter = adapter; - this.storage = storage; + constructor(options: ReplicatorOptions) { + this.adapter = options.adapter; + this.storage = options.storage; } public async start(): Promise { From febbacf29b8b052e11838cd3755a7a6bc719b5f0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 13:58:19 +0200 Subject: [PATCH 014/298] Fixed Replicator arguments Updated lockfile --- .../src/replication/core/ReplicationEngine.ts | 2 +- pnpm-lock.yaml | 7258 +++++++++-------- 2 files changed, 4020 insertions(+), 3240 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index b0fea21df..abcbb346f 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -24,7 +24,7 @@ export class ReplicationEngine { if (this.replicators.has(adapter)) { throw new Error(`Replicator for type ${adapter.name} already registered`); } - this.replicators.set(adapter, new Replicator(adapter, this.options.storage)); + this.replicators.set(adapter, new Replicator({ adapter: adapter, storage: this.options.storage })); } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c250d75f..2cd881d9c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -104,7 +104,7 @@ importers: version: 3.6.0 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 packages/jsonbig: dependencies: @@ -225,7 +225,7 @@ importers: version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -371,7 +371,7 @@ importers: version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -412,38 +412,3229 @@ importers: packages: - /@babel/code-frame@7.24.7: + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.24.8': + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} + engines: {node: '>=6.9.0'} + + '@changesets/apply-release-plan@7.0.4': + resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} + + '@changesets/assemble-release-plan@6.0.3': + resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} + + '@changesets/changelog-git@0.2.0': + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + + '@changesets/cli@2.27.7': + resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} + hasBin: true + + '@changesets/config@3.0.2': + resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.1': + resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} + + '@changesets/get-release-plan@4.0.3': + resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.0': + resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + + '@changesets/logger@0.1.0': + resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + + '@changesets/parse@0.4.0': + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + + '@changesets/pre@2.0.0': + resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + + '@changesets/read@0.6.0': + resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + + '@changesets/should-skip-package@0.1.0': + resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.0.0': + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + + '@changesets/write@0.3.1': + resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@fastify/ajv-compiler@3.6.0': + resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} + + '@fastify/cors@8.4.1': + resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} + + '@fastify/error@3.4.1': + resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + + '@fastify/fast-json-stringify-compiler@4.3.0': + resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + + '@fastify/merge-json-schemas@0.1.1': + resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + + '@gar/promisify@1.1.3': + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + + '@humanwhocodes/momoa@2.0.4': + resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} + engines: {node: '>=10.10.0'} + + '@inquirer/figures@1.0.3': + resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} + engines: {node: '>=18'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@js-sdsl/ordered-set@4.4.2': + resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@mongodb-js/saslprep@1.1.7': + resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@npmcli/fs@2.1.2': + resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + '@npmcli/fs@3.1.1': + resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/git@4.1.0': + resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/installed-package-contents@2.1.0': + resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + '@npmcli/move-file@2.0.1': + resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This functionality has been moved to @npmcli/fs + + '@npmcli/node-gyp@3.0.0': + resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/promise-spawn@6.0.2': + resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/run-script@6.0.2': + resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@opentelemetry/api-logs@0.51.1': + resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} + engines: {node: '>=14'} + + '@opentelemetry/api-logs@0.52.1': + resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} + engines: {node: '>=14'} + + '@opentelemetry/api@1.6.0': + resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.8.0': + resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/context-async-hooks@1.25.1': + resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@1.17.0': + resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.7.0' + + '@opentelemetry/core@1.24.1': + resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/core@1.25.1': + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-metrics-otlp-http@0.51.1': + resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.43.0': + resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.51.1': + resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.38.0': + resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.41.0': + resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.38.0': + resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.42.0': + resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.40.0': + resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.52.1': + resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.42.0': + resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.42.0': + resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.46.0': + resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.40.0': + resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.40.0': + resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.40.0': + resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.39.0': + resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.43.0': + resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis-4@0.41.0': + resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.46.0': + resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.52.1': + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.51.1': + resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/otlp-transformer@0.51.1': + resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' + + '@opentelemetry/redis-common@0.36.2': + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + + '@opentelemetry/resources@1.17.0': + resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.7.0' + + '@opentelemetry/resources@1.24.1': + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/resources@1.25.1': + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.51.1': + resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.9.0' + '@opentelemetry/api-logs': '>=0.39.1' + + '@opentelemetry/sdk-metrics@1.17.0': + resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.7.0' + + '@opentelemetry/sdk-metrics@1.24.1': + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' + + '@opentelemetry/sdk-trace-base@1.24.1': + resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/sdk-trace-base@1.25.1': + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.17.0': + resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.24.1': + resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.25.1': + resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} + engines: {node: '>=14'} + + '@opentelemetry/sql-common@0.40.1': + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.2.2': + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} + + '@prisma/instrumentation@5.16.1': + resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} + + '@rollup/rollup-android-arm-eabi@4.18.1': + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.1': + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.1': + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.1': + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.1': + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.1': + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.1': + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.1': + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.1': + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.1': + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.1': + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.1': + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.1': + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.1': + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + + '@sentry/core@8.17.0': + resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} + engines: {node: '>=14.18'} + + '@sentry/node@8.17.0': + resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} + engines: {node: '>=14.18'} + + '@sentry/opentelemetry@8.17.0': + resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} + engines: {node: '>=14.18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.25.1 + '@opentelemetry/instrumentation': ^0.52.1 + '@opentelemetry/sdk-trace-base': ^1.25.1 + '@opentelemetry/semantic-conventions': ^1.25.1 + + '@sentry/types@8.17.0': + resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} + engines: {node: '>=14.18'} + + '@sentry/utils@8.17.0': + resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} + engines: {node: '>=14.18'} + + '@sigstore/bundle@1.1.0': + resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/protobuf-specs@0.2.1': + resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/sign@1.0.0': + resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/tuf@1.0.3': + resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + + '@syncpoint/wkx@0.5.2': + resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} + + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@tufjs/canonical-json@1.0.0': + resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@tufjs/models@1.0.4': + resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@types/async@3.2.24': + resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} + + '@types/chai-subset@1.3.5': + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + + '@types/chai@4.3.16': + resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + + '@types/connect@3.4.36': + resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + + '@types/mysql@2.15.22': + resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@13.13.52': + resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} + + '@types/node@15.14.9': + resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} + + '@types/node@18.11.11': + resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} + + '@types/pg-pool@2.0.4': + resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + + '@types/pg@8.6.1': + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + + '@types/semver-utils@1.1.3': + resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + + '@types/strip-bom@3.0.0': + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + + '@types/strip-json-comments@0.0.30': + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + + '@types/ws@8.2.3': + resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + + '@vitest/expect@0.34.6': + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + + '@vitest/runner@0.34.6': + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + + '@vitest/snapshot@0.34.6': + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + + '@vitest/spy@0.34.6': + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + + '@vitest/utils@0.34.6': + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + + acorn-import-assertions@1.9.0: + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + + async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + avvio@8.3.2: + resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-ajv-errors@1.2.0: + resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + ajv: 4.11.8 - 8 + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@16.1.3: + resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + cacache@17.1.4: + resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@6.0.0: + resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} + engines: {node: '>=12'} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exponential-backoff@3.1.1: + resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stringify@5.16.1: + resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} + + fast-memoize@2.5.2: + resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-uri@2.4.0: + resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} + + fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + + fastify@4.23.2: + resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-my-way@7.7.0: + resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} + engines: {node: '>=14'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fp-and-or@0.1.4: + resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} + engines: {node: '>=10'} + + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + has-yarn@3.0.0: + resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hosted-git-info@5.2.1: + resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + hosted-git-info@6.1.1: + resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-in-the-middle@1.7.1: + resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} + + import-in-the-middle@1.9.0: + resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@9.3.5: + resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} + engines: {node: '>=18'} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + + is-npm@6.0.0: + resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-yarn-global@0.4.1: + resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} + engines: {node: '>=12'} + + isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + ix@5.0.0: + resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + json-parse-helpfulerror@1.0.3: + resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} + + json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonlines@0.1.1: + resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + light-my-request@5.13.0: + resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + + load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + logform@2.6.1: + resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} + engines: {node: '>= 12.0.0'} + + lossless-json@2.0.11: + resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + make-fetch-happen@10.2.1: + resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + make-fetch-happen@11.1.1: + resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + minipass-fetch@3.0.5: + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-json-stream@1.0.1: + resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mnemonist@0.39.5: + resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} + + module-details-from-path@1.0.3: + resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.8.0: + resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-gyp@9.4.1: + resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} + engines: {node: ^12.13 || ^14.13 || >=16} + hasBin: true + + nodemon@3.1.4: + resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} + engines: {node: '>=10'} + hasBin: true + + noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + + nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + normalize-package-data@5.0.0: + resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + + npm-bundled@3.0.1: + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-check-updates@16.14.20: + resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} + engines: {node: '>=14.14'} + hasBin: true + + npm-install-checks@6.3.0: + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-package-arg@10.1.0: + resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-packlist@7.0.4: + resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-pick-manifest@8.0.2: + resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-registry-fetch@14.0.5: + resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + opentelemetry-instrumentation-fetch-node@1.2.3: + resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} + engines: {node: '>18.0.0'} + peerDependencies: + '@opentelemetry/api': ^1.6.0 + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + + pacote@15.2.0: + resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + parse-github-url@1.0.3: + resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} + engines: {node: '>= 0.10'} + hasBin: true + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pgsql-ast-parser@11.2.0: + resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} + + pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: + resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} + name: pgwire + version: 0.7.0 + engines: {node: '>=14.18.0'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + + pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + + pino@8.21.0: + resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} + hasBin: true + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + preferred-pm@3.1.4: + resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} + engines: {node: '>=10'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + + process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + prompts-ncu@3.0.0: + resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} + engines: {node: '>= 14'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@3.1.0: + resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + engines: {node: '>=12.20'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + + randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + + rc-config-loader@4.1.3: + resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + read-package-json@6.0.4: + resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + remote-git-tags@3.0.0: + resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} + engines: {node: '>=8'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@7.3.0: + resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} + engines: {node: '>=8.6.0'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + + ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@5.0.9: + resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} + engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} + hasBin: true + + rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rsocket-core@1.0.0-alpha.3: + resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} + + rsocket-websocket-client@1.0.0-alpha.3: + resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} + + semver-utils@1.1.4: + resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sigstore@1.9.0: + resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sonic-boom@3.8.1: + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + + spawn-please@2.0.2: + resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} + engines: {node: '>=14'} + + spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssri@10.0.6: + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@5.0.1: + resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} + engines: {node: '>=14.16'} + + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + thread-stream@2.7.0: + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + + tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-codec@1.2.2: + resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} + + ts-node-dev@2.0.0: + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsc-watch@6.2.0: + resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' + + tsconfck@3.1.1: + resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tuf-js@1.1.7: + resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-filename@3.0.0: + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + + update-notifier@6.0.2: + resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} + engines: {node: '>=14.16'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@0.34.6: + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + + vite-tsconfig-paths@4.3.2: + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + winston-transport@4.7.1: + resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} + engines: {node: '>= 12.0.0'} + + winston@3.13.1: + resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} + engines: {node: '>= 12.0.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - dev: false - /@babel/helper-validator-identifier@7.24.7: - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} - dev: false + '@babel/helper-validator-identifier@7.24.7': {} - /@babel/highlight@7.24.7: - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.1 - dev: false - /@babel/runtime@7.24.8: - resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} - engines: {node: '>=6.9.0'} + '@babel/runtime@7.24.8': dependencies: regenerator-runtime: 0.14.1 - dev: true - /@changesets/apply-release-plan@7.0.4: - resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} + '@changesets/apply-release-plan@7.0.4': dependencies: '@babel/runtime': 7.24.8 '@changesets/config': 3.0.2 @@ -459,10 +3650,8 @@ packages: prettier: 2.8.8 resolve-from: 5.0.0 semver: 7.6.2 - dev: true - /@changesets/assemble-release-plan@6.0.3: - resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} + '@changesets/assemble-release-plan@6.0.3': dependencies: '@babel/runtime': 7.24.8 '@changesets/errors': 0.2.0 @@ -471,17 +3660,12 @@ packages: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 semver: 7.6.2 - dev: true - /@changesets/changelog-git@0.2.0: - resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + '@changesets/changelog-git@0.2.0': dependencies: '@changesets/types': 6.0.0 - dev: true - /@changesets/cli@2.27.7: - resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} - hasBin: true + '@changesets/cli@2.27.7': dependencies: '@babel/runtime': 7.24.8 '@changesets/apply-release-plan': 7.0.4 @@ -515,10 +3699,8 @@ packages: semver: 7.6.2 spawndamnit: 2.0.0 term-size: 2.2.1 - dev: true - /@changesets/config@3.0.2: - resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} + '@changesets/config@3.0.2': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.1 @@ -527,26 +3709,20 @@ packages: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.7 - dev: true - /@changesets/errors@0.2.0: - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + '@changesets/errors@0.2.0': dependencies: extendable-error: 0.1.7 - dev: true - /@changesets/get-dependents-graph@2.1.1: - resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} + '@changesets/get-dependents-graph@2.1.1': dependencies: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 semver: 7.6.2 - dev: true - /@changesets/get-release-plan@4.0.3: - resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} + '@changesets/get-release-plan@4.0.3': dependencies: '@babel/runtime': 7.24.8 '@changesets/assemble-release-plan': 6.0.3 @@ -555,14 +3731,10 @@ packages: '@changesets/read': 0.6.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/get-version-range-type@0.4.0: - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - dev: true + '@changesets/get-version-range-type@0.4.0': {} - /@changesets/git@3.0.0: - resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + '@changesets/git@3.0.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/errors': 0.2.0 @@ -571,33 +3743,25 @@ packages: is-subdir: 1.2.0 micromatch: 4.0.7 spawndamnit: 2.0.0 - dev: true - /@changesets/logger@0.1.0: - resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + '@changesets/logger@0.1.0': dependencies: chalk: 2.4.2 - dev: true - /@changesets/parse@0.4.0: - resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + '@changesets/parse@0.4.0': dependencies: '@changesets/types': 6.0.0 js-yaml: 3.14.1 - dev: true - /@changesets/pre@2.0.0: - resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + '@changesets/pre@2.0.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/errors': 0.2.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - dev: true - /@changesets/read@0.6.0: - resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + '@changesets/read@0.6.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/git': 3.0.0 @@ -607,359 +3771,168 @@ packages: chalk: 2.4.2 fs-extra: 7.0.1 p-filter: 2.1.0 - dev: true - /@changesets/should-skip-package@0.1.0: - resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} + '@changesets/should-skip-package@0.1.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/types@4.1.0: - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - dev: true + '@changesets/types@4.1.0': {} - /@changesets/types@6.0.0: - resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - dev: true + '@changesets/types@6.0.0': {} - /@changesets/write@0.3.1: - resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + '@changesets/write@0.3.1': dependencies: '@babel/runtime': 7.24.8 '@changesets/types': 6.0.0 fs-extra: 7.0.1 human-id: 1.0.2 prettier: 2.8.8 - dev: true - /@colors/colors@1.5.0: - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - requiresBuild: true - dev: true + '@colors/colors@1.5.0': optional: true - /@colors/colors@1.6.0: - resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} - engines: {node: '>=0.1.90'} - dev: false + '@colors/colors@1.6.0': {} - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: true - /@dabh/diagnostics@2.0.3: - resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 enabled: 2.0.0 kuler: 2.0.0 - dev: false - /@esbuild/aix-ppc64@0.21.5: - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.21.5': optional: true - /@esbuild/android-arm64@0.21.5: - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.21.5': optional: true - /@esbuild/android-arm@0.21.5: - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.21.5': optional: true - /@esbuild/android-x64@0.21.5: - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-x64@0.21.5': optional: true - /@esbuild/darwin-arm64@0.21.5: - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.21.5': optional: true - /@esbuild/darwin-x64@0.21.5: - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.21.5': optional: true - /@esbuild/freebsd-arm64@0.21.5: - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.21.5': optional: true - /@esbuild/freebsd-x64@0.21.5: - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.21.5': optional: true - /@esbuild/linux-arm64@0.21.5: - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.21.5': optional: true - /@esbuild/linux-arm@0.21.5: - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.21.5': optional: true - /@esbuild/linux-ia32@0.21.5: - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.21.5': optional: true - /@esbuild/linux-loong64@0.21.5: - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.21.5': optional: true - /@esbuild/linux-mips64el@0.21.5: - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.21.5': optional: true - /@esbuild/linux-ppc64@0.21.5: - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.21.5': optional: true - /@esbuild/linux-riscv64@0.21.5: - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.21.5': optional: true - /@esbuild/linux-s390x@0.21.5: - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.21.5': optional: true - /@esbuild/linux-x64@0.21.5: - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.21.5': optional: true - /@esbuild/netbsd-x64@0.21.5: - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.21.5': optional: true - /@esbuild/openbsd-x64@0.21.5: - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.21.5': optional: true - /@esbuild/sunos-x64@0.21.5: - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.21.5': optional: true - /@esbuild/win32-arm64@0.21.5: - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.21.5': optional: true - /@esbuild/win32-ia32@0.21.5: - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.21.5': optional: true - /@esbuild/win32-x64@0.21.5: - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-x64@0.21.5': optional: true - /@fastify/ajv-compiler@3.6.0: - resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} + '@fastify/ajv-compiler@3.6.0': dependencies: ajv: 8.16.0 ajv-formats: 2.1.1(ajv@8.16.0) fast-uri: 2.4.0 - /@fastify/cors@8.4.1: - resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} + '@fastify/cors@8.4.1': dependencies: fastify-plugin: 4.5.1 mnemonist: 0.39.5 - dev: false - /@fastify/error@3.4.1: - resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + '@fastify/error@3.4.1': {} - /@fastify/fast-json-stringify-compiler@4.3.0: - resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + '@fastify/fast-json-stringify-compiler@4.3.0': dependencies: fast-json-stringify: 5.16.1 - /@fastify/merge-json-schemas@0.1.1: - resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + '@fastify/merge-json-schemas@0.1.1': dependencies: fast-deep-equal: 3.1.3 - /@gar/promisify@1.1.3: - resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - dev: true + '@gar/promisify@1.1.3': {} - /@humanwhocodes/momoa@2.0.4: - resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} - engines: {node: '>=10.10.0'} - dev: false + '@humanwhocodes/momoa@2.0.4': {} - /@inquirer/figures@1.0.3: - resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} - engines: {node: '>=18'} - dev: true + '@inquirer/figures@1.0.3': {} - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 + string-width-cjs: string-width@4.2.3 strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 + strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true + wrap-ansi-cjs: wrap-ansi@7.0.0 - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - dev: true - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - dev: true + '@jridgewell/resolve-uri@3.1.2': {} - /@jridgewell/sourcemap-codec@1.5.0: - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - dev: true + '@jridgewell/sourcemap-codec@1.5.0': {} - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /@js-sdsl/ordered-set@4.4.2: - resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} - dev: false + '@js-sdsl/ordered-set@4.4.2': {} - /@manypkg/find-root@1.1.0: - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.8 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 - dev: true - /@manypkg/get-packages@1.1.3: - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@manypkg/get-packages@1.1.3': dependencies: '@babel/runtime': 7.24.8 '@changesets/types': 4.1.0 @@ -967,53 +3940,33 @@ packages: fs-extra: 8.1.0 globby: 11.1.0 read-yaml-file: 1.1.0 - dev: true - /@mongodb-js/saslprep@1.1.7: - resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + '@mongodb-js/saslprep@1.1.7': dependencies: sparse-bitfield: 3.0.3 - dev: false - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: true + '@nodelib/fs.stat@2.0.5': {} - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - dev: true - /@npmcli/fs@2.1.2: - resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + '@npmcli/fs@2.1.2': dependencies: '@gar/promisify': 1.1.3 semver: 7.6.2 - dev: true - /@npmcli/fs@3.1.1: - resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/fs@3.1.1': dependencies: semver: 7.6.2 - dev: true - /@npmcli/git@4.1.0: - resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/git@4.1.0': dependencies: '@npmcli/promise-spawn': 6.0.2 lru-cache: 7.18.3 @@ -1025,41 +3978,24 @@ packages: which: 3.0.1 transitivePeerDependencies: - bluebird - dev: true - /@npmcli/installed-package-contents@2.1.0: - resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + '@npmcli/installed-package-contents@2.1.0': dependencies: npm-bundled: 3.0.1 npm-normalize-package-bin: 3.0.1 - dev: true - /@npmcli/move-file@2.0.1: - resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This functionality has been moved to @npmcli/fs + '@npmcli/move-file@2.0.1': dependencies: mkdirp: 1.0.4 rimraf: 3.0.2 - dev: true - /@npmcli/node-gyp@3.0.0: - resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@npmcli/node-gyp@3.0.0': {} - /@npmcli/promise-spawn@6.0.2: - resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/promise-spawn@6.0.2': dependencies: which: 3.0.1 - dev: true - /@npmcli/run-script@6.0.2: - resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/run-script@6.0.2': dependencies: '@npmcli/node-gyp': 3.0.0 '@npmcli/promise-spawn': 6.0.2 @@ -1069,121 +4005,61 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /@opentelemetry/api-logs@0.51.1: - resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} - engines: {node: '>=14'} + '@opentelemetry/api-logs@0.51.1': dependencies: '@opentelemetry/api': 1.8.0 - dev: false - /@opentelemetry/api-logs@0.52.1: - resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} - engines: {node: '>=14'} + '@opentelemetry/api-logs@0.52.1': dependencies: '@opentelemetry/api': 1.6.0 - dev: false - /@opentelemetry/api@1.6.0: - resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} - engines: {node: '>=8.0.0'} - dev: false + '@opentelemetry/api@1.6.0': {} - /@opentelemetry/api@1.8.0: - resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} - engines: {node: '>=8.0.0'} - dev: false + '@opentelemetry/api@1.8.0': {} - /@opentelemetry/api@1.9.0: - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - dev: false + '@opentelemetry/api@1.9.0': {} - /@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - dev: false - /@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.7.0' + '@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.17.0 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) @@ -1191,37 +4067,22 @@ packages: '@opentelemetry/otlp-transformer': 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/sdk-metrics': 1.17.0(@opentelemetry/api@1.6.0) - dev: false - /@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1230,13 +4091,8 @@ packages: '@types/connect': 3.4.36 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1244,13 +4100,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1258,25 +4109,15 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1284,13 +4125,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1299,13 +4135,8 @@ packages: semver: 7.6.2 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1313,13 +4144,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1327,13 +4153,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1341,13 +4162,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1355,13 +4171,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1369,13 +4180,8 @@ packages: '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1383,26 +4189,16 @@ packages: '@types/mysql': 2.15.22 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1412,13 +4208,8 @@ packages: '@types/pg-pool': 2.0.4 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1426,14 +4217,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} - engines: {node: '>=14'} - requiresBuild: true - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@types/shimmer': 1.2.0 @@ -1443,14 +4228,9 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false optional: true - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/api-logs': 0.52.1 @@ -1461,13 +4241,8 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.52.1 @@ -1478,13 +4253,8 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.52.1 @@ -1495,23 +4265,13 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.0.0 + '@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 @@ -1520,389 +4280,195 @@ packages: '@opentelemetry/sdk-logs': 0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/redis-common@0.36.2: - resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/redis-common@0.36.2': {} - /@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.7.0' + '@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.17.0 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.9.0' - '@opentelemetry/api-logs': '>=0.39.1' + '@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.7.0' + '@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.9.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/semantic-conventions@1.17.0: - resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.17.0': {} - /@opentelemetry/semantic-conventions@1.24.1: - resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.24.1': {} - /@opentelemetry/semantic-conventions@1.25.1: - resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.25.1': {} - /@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.1.0 + '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - dev: false - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: true + '@pkgjs/parseargs@0.11.0': optional: true - /@pnpm/config.env-replace@1.1.0: - resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} - engines: {node: '>=12.22.0'} - dev: true + '@pnpm/config.env-replace@1.1.0': {} - /@pnpm/network.ca-file@1.0.2: - resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} - engines: {node: '>=12.22.0'} + '@pnpm/network.ca-file@1.0.2': dependencies: graceful-fs: 4.2.10 - dev: true - - /@pnpm/npm-conf@2.2.2: - resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} - engines: {node: '>=12'} + + '@pnpm/npm-conf@2.2.2': dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - dev: true - /@prisma/instrumentation@5.16.1: - resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} + '@prisma/instrumentation@5.16.1': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.8.0) transitivePeerDependencies: - supports-color - dev: false - /@rollup/rollup-android-arm-eabi@4.18.1: - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true - /@rollup/rollup-android-arm64@4.18.1: - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-arm64@4.18.1: - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-x64@4.18.1: - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-x64@4.18.1': optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.18.1: - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm-musleabihf@4.18.1: - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm64-gnu@4.18.1: - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-arm64-musl@4.18.1: - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-musl@4.18.1': optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': optional: true - /@rollup/rollup-linux-riscv64-gnu@4.18.1: - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-riscv64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-s390x-gnu@4.18.1: - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-s390x-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-gnu@4.18.1: - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-musl@4.18.1: - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-musl@4.18.1': optional: true - /@rollup/rollup-win32-arm64-msvc@4.18.1: - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-arm64-msvc@4.18.1': optional: true - /@rollup/rollup-win32-ia32-msvc@4.18.1: - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-ia32-msvc@4.18.1': optional: true - /@rollup/rollup-win32-x64-msvc@4.18.1: - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-x64-msvc@4.18.1': optional: true - /@sentry/core@8.17.0: - resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} - engines: {node: '>=14.18'} + '@sentry/core@8.17.0': dependencies: '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 - dev: false - /@sentry/node@8.17.0: - resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} - engines: {node: '>=14.18'} + '@sentry/node@8.17.0': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -1935,17 +4501,8 @@ packages: opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1): - resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} - engines: {node: '>=14.18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/core': ^1.25.1 - '@opentelemetry/instrumentation': ^0.52.1 - '@opentelemetry/sdk-trace-base': ^1.25.1 - '@opentelemetry/semantic-conventions': ^1.25.1 + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) @@ -1955,477 +4512,276 @@ packages: '@sentry/core': 8.17.0 '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 - dev: false - /@sentry/types@8.17.0: - resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} - engines: {node: '>=14.18'} + '@sentry/types@8.17.0': {} - /@sentry/utils@8.17.0: - resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} - engines: {node: '>=14.18'} + '@sentry/utils@8.17.0': dependencies: '@sentry/types': 8.17.0 - dev: false - /@sigstore/bundle@1.1.0: - resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/bundle@1.1.0': dependencies: '@sigstore/protobuf-specs': 0.2.1 - dev: true - /@sigstore/protobuf-specs@0.2.1: - resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@sigstore/protobuf-specs@0.2.1': {} - /@sigstore/sign@1.0.0: - resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/sign@1.0.0': dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /@sigstore/tuf@1.0.3: - resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/tuf@1.0.3': dependencies: '@sigstore/protobuf-specs': 0.2.1 tuf-js: 1.1.7 transitivePeerDependencies: - supports-color - dev: true - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true + '@sinclair/typebox@0.27.8': {} - /@sindresorhus/is@5.6.0: - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} - dev: true + '@sindresorhus/is@5.6.0': {} - /@syncpoint/wkx@0.5.2: - resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} + '@syncpoint/wkx@0.5.2': dependencies: '@types/node': 15.14.9 - dev: false - /@szmarczak/http-timer@5.0.1: - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} + '@szmarczak/http-timer@5.0.1': dependencies: defer-to-connect: 2.0.1 - dev: true - /@tootallnate/once@2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - dev: true + '@tootallnate/once@2.0.0': {} - /@tsconfig/node10@1.0.11: - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - dev: true + '@tsconfig/node10@1.0.11': {} - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true + '@tsconfig/node12@1.0.11': {} - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true + '@tsconfig/node14@1.0.3': {} - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true + '@tsconfig/node16@1.0.4': {} - /@tufjs/canonical-json@1.0.0: - resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@tufjs/canonical-json@1.0.0': {} - /@tufjs/models@1.0.4: - resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@tufjs/models@1.0.4': dependencies: '@tufjs/canonical-json': 1.0.0 minimatch: 9.0.5 - dev: true - /@types/async@3.2.24: - resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - dev: true + '@types/async@3.2.24': {} - /@types/chai-subset@1.3.5: - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + '@types/chai-subset@1.3.5': dependencies: '@types/chai': 4.3.16 - dev: true - /@types/chai@4.3.16: - resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} - dev: true + '@types/chai@4.3.16': {} - /@types/connect@3.4.36: - resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + '@types/connect@3.4.36': dependencies: '@types/node': 18.11.11 - dev: false - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true + '@types/estree@1.0.5': {} - /@types/http-cache-semantics@4.0.4: - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - dev: true + '@types/http-cache-semantics@4.0.4': {} - /@types/lodash@4.17.6: - resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} - dev: true + '@types/lodash@4.17.6': {} - /@types/mysql@2.15.22: - resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + '@types/mysql@2.15.22': dependencies: '@types/node': 18.11.11 - dev: false - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: true + '@types/node@12.20.55': {} - /@types/node@13.13.52: - resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} - dev: false + '@types/node@13.13.52': {} - /@types/node@15.14.9: - resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - dev: false + '@types/node@15.14.9': {} - /@types/node@18.11.11: - resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} + '@types/node@18.11.11': {} - /@types/pg-pool@2.0.4: - resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + '@types/pg-pool@2.0.4': dependencies: '@types/pg': 8.6.1 - dev: false - /@types/pg@8.6.1: - resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + '@types/pg@8.6.1': dependencies: '@types/node': 18.11.11 pg-protocol: 1.6.1 pg-types: 2.2.0 - dev: false - /@types/semver-utils@1.1.3: - resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} - dev: true + '@types/semver-utils@1.1.3': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true + '@types/semver@7.5.8': {} - /@types/shimmer@1.2.0: - resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} - dev: false + '@types/shimmer@1.2.0': {} - /@types/strip-bom@3.0.0: - resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - dev: true + '@types/strip-bom@3.0.0': {} - /@types/strip-json-comments@0.0.30: - resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - dev: true + '@types/strip-json-comments@0.0.30': {} - /@types/triple-beam@1.3.5: - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - dev: false + '@types/triple-beam@1.3.5': {} - /@types/uuid@9.0.8: - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - dev: true + '@types/uuid@9.0.8': {} - /@types/webidl-conversions@7.0.3: - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - dev: false + '@types/webidl-conversions@7.0.3': {} - /@types/whatwg-url@11.0.5: - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/whatwg-url@11.0.5': dependencies: '@types/webidl-conversions': 7.0.3 - dev: false - /@types/ws@8.2.3: - resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + '@types/ws@8.2.3': dependencies: '@types/node': 18.11.11 - dev: true - /@vitest/expect@0.34.6: - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + '@vitest/expect@0.34.6': dependencies: '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 chai: 4.4.1 - dev: true - /@vitest/runner@0.34.6: - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/runner@0.34.6': dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.2 - dev: true - /@vitest/snapshot@0.34.6: - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/snapshot@0.34.6': dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 - dev: true - /@vitest/spy@0.34.6: - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/spy@0.34.6': dependencies: tinyspy: 2.2.1 - dev: true - /@vitest/utils@0.34.6: - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/utils@0.34.6': dependencies: diff-sequences: 29.6.3 loupe: 2.3.7 pretty-format: 29.7.0 - dev: true - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true + abbrev@1.1.1: {} - /abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 - /abstract-logging@2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + abstract-logging@2.0.1: {} - /acorn-import-assertions@1.9.0(acorn@8.12.1): - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} - requiresBuild: true - peerDependencies: - acorn: ^8 + acorn-import-assertions@1.9.0(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: false optional: true - /acorn-import-attributes@1.9.5(acorn@8.12.1): - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: false - /acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} - engines: {node: '>=0.4.0'} + acorn-walk@8.3.3: dependencies: acorn: 8.12.1 - dev: true - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true + acorn@8.12.1: {} - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} + agent-base@6.0.2: dependencies: debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} + agentkeepalive@4.5.0: dependencies: humanize-ms: 1.2.1 - dev: true - /aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - dev: true - /ajv-formats@2.1.1(ajv@8.16.0): - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true + ajv-formats@2.1.1(ajv@8.16.0): dependencies: ajv: 8.16.0 - /ajv-formats@3.0.1(ajv@8.16.0): - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true + ajv-formats@3.0.1(ajv@8.16.0): dependencies: ajv: 8.16.0 - /ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + ajv@8.16.0: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - /ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + ansi-align@3.0.1: dependencies: string-width: 4.2.3 - dev: true - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: true + ansi-colors@4.1.3: {} - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - dev: true - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true + ansi-styles@5.2.0: {} - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true + ansi-styles@6.2.1: {} - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true - /aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: true + aproba@2.0.0: {} - /are-we-there-yet@3.0.1: - resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + are-we-there-yet@3.0.1: dependencies: delegates: 1.0.0 readable-stream: 3.6.2 - dev: true - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true + arg@4.1.3: {} - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true + argparse@2.0.1: {} - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true + array-union@2.1.0: {} - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true + assertion-error@1.1.0: {} - /async-mutex@0.5.0: - resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async-mutex@0.5.0: dependencies: tslib: 2.6.3 - dev: false - /async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.5: {} - /atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} + atomic-sleep@1.0.0: {} - /avvio@8.3.2: - resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + avvio@8.3.2: dependencies: '@fastify/error': 3.4.1 fastq: 1.17.1 - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + balanced-match@1.0.2: {} - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64-js@1.5.1: {} - /better-ajv-errors@1.2.0(ajv@8.16.0): - resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - ajv: 4.11.8 - 8 + better-ajv-errors@1.2.0(ajv@8.16.0): dependencies: '@babel/code-frame': 7.24.7 '@humanwhocodes/momoa': 2.0.4 @@ -2433,31 +4789,20 @@ packages: chalk: 4.1.2 jsonpointer: 5.0.1 leven: 3.1.0 - dev: false - /better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 - dev: true - /binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - dev: true + binary-extensions@2.3.0: {} - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /boxen@7.1.1: - resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} - engines: {node: '>=14.16'} + boxen@7.1.1: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 @@ -2467,57 +4812,37 @@ packages: type-fest: 2.19.0 widest-line: 4.0.1 wrap-ansi: 8.1.0 - dev: true - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - dev: true - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - dev: true - /bson@6.8.0: - resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} - engines: {node: '>=16.20.1'} + bson@6.8.0: {} - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true + buffer-from@1.1.2: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - dev: true + cac@6.7.14: {} - /cacache@16.1.3: - resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + cacache@16.1.3: dependencies: '@npmcli/fs': 2.1.2 '@npmcli/move-file': 2.0.1 @@ -2539,11 +4864,8 @@ packages: unique-filename: 2.0.1 transitivePeerDependencies: - bluebird - dev: true - /cacache@17.1.4: - resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + cacache@17.1.4: dependencies: '@npmcli/fs': 3.1.1 fs-minipass: 3.0.3 @@ -2557,16 +4879,10 @@ packages: ssri: 10.0.6 tar: 6.2.1 unique-filename: 3.0.0 - dev: true - /cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - dev: true + cacheable-lookup@7.0.0: {} - /cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} + cacheable-request@10.2.14: dependencies: '@types/http-cache-semantics': 4.0.4 get-stream: 6.0.1 @@ -2575,16 +4891,10 @@ packages: mimic-response: 4.0.0 normalize-url: 8.0.1 responselike: 3.0.0 - dev: true - /camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - dev: true + camelcase@7.0.1: {} - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} + chai@4.4.1: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -2593,41 +4903,27 @@ packages: loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 - dev: true - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: true + chalk@5.3.0: {} - /chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: true + chardet@0.7.0: {} - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 - dev: true - /chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -2638,145 +4934,83 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - dev: true + chownr@2.0.0: {} - /ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - dev: true + ci-info@3.9.0: {} - /cjs-module-lexer@1.3.1: - resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} - dev: false + cjs-module-lexer@1.3.1: {} - /clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: true + clean-stack@2.2.0: {} - /cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - dev: true + cli-boxes@3.0.0: {} - /cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 - dev: true - /cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - dev: true + cli-spinners@2.9.2: {} - /cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} + cli-table3@0.6.5: dependencies: string-width: 4.2.3 optionalDependencies: '@colors/colors': 1.5.0 - dev: true - /cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - dev: true + cli-width@4.1.0: {} - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@7.0.4: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - dev: true + clone@1.0.4: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false - /color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - dev: true + color-support@1.1.3: {} - /color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@3.2.1: dependencies: color-convert: 1.9.3 color-string: 1.9.1 - dev: false - /colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + colorspace@1.1.4: dependencies: color: 3.2.1 text-hex: 1.0.0 - dev: false - /commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - dev: true + commander@10.0.1: {} - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - dev: false + commander@12.1.0: {} - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false + commander@2.20.3: {} - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + concat-map@0.0.1: {} - /concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} - hasBin: true + concurrently@8.2.2: dependencies: chalk: 4.1.2 date-fns: 2.30.0 @@ -2787,41 +5021,27 @@ packages: supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 - dev: true - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true + confbox@0.1.7: {} - /config-chain@1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + config-chain@1.1.13: dependencies: ini: 1.3.8 proto-list: 1.2.4 - dev: true - /configstore@6.0.0: - resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} - engines: {node: '>=12'} + configstore@6.0.0: dependencies: dot-prop: 6.0.1 graceful-fs: 4.2.11 unique-string: 3.0.0 write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 - dev: true - /console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: true + console-control-strings@1.1.0: {} - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} + cookie@0.6.0: {} - /copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true + copyfiles@2.4.1: dependencies: glob: 7.2.3 minimatch: 3.1.2 @@ -2830,204 +5050,110 @@ packages: through2: 2.0.5 untildify: 4.0.0 yargs: 16.2.0 - dev: true - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true + core-util-is@1.0.3: {} - /cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} + cors@2.8.5: dependencies: object-assign: 4.1.1 vary: 1.1.2 - dev: false - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true + create-require@1.1.1: {} - /cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + cross-spawn@5.1.0: dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 - dev: true - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true - /crypto-random-string@4.0.0: - resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} - engines: {node: '>=12'} + crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 - dev: true - /data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - dev: false + data-uri-to-buffer@4.0.1: {} - /date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} + date-fns@2.30.0: dependencies: '@babel/runtime': 7.24.8 - dev: true - /date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - dev: false + date-fns@3.6.0: {} - /debug@4.3.5(supports-color@5.5.0): - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.5(supports-color@5.5.0): dependencies: ms: 2.1.2 supports-color: 5.5.0 - /decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 - dev: true - /deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} + deep-eql@4.1.4: dependencies: type-detect: 4.0.8 - dev: true - /deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - dev: true + deep-extend@0.6.0: {} - /defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defaults@1.0.4: dependencies: clone: 1.0.4 - dev: true - /defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - dev: true + defer-to-connect@2.0.1: {} - /delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: true + delegates@1.0.0: {} - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true + detect-indent@6.1.0: {} - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true + diff-sequences@29.6.3: {} - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true + diff@4.0.2: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dev: true - /discontinuous-range@1.0.0: - resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} - dev: false + discontinuous-range@1.0.0: {} - /dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} + dot-prop@6.0.1: dependencies: is-obj: 2.0.0 - dev: true - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - dev: false + dotenv@16.4.5: {} - /duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - dev: true + duplexer@0.1.2: {} - /dynamic-dedupe@0.3.0: - resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + dynamic-dedupe@0.3.0: dependencies: xtend: 4.0.2 - dev: true - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true + emoji-regex@9.2.2: {} - /enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: false + enabled@2.0.0: {} - /encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true + encoding@0.1.13: dependencies: iconv-lite: 0.6.3 - dev: true optional: true - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - dev: true - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: true + env-paths@2.2.1: {} - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: true + err-code@2.0.3: {} - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 '@esbuild/android-arm': 0.21.5 @@ -3052,30 +5178,16 @@ packages: '@esbuild/win32-arm64': 0.21.5 '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - dev: true - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - dev: true + escalade@3.1.2: {} - /escape-goat@4.0.0: - resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} - engines: {node: '>=12'} - dev: true + escape-goat@4.0.0: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true + esprima@4.0.1: {} - /event-stream@3.3.4: - resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + event-stream@3.3.4: dependencies: duplexer: 0.1.2 from: 0.1.7 @@ -3084,55 +5196,36 @@ packages: split: 0.3.3 stream-combiner: 0.0.4 through: 2.3.8 - dev: true - /event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} + event-target-shim@5.0.1: {} - /events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} + events@3.3.0: {} - /exponential-backoff@3.1.1: - resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} - dev: true + exponential-backoff@3.1.1: {} - /extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - dev: true + extendable-error@0.1.7: {} - /external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} + external-editor@3.1.0: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 - dev: true - /fast-content-type-parse@1.1.0: - resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + fast-content-type-parse@1.1.0: {} - /fast-decode-uri-component@1.0.1: - resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-decode-uri-component@1.0.1: {} - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.7 - dev: true - /fast-json-stringify@5.16.1: - resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} + fast-json-stringify@5.16.1: dependencies: '@fastify/merge-json-schemas': 0.1.1 ajv: 8.16.0 @@ -3142,27 +5235,19 @@ packages: json-schema-ref-resolver: 1.0.1 rfdc: 1.4.1 - /fast-memoize@2.5.2: - resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} - dev: true + fast-memoize@2.5.2: {} - /fast-querystring@1.1.2: - resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fast-querystring@1.1.2: dependencies: fast-decode-uri-component: 1.0.1 - /fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} + fast-redact@3.5.0: {} - /fast-uri@2.4.0: - resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} + fast-uri@2.4.0: {} - /fastify-plugin@4.5.1: - resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + fastify-plugin@4.5.1: {} - /fastify@4.23.2: - resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} + fastify@4.23.2: dependencies: '@fastify/ajv-compiler': 3.6.0 '@fastify/error': 3.4.1 @@ -3181,149 +5266,89 @@ packages: semver: 7.6.2 toad-cache: 3.7.0 - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - /fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - dev: false + fecha@4.2.3: {} - /fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 - dev: false - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /find-my-way@7.7.0: - resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} - engines: {node: '>=14'} + find-my-way@7.7.0: dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 safe-regex2: 2.0.0 - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /find-yarn-workspace-root2@1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + find-yarn-workspace-root2@1.2.16: dependencies: micromatch: 4.0.7 pkg-dir: 4.2.0 - dev: true - /fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: false + fn.name@1.1.0: {} - /foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - dev: true + form-data-encoder@2.1.4: {} - /formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 - dev: false - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} + forwarded@0.2.0: {} - /fp-and-or@0.1.4: - resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} - engines: {node: '>=10'} - dev: true + fp-and-or@0.1.4: {} - /from@0.1.7: - resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} - dev: true + from@0.1.7: {} - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 - dev: true - /fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fs-minipass@3.0.3: dependencies: minipass: 7.1.2 - dev: true - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /gauge@4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + gauge@4.0.4: dependencies: aproba: 2.0.0 color-support: 1.1.3 @@ -3333,37 +5358,20 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wide-align: 1.1.5 - dev: true - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + get-caller-file@2.0.5: {} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true + get-func-name@2.0.2: {} - /get-stdin@8.0.0: - resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} - engines: {node: '>=10'} - dev: true + get-stdin@8.0.0: {} - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: true + get-stream@6.0.1: {} - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true + glob@10.4.5: dependencies: foreground-child: 3.2.1 jackspeak: 3.4.3 @@ -3371,11 +5379,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -3383,30 +5388,20 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true - /glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + glob@8.1.0: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 minimatch: 5.1.6 once: 1.4.0 - dev: true - /global-dirs@3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} + global-dirs@3.0.1: dependencies: ini: 2.0.0 - dev: true - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -3414,15 +5409,10 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - dev: true - /globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - dev: true + globrex@0.1.2: {} - /got@12.6.1: - resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} - engines: {node: '>=14.16'} + got@12.6.1: dependencies: '@sindresorhus/is': 5.6.0 '@szmarczak/http-timer': 5.0.1 @@ -3435,198 +5425,115 @@ packages: lowercase-keys: 3.0.0 p-cancelable: 3.0.0 responselike: 3.0.0 - dev: true - /graceful-fs@4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: true + graceful-fs@4.2.10: {} - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true + graceful-fs@4.2.11: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: true + has-unicode@2.0.1: {} - /has-yarn@3.0.0: - resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + has-yarn@3.0.0: {} - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /hosted-git-info@5.2.1: - resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hosted-git-info@5.2.1: dependencies: lru-cache: 7.18.3 - dev: true - /hosted-git-info@6.1.1: - resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hosted-git-info@6.1.1: dependencies: lru-cache: 7.18.3 - dev: true - /http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: true + http-cache-semantics@4.1.1: {} - /http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} + http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - dev: true - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - dev: true + human-id@1.0.2: {} - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + humanize-ms@1.2.1: dependencies: ms: 2.1.3 - dev: true - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: true - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - requiresBuild: true + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - dev: true optional: true - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ieee754@1.2.1: {} - /ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - dev: true + ignore-by-default@1.0.1: {} - /ignore-walk@6.0.5: - resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ignore-walk@6.0.5: dependencies: minimatch: 9.0.5 - dev: true - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: true + ignore@5.3.1: {} - /import-in-the-middle@1.7.1: - resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} - requiresBuild: true + import-in-the-middle@1.7.1: dependencies: acorn: 8.12.1 acorn-import-assertions: 1.9.0(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 - dev: false optional: true - /import-in-the-middle@1.9.0: - resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} + import-in-the-middle@1.9.0: dependencies: acorn: 8.12.1 acorn-import-attributes: 1.9.5(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 - dev: false - /import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - dev: true + import-lazy@4.0.0: {} - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true + imurmurhash@0.1.4: {} - /indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - dev: true + indent-string@4.0.0: {} - /infer-owner@1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - dev: true + infer-owner@1.0.4: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true + ini@1.3.8: {} - /ini@2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - dev: true + ini@2.0.0: {} - /ini@4.1.3: - resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + ini@4.1.3: {} - /inquirer@9.3.5: - resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} - engines: {node: '>=18'} + inquirer@9.3.5: dependencies: '@inquirer/figures': 1.0.3 ansi-escapes: 4.3.2 @@ -3640,326 +5547,178 @@ packages: strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 - dev: true - /ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} + ip-address@9.0.5: dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 - dev: true - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} + ipaddr.js@1.9.1: {} - /ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} - engines: {node: '>= 10'} - dev: false + ipaddr.js@2.2.0: {} - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + is-arrayish@0.3.2: {} - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - dev: true - /is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true + is-ci@3.0.1: dependencies: ci-info: 3.9.0 - dev: true - /is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} - engines: {node: '>= 0.4'} + is-core-module@2.14.0: dependencies: hasown: 2.0.2 - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true + is-fullwidth-code-point@3.0.0: {} - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - - /is-installed-globally@0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} + + is-installed-globally@0.4.0: dependencies: global-dirs: 3.0.1 is-path-inside: 3.0.3 - dev: true - /is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - dev: true + is-interactive@1.0.0: {} - /is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: true + is-lambda@1.0.1: {} - /is-npm@6.0.0: - resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-npm@6.0.0: {} - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + is-number@7.0.0: {} - /is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - dev: true + is-obj@2.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true + is-path-inside@3.0.3: {} - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false + is-stream@2.0.1: {} - /is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 - dev: true - /is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: true + is-typedarray@1.0.0: {} - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true + is-unicode-supported@0.1.0: {} - /is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - dev: true + is-windows@1.0.2: {} - /is-yarn-global@0.4.1: - resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} - engines: {node: '>=12'} - dev: true + is-yarn-global@0.4.1: {} - /isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - dev: true + isarray@0.0.1: {} - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true + isarray@1.0.0: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + isexe@2.0.0: {} - /ix@5.0.0: - resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} + ix@5.0.0: dependencies: '@types/node': 13.13.52 tslib: 2.6.3 - dev: false - /jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - dev: true + jju@1.4.0: {} - /jose@4.15.9: - resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - dev: false + jose@4.15.9: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false + js-tokens@4.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - dev: true - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - dev: true + jsbn@1.1.0: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + json-buffer@3.0.1: {} - /json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + json-parse-even-better-errors@3.0.2: {} - /json-parse-helpfulerror@1.0.3: - resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} + json-parse-helpfulerror@1.0.3: dependencies: jju: 1.4.0 - dev: true - /json-schema-ref-resolver@1.0.1: - resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + json-schema-ref-resolver@1.0.1: dependencies: fast-deep-equal: 3.1.3 - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true + json5@2.2.3: {} - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - dev: true - /jsonlines@0.1.1: - resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} - dev: true + jsonlines@0.1.1: {} - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: true + jsonparse@1.3.1: {} - /jsonpointer@5.0.1: - resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} - engines: {node: '>=0.10.0'} - dev: false + jsonpointer@5.0.1: {} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - dev: true - /kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - dev: true + kleur@4.1.5: {} - /kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: false + kuler@2.0.0: {} - /latest-version@7.0.0: - resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} - engines: {node: '>=14.16'} + latest-version@7.0.0: dependencies: package-json: 8.1.1 - dev: true - /leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - dev: false + leven@3.1.0: {} - /light-my-request@5.13.0: - resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + light-my-request@5.13.0: dependencies: cookie: 0.6.0 process-warning: 3.0.0 set-cookie-parser: 2.6.0 - /load-yaml-file@0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} + load-yaml-file@0.2.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - dev: true + local-pkg@0.4.3: {} - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: false + lodash.merge@4.6.2: {} - /lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - dev: true + lodash.startcase@4.4.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.21: {} - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - /logform@2.6.1: - resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} - engines: {node: '>= 12.0.0'} + logform@2.6.1: dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -3967,51 +5726,31 @@ packages: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 - dev: false - /lossless-json@2.0.11: - resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} - dev: false + lossless-json@2.0.11: {} - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 - dev: true - /lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + lowercase-keys@3.0.0: {} - /lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@10.4.3: {} - /lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: true - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - dev: true + lru-cache@7.18.3: {} - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true + make-error@1.3.6: {} - /make-fetch-happen@10.2.1: - resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + make-fetch-happen@10.2.1: dependencies: agentkeepalive: 4.5.0 cacache: 16.1.3 @@ -4032,11 +5771,8 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /make-fetch-happen@11.1.1: - resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + make-fetch-happen@11.1.1: dependencies: agentkeepalive: 4.5.0 cacache: 17.1.4 @@ -4055,277 +5791,146 @@ packages: ssri: 10.0.6 transitivePeerDependencies: - supports-color - dev: true - /map-stream@0.1.0: - resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} - dev: true + map-stream@0.1.0: {} - /memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - dev: false + memory-pager@1.5.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true + merge2@1.4.1: {} - /micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} + micromatch@4.0.7: dependencies: braces: 3.0.3 picomatch: 2.3.1 - dev: true - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true + mimic-fn@2.1.0: {} - /mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - dev: true + mimic-response@3.1.0: {} - /mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + mimic-response@4.0.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - dev: true - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true + minimist@1.2.8: {} - /minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} + minipass-collect@1.0.2: dependencies: minipass: 3.3.6 - dev: true - /minipass-fetch@2.1.2: - resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + minipass-fetch@2.1.2: dependencies: minipass: 3.3.6 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 - dev: true - /minipass-fetch@3.0.5: - resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + minipass-fetch@3.0.5: dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 - dev: true - /minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} + minipass-flush@1.0.5: dependencies: minipass: 3.3.6 - dev: true - /minipass-json-stream@1.0.1: - resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} + minipass-json-stream@1.0.1: dependencies: jsonparse: 1.3.1 minipass: 3.3.6 - dev: true - /minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} + minipass-pipeline@1.2.4: dependencies: minipass: 3.3.6 - dev: true - /minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} + minipass-sized@1.0.3: dependencies: minipass: 3.3.6 - dev: true - /minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} + minipass@3.3.6: dependencies: yallist: 4.0.0 - dev: true - /minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - dev: true + minipass@5.0.0: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + minizlib@2.1.2: dependencies: minipass: 3.3.6 yallist: 4.0.0 - dev: true - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@1.0.4: {} - /mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.1: dependencies: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.3 ufo: 1.5.3 - dev: true - /mnemonist@0.39.5: - resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} + mnemonist@0.39.5: dependencies: obliterator: 2.0.4 - dev: false - /module-details-from-path@1.0.3: - resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} - dev: false + module-details-from-path@1.0.3: {} - /mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + mongodb-connection-string-url@3.0.1: dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 - dev: false - /mongodb@6.8.0: - resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true + mongodb@6.8.0: dependencies: '@mongodb-js/saslprep': 1.1.7 bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - dev: false - /moo@0.5.2: - resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} - dev: false + moo@0.5.2: {} - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true + mri@1.2.0: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.2: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + mute-stream@1.0.0: {} - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + nanoid@3.3.7: {} - /nearley@2.20.1: - resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} - hasBin: true + nearley@2.20.1: dependencies: commander: 2.20.3 moo: 0.5.2 railroad-diagrams: 1.0.0 randexp: 0.4.6 - dev: false - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: true + negotiator@0.6.3: {} - /node-cleanup@2.1.2: - resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} - dev: true + node-cleanup@2.1.2: {} - /node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - dev: false + node-domexception@1.0.0: {} - /node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - dev: false - /node-gyp@9.4.1: - resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} - engines: {node: ^12.13 || ^14.13 || >=16} - hasBin: true + node-gyp@9.4.1: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.1 @@ -4341,12 +5946,8 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /nodemon@3.1.4: - resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} - engines: {node: '>=10'} - hasBin: true + nodemon@3.1.4: dependencies: chokidar: 3.6.0 debug: 4.3.5(supports-color@5.5.0) @@ -4358,54 +5959,32 @@ packages: supports-color: 5.5.0 touch: 3.1.1 undefsafe: 2.0.5 - dev: true - /noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + noms@0.0.0: dependencies: inherits: 2.0.4 readable-stream: 1.0.34 - dev: true - /nopt@6.0.0: - resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true + nopt@6.0.0: dependencies: abbrev: 1.1.1 - dev: true - /normalize-package-data@5.0.0: - resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + normalize-package-data@5.0.0: dependencies: hosted-git-info: 6.1.1 is-core-module: 2.14.0 semver: 7.6.2 validate-npm-package-license: 3.0.4 - dev: true - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} - engines: {node: '>=14.16'} - dev: true + normalize-url@8.0.1: {} - /npm-bundled@3.0.1: - resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-bundled@3.0.1: dependencies: npm-normalize-package-bin: 3.0.1 - dev: true - /npm-check-updates@16.14.20: - resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} - engines: {node: '>=14.14'} - hasBin: true + npm-check-updates@16.14.20: dependencies: '@types/semver-utils': 1.1.3 chalk: 5.3.0 @@ -4443,50 +6022,32 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /npm-install-checks@6.3.0: - resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-install-checks@6.3.0: dependencies: semver: 7.6.2 - dev: true - /npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + npm-normalize-package-bin@3.0.1: {} - /npm-package-arg@10.1.0: - resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-package-arg@10.1.0: dependencies: hosted-git-info: 6.1.1 proc-log: 3.0.0 semver: 7.6.2 validate-npm-package-name: 5.0.1 - dev: true - /npm-packlist@7.0.4: - resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-packlist@7.0.4: dependencies: ignore-walk: 6.0.5 - dev: true - /npm-pick-manifest@8.0.2: - resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-pick-manifest@8.0.2: dependencies: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 10.1.0 semver: 7.6.2 - dev: true - /npm-registry-fetch@14.0.5: - resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-registry-fetch@14.0.5: dependencies: make-fetch-happen: 11.1.1 minipass: 5.0.0 @@ -4497,69 +6058,42 @@ packages: proc-log: 3.0.0 transitivePeerDependencies: - supports-color - dev: true - /npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + npmlog@6.0.2: dependencies: are-we-there-yet: 3.0.1 console-control-strings: 1.1.0 gauge: 4.0.4 set-blocking: 2.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - dev: false + object-assign@4.1.1: {} - /obliterator@2.0.4: - resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} - dev: false + obliterator@2.0.4: {} - /on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} + on-exit-leak-free@2.1.2: {} - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - dev: true - /one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + one-time@1.0.0: dependencies: fn.name: 1.1.0 - dev: false - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - dev: true - /opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} - engines: {node: '>18.0.0'} - requiresBuild: true - peerDependencies: - '@opentelemetry/api': ^1.6.0 + opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false optional: true - /ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} + ora@5.4.1: dependencies: bl: 4.1.0 chalk: 4.1.2 @@ -4570,99 +6104,55 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - dev: true - /os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: true + os-tmpdir@1.0.2: {} - /outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - dev: true + outdent@0.5.0: {} - /p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - dev: true + p-cancelable@3.0.0: {} - /p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} + p-filter@2.1.0: dependencies: p-map: 2.1.0 - dev: true - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + p-limit@2.3.0: dependencies: p-try: 2.2.0 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-limit@4.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + p-locate@4.1.0: dependencies: p-limit: 2.3.0 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true + p-map@2.1.0: {} - /p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} + p-map@4.0.0: dependencies: aggregate-error: 3.1.0 - dev: true - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true + p-try@2.2.0: {} - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /package-json@8.1.1: - resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} - engines: {node: '>=14.16'} + package-json@8.1.1: dependencies: got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 semver: 7.6.2 - dev: true - /pacote@15.2.0: - resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + pacote@15.2.0: dependencies: '@npmcli/git': 4.1.0 '@npmcli/installed-package-contents': 2.1.0 @@ -4685,111 +6175,65 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /parse-github-url@1.0.3: - resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} - engines: {node: '>= 0.10'} - hasBin: true - dev: true + parse-github-url@1.0.3: {} - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + path-exists@4.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-is-absolute@1.0.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true + path-key@3.1.1: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-parse@1.0.7: {} - /path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 minipass: 7.1.2 - dev: true - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + path-type@4.0.0: {} - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true + pathe@1.1.2: {} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true + pathval@1.1.1: {} - /pause-stream@0.0.11: - resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pause-stream@0.0.11: dependencies: through: 2.3.8 - dev: true - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - dev: false + pg-int8@1.0.1: {} - /pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} - dev: false + pg-protocol@1.6.1: {} - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 postgres-bytea: 1.0.0 postgres-date: 1.0.7 postgres-interval: 1.2.0 - dev: false - /pgsql-ast-parser@11.2.0: - resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} + pgsql-ast-parser@11.2.0: dependencies: moo: 0.5.2 nearley: 2.20.1 - dev: false - /picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + picocolors@1.0.1: {} - /pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - dev: true + picomatch@2.3.1: {} - /pino-abstract-transport@1.2.0: - resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + pify@4.0.1: {} + + pino-abstract-transport@1.2.0: dependencies: readable-stream: 4.5.2 split2: 4.2.0 - /pino-std-serializers@6.2.2: - resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino-std-serializers@6.2.2: {} - /pino@8.21.0: - resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} - hasBin: true + pino@8.21.0: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 @@ -4803,190 +6247,106 @@ packages: sonic-boom: 3.8.1 thread-stream: 2.7.0 - /pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - dev: true - /pkg-types@1.1.3: - resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pkg-types@1.1.3: dependencies: confbox: 0.1.7 mlly: 1.7.1 pathe: 1.1.2 - dev: true - /postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - dev: true - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - dev: false + postgres-array@2.0.0: {} - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - dev: false + postgres-bytea@1.0.0: {} - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - dev: false + postgres-date@1.0.7: {} - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} + postgres-interval@1.2.0: dependencies: xtend: 4.0.2 - dev: false - /preferred-pm@3.1.4: - resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} - engines: {node: '>=10'} + preferred-pm@3.1.4: dependencies: find-up: 5.0.0 find-yarn-workspace-root2: 1.2.16 path-exists: 4.0.0 which-pm: 2.2.0 - dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true + prettier@2.8.8: {} - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - dev: true - /proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + proc-log@3.0.0: {} - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true + process-nextick-args@2.0.1: {} - /process-warning@2.3.2: - resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + process-warning@2.3.2: {} - /process-warning@3.0.0: - resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + process-warning@3.0.0: {} - /process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} + process@0.11.10: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true + progress@2.0.3: {} - /promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - dev: true + promise-inflight@1.0.1: {} - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} + promise-retry@2.0.1: dependencies: err-code: 2.0.3 retry: 0.12.0 - dev: true - /prompts-ncu@3.0.0: - resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} - engines: {node: '>= 14'} + prompts-ncu@3.0.0: dependencies: kleur: 4.1.5 sisteransi: 1.0.5 - dev: true - /proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - dev: true + proto-list@1.2.4: {} - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - /ps-tree@1.2.0: - resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} - engines: {node: '>= 0.10'} - hasBin: true + ps-tree@1.2.0: dependencies: event-stream: 3.3.4 - dev: true - /pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: true + pseudomap@1.0.2: {} - /pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - dev: true + pstree.remy@1.1.8: {} - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /pupa@3.1.0: - resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} - engines: {node: '>=12.20'} + pupa@3.1.0: dependencies: escape-goat: 4.0.0 - dev: true - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + queue-microtask@1.2.3: {} - /quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quick-format-unescaped@4.0.4: {} - /quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - dev: true + quick-lru@5.1.1: {} - /railroad-diagrams@1.0.0: - resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} - dev: false + railroad-diagrams@1.0.0: {} - /randexp@0.4.6: - resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} - engines: {node: '>=0.12'} + randexp@0.4.6: dependencies: discontinuous-range: 1.0.0 ret: 0.1.15 - dev: false - /rc-config-loader@4.1.3: - resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} + rc-config-loader@4.1.3: dependencies: debug: 4.3.5(supports-color@5.5.0) js-yaml: 4.1.0 @@ -4994,62 +6354,43 @@ packages: require-from-string: 2.0.2 transitivePeerDependencies: - supports-color - dev: true - /rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true + rc@1.2.8: dependencies: deep-extend: 0.6.0 ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - dev: true - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true + react-is@18.3.1: {} - /read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 npm-normalize-package-bin: 3.0.1 - dev: true - /read-package-json@6.0.4: - resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + read-package-json@6.0.4: dependencies: glob: 10.4.5 json-parse-even-better-errors: 3.0.2 normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 - dev: true - /read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + readable-stream@1.0.34: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 isarray: 0.0.1 string_decoder: 0.10.31 - dev: true - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -5058,19 +6399,14 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readable-stream@4.5.2: dependencies: abort-controller: 3.0.0 buffer: 6.0.3 @@ -5078,141 +6414,78 @@ packages: process: 0.11.10 string_decoder: 1.3.0 - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - dev: true - /real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} + real-require@0.2.0: {} - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: true + regenerator-runtime@0.14.1: {} - /registry-auth-token@5.0.2: - resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} - engines: {node: '>=14'} + registry-auth-token@5.0.2: dependencies: '@pnpm/npm-conf': 2.2.2 - dev: true - /registry-url@6.0.1: - resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} - engines: {node: '>=12'} + registry-url@6.0.1: dependencies: rc: 1.2.8 - dev: true - /remote-git-tags@3.0.0: - resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} - engines: {node: '>=8'} - dev: true + remote-git-tags@3.0.0: {} - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /require-in-the-middle@7.3.0: - resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} - engines: {node: '>=8.6.0'} + require-in-the-middle@7.3.0: dependencies: debug: 4.3.5(supports-color@5.5.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: false - /resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - dev: true + resolve-alpn@1.2.1: {} - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true + resolve-from@5.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} + responselike@3.0.0: dependencies: lowercase-keys: 3.0.0 - dev: true - /restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: true - /ret@0.1.15: - resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} - engines: {node: '>=0.12'} - dev: false + ret@0.1.15: {} - /ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} + ret@0.2.2: {} - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: true + retry@0.12.0: {} - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + reusify@1.0.4: {} - /rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rfdc@1.4.1: {} - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@2.7.1: dependencies: glob: 7.2.3 - dev: true - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - dev: true - /rimraf@5.0.9: - resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} - engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} - hasBin: true + rimraf@5.0.9: dependencies: glob: 10.4.5 - dev: true - /rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -5233,129 +6506,72 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.18.1 '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 - dev: true - /rsocket-core@1.0.0-alpha.3: - resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} + rsocket-core@1.0.0-alpha.3: {} - /rsocket-websocket-client@1.0.0-alpha.3: - resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} + rsocket-websocket-client@1.0.0-alpha.3: dependencies: rsocket-core: 1.0.0-alpha.3 - dev: true - /run-async@3.0.0: - resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} - engines: {node: '>=0.12.0'} - dev: true + run-async@3.0.0: {} - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - dev: true - /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.1: dependencies: tslib: 2.6.3 - dev: true - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-regex2@2.0.0: - resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + safe-regex2@2.0.0: dependencies: ret: 0.2.2 - /safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} - engines: {node: '>=10'} + safe-stable-stringify@2.4.3: {} - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true + safer-buffer@2.1.2: {} - /secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + secure-json-parse@2.7.0: {} - /semver-diff@4.0.0: - resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} - engines: {node: '>=12'} + semver-diff@4.0.0: dependencies: semver: 7.6.2 - dev: true - /semver-utils@1.1.4: - resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} - dev: true + semver-utils@1.1.4: {} - /semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.2: {} - /set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: true + set-blocking@2.0.0: {} - /set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + set-cookie-parser@2.6.0: {} - /shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 - dev: true - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - dev: true - /shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - dev: true + shebang-regex@1.0.0: {} - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - dev: false + shimmer@1.2.1: {} - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true + siginfo@2.0.0: {} - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: true + signal-exit@4.1.0: {} - /sigstore@1.9.0: - resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + sigstore@1.9.0: dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 @@ -5364,271 +6580,161 @@ packages: make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - dev: false - /simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} + simple-update-notifier@2.0.0: dependencies: semver: 7.6.2 - dev: true - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true + sisteransi@1.0.5: {} - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + slash@3.0.0: {} - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: true + smart-buffer@4.2.0: {} - /socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} + socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color - dev: true - /socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.3: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 - dev: true - /sonic-boom@3.8.1: - resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@3.8.1: dependencies: atomic-sleep: 1.0.0 - /source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - dev: true + source-map-js@1.2.0: {} - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true + source-map@0.6.1: {} - /sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 - dev: false - /spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - dev: true + spawn-command@0.0.2: {} - /spawn-please@2.0.2: - resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} - engines: {node: '>=14'} + spawn-please@2.0.2: dependencies: cross-spawn: 7.0.3 - dev: true - /spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + spawndamnit@2.0.0: dependencies: cross-spawn: 5.1.0 signal-exit: 3.0.7 - dev: true - /spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.18 - dev: true - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - dev: true + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 - dev: true - /spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - dev: true + spdx-license-ids@3.0.18: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + split2@4.2.0: {} - /split@0.3.3: - resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + split@0.3.3: dependencies: through: 2.3.8 - dev: true - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true + sprintf-js@1.0.3: {} - /sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - dev: true + sprintf-js@1.1.3: {} - /ssri@10.0.6: - resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ssri@10.0.6: dependencies: minipass: 7.1.2 - dev: true - /ssri@9.0.1: - resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + ssri@9.0.1: dependencies: minipass: 3.3.6 - dev: true - /stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - dev: false + stack-trace@0.0.10: {} - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true + stackback@0.0.2: {} - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true + std-env@3.7.0: {} - /stream-combiner@0.0.4: - resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-combiner@0.0.4: dependencies: duplexer: 0.1.2 - dev: true - /string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - dev: true + string-argv@0.3.2: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - /string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - dev: true + string_decoder@0.10.31: {} - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - dev: true - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - dev: true - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true + strip-bom@3.0.0: {} - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - dev: true + strip-json-comments@2.0.1: {} - /strip-json-comments@5.0.1: - resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} - engines: {node: '>=14.16'} - dev: true + strip-json-comments@5.0.1: {} - /strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + strip-literal@1.3.0: dependencies: acorn: 8.12.1 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} + tar@6.2.1: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 @@ -5636,101 +6742,51 @@ packages: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - dev: true - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: true + term-size@2.2.1: {} - /text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: false + text-hex@1.0.0: {} - /thread-stream@2.7.0: - resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@2.7.0: dependencies: real-require: 0.2.0 - /through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + through2@2.0.5: dependencies: readable-stream: 2.3.8 xtend: 4.0.2 - dev: true - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true + through@2.3.8: {} - /tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - dev: true + tinybench@2.8.0: {} - /tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} - dev: true + tinypool@0.7.0: {} - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true + tinyspy@2.2.1: {} - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - dev: true - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toad-cache@3.7.0: - resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} - engines: {node: '>=12'} + toad-cache@3.7.0: {} - /touch@3.1.1: - resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} - hasBin: true - dev: true + touch@3.1.1: {} - /tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} + tr46@4.1.1: dependencies: punycode: 2.3.1 - dev: false - /tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - dev: true + tree-kill@1.2.2: {} - /triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - dev: false + triple-beam@1.4.1: {} - /ts-codec@1.2.2: - resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} - dev: false + ts-codec@1.2.2: {} - /ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): - resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} - engines: {node: '>=0.8.0'} - hasBin: true - peerDependencies: - node-notifier: '*' - typescript: '*' - peerDependenciesMeta: - node-notifier: - optional: true + ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): dependencies: chokidar: 3.6.0 dynamic-dedupe: 0.3.0 @@ -5747,21 +6803,8 @@ packages: - '@swc/core' - '@swc/wasm' - '@types/node' - dev: true - /ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true + ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -5778,146 +6821,79 @@ packages: typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true - /tsc-watch@6.2.0(typescript@5.2.2): - resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} - engines: {node: '>=12.12.0'} - hasBin: true - peerDependencies: - typescript: '*' + tsc-watch@6.2.0(typescript@5.2.2): dependencies: cross-spawn: 7.0.3 node-cleanup: 2.1.2 ps-tree: 1.2.0 string-argv: 0.3.2 typescript: 5.2.2 - dev: true - /tsconfck@3.1.1(typescript@5.2.2): - resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true + tsconfck@3.1.1(typescript@5.2.2): dependencies: typescript: 5.2.2 - dev: true - /tsconfig@7.0.0: - resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + tsconfig@7.0.0: dependencies: '@types/strip-bom': 3.0.0 '@types/strip-json-comments': 0.0.30 strip-bom: 3.0.0 strip-json-comments: 2.0.1 - dev: true - /tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.6.3: {} - /tuf-js@1.1.7: - resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 debug: 4.3.5(supports-color@5.5.0) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true + type-detect@4.0.8: {} - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true + type-fest@0.21.3: {} - /type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - dev: true + type-fest@1.4.0: {} - /type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - dev: true + type-fest@2.19.0: {} - /typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + typedarray-to-buffer@3.1.5: dependencies: is-typedarray: 1.0.0 - dev: true - /typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} - engines: {node: '>=14.17'} - hasBin: true - dev: true + typescript@5.2.2: {} - /ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - dev: true + ufo@1.5.3: {} - /undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - dev: true + undefsafe@2.0.5: {} - /unique-filename@2.0.1: - resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-filename@2.0.1: dependencies: unique-slug: 3.0.0 - dev: true - /unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 - dev: true - /unique-slug@3.0.0: - resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-slug@3.0.0: dependencies: imurmurhash: 0.1.4 - dev: true - /unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-slug@4.0.0: dependencies: imurmurhash: 0.1.4 - dev: true - /unique-string@3.0.0: - resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} - engines: {node: '>=12'} + unique-string@3.0.0: dependencies: crypto-random-string: 4.0.0 - dev: true - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true + universalify@0.1.2: {} - /untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - dev: true + untildify@4.0.0: {} - /update-notifier@6.0.2: - resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} - engines: {node: '>=14.16'} + update-notifier@6.0.2: dependencies: boxen: 7.1.1 chalk: 5.3.0 @@ -5933,46 +6909,27 @@ packages: semver: 7.6.2 semver-diff: 4.0.0 xdg-basedir: 5.1.0 - dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - dev: false + uuid@9.0.1: {} - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true + v8-compile-cache-lib@3.0.1: {} - /validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - dev: true - /validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + validate-npm-package-name@5.0.1: {} - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false + vary@1.1.2: {} - /vite-node@0.34.6(@types/node@18.11.11): - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true + vite-node@0.34.6(@types/node@18.11.11): dependencies: cac: 6.7.14 debug: 4.3.5(supports-color@5.5.0) @@ -5989,15 +6946,8 @@ packages: - sugarss - supports-color - terser - dev: true - /vite-tsconfig-paths@4.3.2(typescript@5.2.2): - resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true + vite-tsconfig-paths@4.3.2(typescript@5.2.2): dependencies: debug: 4.3.5(supports-color@5.5.0) globrex: 0.1.2 @@ -6005,35 +6955,8 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: true - /vite@5.3.3(@types/node@18.11.11): - resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.3.3(@types/node@18.11.11): dependencies: '@types/node': 18.11.11 esbuild: 0.21.5 @@ -6041,38 +6964,8 @@ packages: rollup: 4.18.1 optionalDependencies: fsevents: 2.3.3 - dev: true - /vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true + vitest@0.34.6: dependencies: '@types/chai': 4.3.16 '@types/chai-subset': 1.3.5 @@ -6106,97 +6999,57 @@ packages: - sugarss - supports-color - terser - dev: true - /wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 - dev: true - /web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - dev: false + web-streams-polyfill@3.3.3: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} - /whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} + whatwg-url@13.0.0: dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 - dev: false - /which-pm@2.2.0: - resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} - engines: {node: '>=8.15'} + which-pm@2.2.0: dependencies: load-yaml-file: 0.2.0 path-exists: 4.0.0 - dev: true - /which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + which@1.3.1: dependencies: isexe: 2.0.0 - dev: true - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - dev: true - /which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + which@3.0.1: dependencies: isexe: 2.0.0 - dev: true - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - dev: true - /wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + wide-align@1.1.5: dependencies: string-width: 4.2.3 - dev: true - /widest-line@4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} + widest-line@4.0.1: dependencies: string-width: 5.1.2 - dev: true - /winston-transport@4.7.1: - resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} - engines: {node: '>= 12.0.0'} + winston-transport@4.7.1: dependencies: logform: 2.6.1 readable-stream: 3.6.2 triple-beam: 1.4.1 - dev: false - /winston@3.13.1: - resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} - engines: {node: '>= 12.0.0'} + winston@3.13.1: dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 @@ -6209,101 +7062,53 @@ packages: stack-trace: 0.0.10 triple-beam: 1.4.1 winston-transport: 4.7.1 - dev: false - /wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true + wrappy@1.0.2: {} - /write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + write-file-atomic@3.0.3: dependencies: imurmurhash: 0.1.4 is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - dev: true - /ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@8.18.0: {} - /xdg-basedir@5.1.0: - resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} - engines: {node: '>=12'} - dev: true + xdg-basedir@5.1.0: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: true + yallist@2.1.2: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true + yallist@4.0.0: {} - /yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} - engines: {node: '>= 14'} - hasBin: true - dev: false + yaml@2.4.5: {} - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - dev: true + yargs-parser@20.2.9: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true + yargs-parser@21.1.1: {} - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + yargs@16.2.0: dependencies: cliui: 7.0.4 escalade: 3.1.2 @@ -6312,11 +7117,8 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - dev: true - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -6325,35 +7127,13 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + yn@3.1.1: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@0.1.0: {} - /yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - dev: true + yocto-queue@1.1.1: {} - /zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - dev: false + yoctocolors-cjs@2.1.2: {} - github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: - resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} - name: pgwire - version: 0.7.0 - engines: {node: '>=14.18.0'} - dev: false + zod@3.23.8: {} From 299e5b91f2544ba2aa0582b92e8141d0b2f45e6f Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 16 Jul 2024 15:23:01 +0200 Subject: [PATCH 015/298] wip: move pg utils out of core --- modules/module-postgres/package.json | 3 + .../src/auth/SupabaseKeyCollector.ts | 23 ++++--- modules/module-postgres/src/types/types.ts | 8 +++ .../src/utils}/pgwire_utils.ts | 2 +- modules/module-postgres/src/utils/utils.ts | 55 ++++++++++++++++ packages/service-core/src/auth/auth-index.ts | 1 - packages/service-core/src/util/config.ts | 23 +------ .../util/config/compound-config-collector.ts | 29 +++------ .../service-core/src/util/config/types.ts | 5 +- .../src/util/populate_test_data.ts | 7 +-- packages/service-core/src/util/utils.ts | 55 ++-------------- pnpm-lock.yaml | 62 ++++++++++++++----- 12 files changed, 142 insertions(+), 131 deletions(-) rename {packages/service-core => modules/module-postgres}/src/auth/SupabaseKeyCollector.ts (68%) rename {packages/service-core/src/util => modules/module-postgres/src/utils}/pgwire_utils.ts (98%) create mode 100644 modules/module-postgres/src/utils/utils.ts diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index 2c01b3a60..8e11bc774 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -23,10 +23,13 @@ "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87", + "jose": "^4.15.1", "ts-codec": "^1.2.2", + "uuid": "^9.0.1", "uri-js": "^4.4.1" }, "devDependencies": { + "@types/uuid": "^9.0.4", "typescript": "^5.2.2", "vitest": "^0.34.6" } diff --git a/packages/service-core/src/auth/SupabaseKeyCollector.ts b/modules/module-postgres/src/auth/SupabaseKeyCollector.ts similarity index 68% rename from packages/service-core/src/auth/SupabaseKeyCollector.ts rename to modules/module-postgres/src/auth/SupabaseKeyCollector.ts index 559e0e7f9..cb59bc401 100644 --- a/packages/service-core/src/auth/SupabaseKeyCollector.ts +++ b/modules/module-postgres/src/auth/SupabaseKeyCollector.ts @@ -1,10 +1,9 @@ import * as jose from 'jose'; import * as pgwire from '@powersync/service-jpgwire'; -import { connectPgWirePool, pgwireRows } from '@powersync/service-jpgwire'; -import { KeyCollector } from './KeyCollector.js'; -import { KeyOptions, KeySpec } from './KeySpec.js'; -import { retriedQuery } from '../util/pgwire_utils.js'; -import { ResolvedConnection } from '../util/config/types.js'; +import { auth } from '@powersync/service-core'; + +import { ResolvedConnectionConfig } from '../types/types.js'; +import * as pgwire_utils from '../utils/pgwire_utils.js'; /** * Fetches key from the Supabase database. @@ -12,16 +11,16 @@ import { ResolvedConnection } from '../util/config/types.js'; * Unfortunately, despite the JWTs containing a kid, we have no way to lookup that kid * before receiving a valid token. */ -export class SupabaseKeyCollector implements KeyCollector { +export class SupabaseKeyCollector implements auth.KeyCollector { private pool: pgwire.PgClient; - private keyOptions: KeyOptions = { + private keyOptions: auth.KeyOptions = { requiresAudience: ['authenticated'], maxLifetimeSeconds: 86400 * 7 + 1200 // 1 week + 20 minutes margin }; - constructor(connection: ResolvedConnection) { - this.pool = connectPgWirePool(connection, { + constructor(connectionConfig: ResolvedConnectionConfig) { + this.pool = pgwire.connectPgWirePool(connectionConfig, { // To avoid overloading the source database with open connections, // limit to a single connection, and close the connection shortly // after using it. @@ -33,8 +32,8 @@ export class SupabaseKeyCollector implements KeyCollector { async getKeys() { let row: { jwt_secret: string }; try { - const rows = pgwireRows( - await retriedQuery(this.pool, `SELECT current_setting('app.settings.jwt_secret') as jwt_secret`) + const rows = pgwire.pgwireRows( + await pgwire_utils.retriedQuery(this.pool, `SELECT current_setting('app.settings.jwt_secret') as jwt_secret`) ); row = rows[0] as any; } catch (e) { @@ -57,7 +56,7 @@ export class SupabaseKeyCollector implements KeyCollector { // While the secret is valid base64, the base64-encoded form is the secret value. k: Buffer.from(secret, 'utf8').toString('base64url') }; - const imported = await KeySpec.importKey(key, this.keyOptions); + const imported = await auth.KeySpec.importKey(key, this.keyOptions); return { keys: [imported], errors: [] diff --git a/modules/module-postgres/src/types/types.ts b/modules/module-postgres/src/types/types.ts index 678295a91..8a5869071 100644 --- a/modules/module-postgres/src/types/types.ts +++ b/modules/module-postgres/src/types/types.ts @@ -54,8 +54,16 @@ export const PostgresConnectionConfig = service_types.configFile.dataSourceConfi }) ); +/** + * Config input specified when starting services + */ export type PostgresConnectionConfig = t.Decoded; +/** + * Resolved version of {@link PostgresConnectionConfig} + */ +export type ResolvedConnectionConfig = PostgresConnectionConfig & NormalizedPostgresConnectionConfig; + /** * Validate and normalize connection options. * diff --git a/packages/service-core/src/util/pgwire_utils.ts b/modules/module-postgres/src/utils/pgwire_utils.ts similarity index 98% rename from packages/service-core/src/util/pgwire_utils.ts rename to modules/module-postgres/src/utils/pgwire_utils.ts index 9aa042980..b0cb6b986 100644 --- a/packages/service-core/src/util/pgwire_utils.ts +++ b/modules/module-postgres/src/utils/pgwire_utils.ts @@ -5,8 +5,8 @@ import * as uuid from 'uuid'; import * as pgwire from '@powersync/service-jpgwire'; import { SqliteJsonValue, SqliteRow, ToastableSqliteRow, toSyncRulesRow } from '@powersync/service-sync-rules'; -import * as replication from '../replication/replication-index.js'; import { logger } from '@powersync/lib-services-framework'; +import { replication } from '@powersync/service-core'; /** * pgwire message -> SQLite row. diff --git a/modules/module-postgres/src/utils/utils.ts b/modules/module-postgres/src/utils/utils.ts new file mode 100644 index 000000000..486582a5e --- /dev/null +++ b/modules/module-postgres/src/utils/utils.ts @@ -0,0 +1,55 @@ +import * as pgwire from '@powersync/service-jpgwire'; + +import { storage, utils } from '@powersync/service-core'; +import { logger } from '@powersync/lib-services-framework'; + +import * as pgwire_utils from './pgwire_utils.js'; + +// TODO these should probably be on the API provider + +export async function getClientCheckpoint( + db: pgwire.PgClient, + bucketStorage: storage.BucketStorageFactory, + options?: { timeout?: number } +): Promise { + const start = Date.now(); + + const [{ lsn }] = pgwire.pgwireRows( + await db.query(`SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) + ); + + // This old API needs a persisted checkpoint id. + // Since we don't use LSNs anymore, the only way to get that is to wait. + + const timeout = options?.timeout ?? 50_000; + + logger.info(`Waiting for LSN checkpoint: ${lsn}`); + while (Date.now() - start < timeout) { + const cp = await bucketStorage.getActiveCheckpoint(); + if (!cp.hasSyncRules()) { + throw new Error('No sync rules available'); + } + if (cp.lsn >= lsn) { + logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`); + return cp.checkpoint; + } + + await new Promise((resolve) => setTimeout(resolve, 30)); + } + + throw new Error('Timeout while waiting for checkpoint'); +} + +export async function createWriteCheckpoint( + db: pgwire.PgClient, + bucketStorage: storage.BucketStorageFactory, + user_id: string +): Promise { + const [{ lsn }] = pgwire.pgwireRows( + await pgwire_utils.retriedQuery(db, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) + ); + + const id = await bucketStorage.createWriteCheckpoint(user_id, { '1': lsn }); + logger.info(`Write checkpoint 2: ${JSON.stringify({ lsn, id: String(id) })}`); + return id; +} diff --git a/packages/service-core/src/auth/auth-index.ts b/packages/service-core/src/auth/auth-index.ts index efb35acfa..dae123d1a 100644 --- a/packages/service-core/src/auth/auth-index.ts +++ b/packages/service-core/src/auth/auth-index.ts @@ -7,4 +7,3 @@ export * from './KeyStore.js'; export * from './LeakyBucket.js'; export * from './RemoteJWKSCollector.js'; export * from './StaticKeyCollector.js'; -export * from './SupabaseKeyCollector.js'; diff --git a/packages/service-core/src/util/config.ts b/packages/service-core/src/util/config.ts index f11e798a3..36aa003d4 100644 --- a/packages/service-core/src/util/config.ts +++ b/packages/service-core/src/util/config.ts @@ -1,29 +1,8 @@ import * as fs from 'fs/promises'; -import { baseUri } from '@powersync/service-types'; -import { ResolvedConnection, ResolvedPowerSyncConfig, RunnerConfig } from './config/types.js'; +import { ResolvedPowerSyncConfig, RunnerConfig } from './config/types.js'; import { CompoundConfigCollector } from './config/compound-config-collector.js'; -/** - * Build a single URI from full postgres credentials. - */ -export function buildDemoPgUri(options: ResolvedConnection): string { - if (!options.debug_api) { - throw new Error('Not supported'); - } - - const uri = new URL(baseUri(options)); - uri.username = options.username; - uri.password = options.password; - if (options.sslmode != 'disable') { - // verify-full is tricky to actually use on a client, since they won't have the cert - // Just use "require" by default - // uri.searchParams.set('sslmode', options.sslmode); - uri.searchParams.set('sslmode', 'require'); - } - return uri.toString(); -} - export function loadConfig(runnerConfig: RunnerConfig = {}) { const collector = new CompoundConfigCollector(); return collector.collectConfig(runnerConfig); diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 45073b26a..2bcde30b9 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -1,6 +1,6 @@ -import { configFile, normalizeConnection } from '@powersync/service-types'; +import { configFile } from '@powersync/service-types'; import { ConfigCollector } from './collectors/config-collector.js'; -import { ResolvedConnection, ResolvedPowerSyncConfig, RunnerConfig, SyncRulesConfig } from './types.js'; +import { ResolvedPowerSyncConfig, RunnerConfig, SyncRulesConfig } from './types.js'; import * as auth from '../../auth/auth-index.js'; import { SyncRulesCollector } from './sync-rules/sync-collector.js'; import { Base64ConfigCollector } from './collectors/impl/base64-config-collector.js'; @@ -46,21 +46,11 @@ export class CompoundConfigCollector { async collectConfig(runner_config: RunnerConfig = {}): Promise { const baseConfig = await this.collectBaseConfig(runner_config); - const connections = baseConfig.replication?.connections ?? []; - if (connections.length > 1) { - throw new Error('Only a single replication connection is supported currently'); + const dataSources = baseConfig.replication?.data_sources ?? []; + if (dataSources.length > 1) { + throw new Error('Only a single replication data source is supported currently'); } - const mapped = connections.map((c) => { - const conf: ResolvedConnection = { - type: 'postgresql' as const, - ...normalizeConnection(c), - debug_api: c.debug_api ?? false - }; - - return conf; - }); - const collectors = new auth.CompoundKeyCollector(); const keyStore = new auth.KeyStore(collectors); @@ -69,10 +59,6 @@ export class CompoundConfigCollector { collectors.add(staticCollector); - if (baseConfig.client_auth?.supabase && mapped.length > 0) { - collectors.add(new auth.CachedKeyCollector(new auth.SupabaseKeyCollector(mapped[0]))); - } - let jwks_uris = baseConfig.client_auth?.jwks_uri ?? []; if (typeof jwks_uris == 'string') { jwks_uris = [jwks_uris]; @@ -98,7 +84,6 @@ export class CompoundConfigCollector { let jwt_audiences: string[] = baseConfig.client_auth?.audience ?? []; let config: ResolvedPowerSyncConfig = { - connection: mapped[0], data_sources: baseConfig.replication?.data_sources || [], storage: baseConfig.storage, client_keystore: keyStore, @@ -125,7 +110,9 @@ export class CompoundConfigCollector { internal_service_endpoint: baseConfig.telemetry?.internal_service_endpoint ?? 'https://pulse.journeyapps.com/v1/metrics' }, - slot_name_prefix: connections[0]?.slot_name_prefix ?? 'powersync_' + // TODO maybe move this out of the connection or something + // slot_name_prefix: connections[0]?.slot_name_prefix ?? 'powersync_' + slot_name_prefix: 'powersync_' }; return config; } diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index 52f34c7ef..22e5428fb 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -1,4 +1,4 @@ -import { NormalizedPostgresConnection, configFile } from '@powersync/service-types'; +import { configFile } from '@powersync/service-types'; import { KeySpec } from '../../auth/KeySpec.js'; import { KeyStore } from '../../auth/KeyStore.js'; @@ -20,8 +20,6 @@ export type MigrationContext = { export type Runner = (config: RunnerConfig) => Promise; -export type ResolvedConnection = configFile.PostgresConnection & NormalizedPostgresConnection; - export type SyncRulesConfig = { present: boolean; content?: string; @@ -29,7 +27,6 @@ export type SyncRulesConfig = { }; export type ResolvedPowerSyncConfig = { - connection?: ResolvedConnection; data_sources?: configFile.DataSourceConfig[]; storage: configFile.StorageConfig; dev: { diff --git a/packages/service-core/src/util/populate_test_data.ts b/packages/service-core/src/util/populate_test_data.ts index f53648831..be4c67729 100644 --- a/packages/service-core/src/util/populate_test_data.ts +++ b/packages/service-core/src/util/populate_test_data.ts @@ -1,13 +1,12 @@ import * as crypto from 'crypto'; import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads'; -import { connectPgWire } from '@powersync/service-jpgwire'; -import { NormalizedPostgresConnection } from '@powersync/service-types'; +import * as pgwire from '@powersync/service-jpgwire'; // This util is actually for tests only, but we need it compiled to JS for the service to work, so it's placed in the service. export interface PopulateDataOptions { - connection: NormalizedPostgresConnection; + connection: pgwire.NormalizedConnectionConfig; num_transactions: number; per_transaction: number; size: number; @@ -32,7 +31,7 @@ if (isMainThread || parentPort == null) { async function populateDataInner(options: PopulateDataOptions) { // Dedicated connection so we can release the memory easily - const initialDb = await connectPgWire(options.connection, { type: 'standard' }); + const initialDb = await pgwire.connectPgWire(options.connection, { type: 'standard' }); const largeDescription = crypto.randomBytes(options.size / 2).toString('hex'); let operation_count = 0; for (let i = 0; i < options.num_transactions; i++) { diff --git a/packages/service-core/src/util/utils.ts b/packages/service-core/src/util/utils.ts index 5e45fed5b..4d2b7f1ff 100644 --- a/packages/service-core/src/util/utils.ts +++ b/packages/service-core/src/util/utils.ts @@ -1,14 +1,12 @@ import crypto from 'crypto'; -import * as pgwire from '@powersync/service-jpgwire'; -import { pgwireRows } from '@powersync/service-jpgwire'; - -import * as storage from '../storage/storage-index.js'; import { BucketChecksum, OpId } from './protocol-types.js'; -import { retriedQuery } from './pgwire_utils.js'; -import { logger } from '@powersync/lib-services-framework'; export type ChecksumMap = Map; +export function escapeIdentifier(identifier: string) { + return `"${identifier.replace(/"/g, '""').replace(/\./g, '"."')}"`; +} + export function hashData(type: string, id: string, data: string): number { const hash = crypto.createHash('sha256'); hash.update(`put.${type}.${id}.${data}`); @@ -75,48 +73,3 @@ export function addBucketChecksums(a: BucketChecksum, b: BucketChecksum | null): }; } } - -export async function getClientCheckpoint( - db: pgwire.PgClient, - bucketStorage: storage.BucketStorageFactory, - options?: { timeout?: number } -): Promise { - const start = Date.now(); - - const [{ lsn }] = pgwireRows(await db.query(`SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`)); - - // This old API needs a persisted checkpoint id. - // Since we don't use LSNs anymore, the only way to get that is to wait. - - const timeout = options?.timeout ?? 50_000; - - logger.info(`Waiting for LSN checkpoint: ${lsn}`); - while (Date.now() - start < timeout) { - const cp = await bucketStorage.getActiveCheckpoint(); - if (!cp.hasSyncRules()) { - throw new Error('No sync rules available'); - } - if (cp.lsn >= lsn) { - logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`); - return cp.checkpoint; - } - - await new Promise((resolve) => setTimeout(resolve, 30)); - } - - throw new Error('Timeout while waiting for checkpoint'); -} - -export async function createWriteCheckpoint( - db: pgwire.PgClient, - bucketStorage: storage.BucketStorageFactory, - user_id: string -): Promise { - const [{ lsn }] = pgwireRows( - await retriedQuery(db, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) - ); - - const id = await bucketStorage.createWriteCheckpoint(user_id, { '1': lsn }); - logger.info(`Write checkpoint 2: ${JSON.stringify({ lsn, id: String(id) })}`); - return id; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1542f431b..d55af25d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,6 +108,9 @@ importers: '@powersync/service-types': specifier: workspace:* version: link:../../packages/types + jose: + specifier: ^4.15.1 + version: 4.15.9 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 @@ -117,7 +120,13 @@ importers: uri-js: specifier: ^4.4.1 version: 4.4.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 devDependencies: + '@types/uuid': + specifier: ^9.0.4 + version: 9.0.8 typescript: specifier: ^5.2.2 version: 5.2.2 @@ -4080,6 +4089,11 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.24.1 + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0)': + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4261,6 +4275,18 @@ snapshots: - supports-color optional: true + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0)': + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 + require-in-the-middle: 7.3.0 + semver: 7.6.2 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4326,6 +4352,12 @@ snapshots: '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0)': + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4380,6 +4412,13 @@ snapshots: '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0)': + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4387,13 +4426,6 @@ snapshots: '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/semantic-conventions@1.17.0': {} '@opentelemetry/semantic-conventions@1.24.1': {} @@ -4485,8 +4517,8 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) @@ -4503,11 +4535,11 @@ snapshots: '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4515,12 +4547,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.25.1 '@sentry/core': 8.17.0 '@sentry/types': 8.17.0 From 3b297da6e49195d691414fca82d53e3b55450aa8 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 16 Jul 2024 15:31:50 +0200 Subject: [PATCH 016/298] move pg relation and utils --- .../module-postgres}/src/replication/PgRelation.ts | 0 .../src/replication/replication-utils.ts | 9 +++++---- packages/service-core/src/api/SyncAPI.ts | 10 ++++++++++ packages/service-core/src/util/util-index.ts | 2 -- 4 files changed, 15 insertions(+), 6 deletions(-) rename {packages/service-core => modules/module-postgres}/src/replication/PgRelation.ts (100%) rename packages/service-core/src/replication/util.ts => modules/module-postgres/src/replication/replication-utils.ts (92%) diff --git a/packages/service-core/src/replication/PgRelation.ts b/modules/module-postgres/src/replication/PgRelation.ts similarity index 100% rename from packages/service-core/src/replication/PgRelation.ts rename to modules/module-postgres/src/replication/PgRelation.ts diff --git a/packages/service-core/src/replication/util.ts b/modules/module-postgres/src/replication/replication-utils.ts similarity index 92% rename from packages/service-core/src/replication/util.ts rename to modules/module-postgres/src/replication/replication-utils.ts index 8c719205a..2f4867d56 100644 --- a/packages/service-core/src/replication/util.ts +++ b/modules/module-postgres/src/replication/replication-utils.ts @@ -1,6 +1,7 @@ import * as pgwire from '@powersync/service-jpgwire'; -import * as util from '../util/util-index.js'; +import * as pgwire_utils from '../utils/pgwire_utils.js'; + import { ReplicationColumn, ReplicationIdentity } from './PgRelation.js'; export interface ReplicaIdentityResult { @@ -14,7 +15,7 @@ export async function getPrimaryKeyColumns( mode: 'primary' | 'replident' ): Promise { const indexFlag = mode == 'primary' ? `i.indisprimary` : `i.indisreplident`; - const attrRows = await util.retriedQuery(db, { + const attrRows = await pgwire_utils.retriedQuery(db, { statement: `SELECT a.attname as name, a.atttypid as typeid, a.attnum as attnum FROM pg_index i JOIN pg_attribute a @@ -33,7 +34,7 @@ export async function getPrimaryKeyColumns( } export async function getAllColumns(db: pgwire.PgClient, relationId: number): Promise { - const attrRows = await util.retriedQuery(db, { + const attrRows = await pgwire_utils.retriedQuery(db, { statement: `SELECT a.attname as name, a.atttypid as typeid, a.attnum as attnum FROM pg_attribute a WHERE a.attrelid = $1::oid @@ -50,7 +51,7 @@ export async function getReplicationIdentityColumns( db: pgwire.PgClient, relationId: number ): Promise { - const rows = await util.retriedQuery(db, { + const rows = await pgwire_utils.retriedQuery(db, { statement: `SELECT CASE relreplident WHEN 'd' THEN 'default' WHEN 'n' THEN 'nothing' diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index 68f9ae7d4..52e4467c1 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -50,9 +50,19 @@ export interface SyncAPI { */ executeQuery(query: string, params: any[]): Promise; + /** + * The management service and SDK expose a demo credentials endpoint. + * Not sure if this is actually used. + */ + getDemoCredentials(): Promise; + //CRUD API : I don't think this is used besides maybe niche dev use cases } +export interface DemoCredentials { + url: string; +} + // TODO: Export this when the existing definition in WALConnection is removed interface PatternResult { schema: string; diff --git a/packages/service-core/src/util/util-index.ts b/packages/service-core/src/util/util-index.ts index c726fd99a..e1a556d32 100644 --- a/packages/service-core/src/util/util-index.ts +++ b/packages/service-core/src/util/util-index.ts @@ -3,8 +3,6 @@ export * from './env.js'; export * from './memory-tracking.js'; export * from './migration_lib.js'; export * from './Mutex.js'; -export * from './PgManager.js'; -export * from './pgwire_utils.js'; export * from './populate_test_data.js'; export * from './protocol-types.js'; export * from './secs.js'; From 6169a74814e1ba63b7ea1f828567e2bb54f6cfaf Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 16 Jul 2024 16:55:17 +0200 Subject: [PATCH 017/298] move diagnostics api --- .../src/module/PostgresModule.ts | 16 +++- .../replication/PostgresReplicationAdapter.ts | 14 +-- .../src/replication/PostgresSyncAPIAdapter.ts | 80 +++++++++++++++-- .../src/replication/replication-utils.ts | 89 +++++++++++++++++++ .../module-postgres/src/utils/pgwire_utils.ts | 45 +--------- packages/service-core/src/api/SyncAPI.ts | 15 ++-- packages/service-core/src/api/diagnostics.ts | 88 ++++-------------- .../src/replication/WalConnection.ts | 43 --------- .../src/routes/endpoints/admin.ts | 61 ++++--------- packages/service-core/src/routes/router.ts | 2 + packages/types/src/config/PowerSyncConfig.ts | 4 +- 11 files changed, 231 insertions(+), 226 deletions(-) diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index fe1666944..ec1a9dab3 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -2,7 +2,7 @@ import * as t from 'ts-codec'; import { api, replication } from '@powersync/service-core'; -import { PostgresConnectionConfig } from '../types/types.js'; +import { normalizeConnectionConfig, PostgresConnectionConfig, ResolvedConnectionConfig } from '../types/types.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; import { PostgresSyncAPIAdapter } from '../replication/PostgresSyncAPIAdapter.js'; @@ -13,11 +13,21 @@ export class PostgresModule extends replication.ReplicationModule { } protected createSyncAPIAdapter(config: PostgresConnectionConfig): api.SyncAPI { - throw new PostgresSyncAPIAdapter(config); + throw new PostgresSyncAPIAdapter(this.resolveConfig(config)); } protected createReplicationAdapter(config: PostgresConnectionConfig): PostgresReplicationAdapter { - return new PostgresReplicationAdapter(config); + return new PostgresReplicationAdapter(this.resolveConfig(config)); + } + + /** + * Combines base config with normalized connection settings + */ + private resolveConfig(config: PostgresConnectionConfig): ResolvedConnectionConfig { + return { + ...config, + ...normalizeConnectionConfig(config) + }; } public shutdown(): Promise { diff --git a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts index 90a4d8d2d..99f96f1e9 100644 --- a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts @@ -2,25 +2,17 @@ import { replication, storage } from '@powersync/service-core'; import * as sync_rules from '@powersync/service-sync-rules'; import { PostgresConnection, PostgresConnectionManager } from '../connection/PostgresConnectionManager.js'; -import { - normalizeConnectionConfig, - NormalizedPostgresConnectionConfig, - PostgresConnectionConfig -} from '../types/types.js'; +import { ResolvedConnectionConfig } from '../types/types.js'; export class PostgresReplicationAdapter implements replication.ReplicationAdapter { - protected normalizedConfig: NormalizedPostgresConnectionConfig; - - constructor(config: PostgresConnectionConfig) { - this.normalizedConfig = normalizeConnectionConfig(config); - } + constructor(protected config: ResolvedConnectionConfig) {} name(): string { return 'postgres'; } createConnectionManager(): PostgresConnectionManager { - return new PostgresConnectionManager(this.normalizedConfig); + return new PostgresConnectionManager(this.config); } validateConfiguration(connection: PostgresConnection): void { diff --git a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts index c9e1dd3fb..9b0362f8f 100644 --- a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts @@ -1,15 +1,71 @@ +import * as pgwire from '@powersync/service-jpgwire'; import { api, replication } from '@powersync/service-core'; import { TablePattern } from '@powersync/service-sync-rules'; -import { DatabaseSchema } from '@powersync/service-types'; +import { DatabaseSchema, internal_routes } from '@powersync/service-types'; // TODO import { ExecuteSqlResponse } from '@powersync/service-types/src/routes.js'; -import { PostgresConnectionConfig } from '../types/types.js'; +import * as pg_utils from '../utils/pgwire_utils.js'; +import * as replication_utils from '../replication/replication-utils.js'; +import { baseUri, ResolvedConnectionConfig } from '../types/types.js'; +import { DataSourceConfig } from '@powersync/service-types/src/config/PowerSyncConfig.js'; export class PostgresSyncAPIAdapter implements api.SyncAPI { - constructor(protected config: PostgresConnectionConfig) {} + // TODO manage lifecycle of this + protected pool: pgwire.PgClient; + + constructor(protected config: ResolvedConnectionConfig) { + this.pool = pgwire.connectPgWirePool(config, { + idleTimeout: 30_000 + }); + } + + async getSourceConfig(): Promise { + return this.config; + } + + async getConnectionStatus(): Promise { + const base = { + id: this.config.id, + postgres_uri: baseUri(this.config) + }; + + try { + await pg_utils.retriedQuery(this.pool, `SELECT 'PowerSync connection test'`); + } catch (e) { + return { + ...base, + connected: false, + errors: [{ level: 'fatal', message: e.message }] + }; + } + + try { + await replication_utils.checkSourceConfiguration(this.pool); + } catch (e) { + return { + ...base, + connected: true, + errors: [{ level: 'fatal', message: e.message }] + }; + } + + return { + ...base, + connected: true, + errors: [] + }; + } + + executeQuery(query: string, params: any[]): Promise { + throw new Error('Method not implemented.'); + } + + getDemoCredentials(): Promise { + throw new Error('Method not implemented.'); + } getDiagnostics(): Promise<{ connected: boolean; errors?: Array<{ level: string; message: string }> }> { throw new Error('Method not implemented.'); @@ -19,8 +75,22 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { throw new Error('Method not implemented.'); } - getReplicationLag(): Promise { - throw new Error('Method not implemented.'); + async getReplicationLag(slotName: string): Promise { + const results = await pg_utils.retriedQuery(this.pool, { + statement: `SELECT + slot_name, + confirmed_flush_lsn, + pg_current_wal_lsn(), + (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance +FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, + params: [{ type: 'varchar', value: slotName }] + }); + const [row] = pgwire.pgwireRows(results); + if (row) { + return Number(row.lsn_distance); + } + + throw new Error(`Could not determine replication lag for slot ${slotName}`); } getCheckpoint(): Promise { diff --git a/modules/module-postgres/src/replication/replication-utils.ts b/modules/module-postgres/src/replication/replication-utils.ts index 2f4867d56..9d430df9d 100644 --- a/modules/module-postgres/src/replication/replication-utils.ts +++ b/modules/module-postgres/src/replication/replication-utils.ts @@ -1,5 +1,10 @@ +import bson from 'bson'; +import * as uuid from 'uuid'; + import * as pgwire from '@powersync/service-jpgwire'; +import * as sync_rules from '@powersync/service-sync-rules'; + import * as pgwire_utils from '../utils/pgwire_utils.js'; import { ReplicationColumn, ReplicationIdentity } from './PgRelation.js'; @@ -75,3 +80,87 @@ WHERE oid = $1::oid LIMIT 1`, return { replicationIdentity: 'nothing', columns: [] }; } } + +export async function checkSourceConfiguration(db: pgwire.PgClient) { + // TODO: configurable + const publication_name = 'powersync'; + + // Check basic config + await pgwire_utils.retriedQuery( + db, + `DO $$ +BEGIN +if current_setting('wal_level') is distinct from 'logical' then +raise exception 'wal_level must be set to ''logical'', your database has it set to ''%''. Please edit your config file and restart PostgreSQL.', current_setting('wal_level'); +end if; +if (current_setting('max_replication_slots')::int >= 1) is not true then +raise exception 'Your max_replication_slots setting is too low, it must be greater than 1. Please edit your config file and restart PostgreSQL.'; +end if; +if (current_setting('max_wal_senders')::int >= 1) is not true then +raise exception 'Your max_wal_senders setting is too low, it must be greater than 1. Please edit your config file and restart PostgreSQL.'; +end if; +end; +$$ LANGUAGE plpgsql;` + ); + + // Check that publication exists + const rs = await pgwire_utils.retriedQuery(db, { + statement: `SELECT * FROM pg_publication WHERE pubname = $1`, + params: [{ type: 'varchar', value: publication_name }] + }); + const row = pgwire.pgwireRows(rs)[0]; + if (row == null) { + throw new Error( + `Publication '${publication_name}' does not exist. Run: \`CREATE PUBLICATION ${publication_name} FOR ALL TABLES\`, or read the documentation for details.` + ); + } + if (row.pubinsert == false || row.pubupdate == false || row.pubdelete == false || row.pubtruncate == false) { + throw new Error( + `Publication '${publication_name}' does not publish all changes. Create a publication using \`WITH (publish = "insert, update, delete, truncate")\` (the default).` + ); + } + if (row.pubviaroot) { + throw new Error(`'${publication_name}' uses publish_via_partition_root, which is not supported.`); + } +} + +function getRawReplicaIdentity( + tuple: sync_rules.ToastableSqliteRow, + columns: ReplicationColumn[] +): Record { + let result: Record = {}; + for (let column of columns) { + const name = column.name; + result[name] = tuple[name]; + } + return result; +} + +export function getUuidReplicaIdentityString( + tuple: sync_rules.ToastableSqliteRow, + columns: ReplicationColumn[] +): string { + const rawIdentity = getRawReplicaIdentity(tuple, columns); + + return uuidForRow(rawIdentity); +} + +export function uuidForRow(row: sync_rules.SqliteRow): string { + // Important: This must not change, since it will affect how ids are generated. + // Use BSON so that it's a well-defined format without encoding ambiguities. + const repr = bson.serialize(row); + return uuid.v5(repr, pgwire_utils.ID_NAMESPACE); +} + +export function getUuidReplicaIdentityBson( + tuple: sync_rules.ToastableSqliteRow, + columns: ReplicationColumn[] +): bson.UUID { + if (columns.length == 0) { + // REPLICA IDENTITY NOTHING - generate random id + return new bson.UUID(uuid.v4()); + } + const rawIdentity = getRawReplicaIdentity(tuple, columns); + + return pgwire_utils.uuidForRowBson(rawIdentity); +} diff --git a/modules/module-postgres/src/utils/pgwire_utils.ts b/modules/module-postgres/src/utils/pgwire_utils.ts index b0cb6b986..c65b419de 100644 --- a/modules/module-postgres/src/utils/pgwire_utils.ts +++ b/modules/module-postgres/src/utils/pgwire_utils.ts @@ -6,7 +6,8 @@ import * as pgwire from '@powersync/service-jpgwire'; import { SqliteJsonValue, SqliteRow, ToastableSqliteRow, toSyncRulesRow } from '@powersync/service-sync-rules'; import { logger } from '@powersync/lib-services-framework'; -import { replication } from '@powersync/service-core'; + +export const ID_NAMESPACE = 'a396dd91-09fc-4017-a28d-3df722f651e9'; /** * pgwire message -> SQLite row. @@ -45,48 +46,6 @@ export function constructBeforeRecord(message: pgwire.PgoutputDelete | pgwire.Pg return toSyncRulesRow(record); } -function getRawReplicaIdentity( - tuple: ToastableSqliteRow, - columns: replication.ReplicationColumn[] -): Record { - let result: Record = {}; - for (let column of columns) { - const name = column.name; - result[name] = tuple[name]; - } - return result; -} -const ID_NAMESPACE = 'a396dd91-09fc-4017-a28d-3df722f651e9'; - -export function getUuidReplicaIdentityString( - tuple: ToastableSqliteRow, - columns: replication.ReplicationColumn[] -): string { - const rawIdentity = getRawReplicaIdentity(tuple, columns); - - return uuidForRow(rawIdentity); -} - -export function uuidForRow(row: SqliteRow): string { - // Important: This must not change, since it will affect how ids are generated. - // Use BSON so that it's a well-defined format without encoding ambiguities. - const repr = bson.serialize(row); - return uuid.v5(repr, ID_NAMESPACE); -} - -export function getUuidReplicaIdentityBson( - tuple: ToastableSqliteRow, - columns: replication.ReplicationColumn[] -): bson.UUID { - if (columns.length == 0) { - // REPLICA IDENTITY NOTHING - generate random id - return new bson.UUID(uuid.v4()); - } - const rawIdentity = getRawReplicaIdentity(tuple, columns); - - return uuidForRowBson(rawIdentity); -} - export function uuidForRowBson(row: SqliteRow): bson.UUID { // Important: This must not change, since it will affect how ids are generated. // Use BSON so that it's a well-defined format without encoding ambiguities. diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index 52e4467c1..799d8e0a6 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -5,11 +5,16 @@ import * as types from '@powersync/service-types'; * Describes all the methods currently required to service the sync API endpoints. */ export interface SyncAPI { + /** + * @returns basic identification of the connection + */ + getSourceConfig(): Promise; + /** * Checks the current connection status of the datasource. * This is usually some test query to verify the source can be reached. */ - getConnectionStatus(): Promise; + getConnectionStatus(): Promise; /** * Generates replication table information from a given pattern of tables. @@ -28,7 +33,7 @@ export interface SyncAPI { * @returns The replication lag: that is the amount of data which has not been * replicated yet, in bytes. */ - getReplicationLag(): Promise; + getReplicationLag(slotName: string): Promise; /** * Get the current LSN or equivalent replication position identifier @@ -48,7 +53,7 @@ export interface SyncAPI { * Executes a query and return the result from the data source. This is currently used in the * admin API which is exposed in Collide. */ - executeQuery(query: string, params: any[]): Promise; + executeQuery(query: string, params: any[]): Promise; /** * The management service and SDK expose a demo credentials endpoint. @@ -76,12 +81,12 @@ interface ErrorDescription { level: string; message: string; } -interface StatusResponse { +export interface ConnectionStatusResponse { connected: boolean; errors?: ErrorDescription[]; } -interface QueryResults { +export interface QueryResults { columns: string[]; rows: (string | number | boolean | null)[][]; } diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 46a7cde98..a9b1eb697 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -1,51 +1,9 @@ import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/service-sync-rules'; -import { pgwireRows } from '@powersync/service-jpgwire'; -import { ConnectionStatus, SyncRulesStatus, TableInfo, baseUri } from '@powersync/service-types'; +import { SyncRulesStatus, TableInfo } from '@powersync/service-types'; -import * as replication from '../replication/replication-index.js'; -import * as storage from '../storage/storage-index.js'; -import * as util from '../util/util-index.js'; - -import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; import { logger } from '@powersync/lib-services-framework'; - -export async function getConnectionStatus(system: CorePowerSyncSystem): Promise { - if (system.pgwire_pool == null) { - return null; - } - - const pool = system.requirePgPool(); - - const base = { - id: system.config.connection!.id, - postgres_uri: baseUri(system.config.connection!) - }; - try { - await util.retriedQuery(pool, `SELECT 'PowerSync connection test'`); - } catch (e) { - return { - ...base, - connected: false, - errors: [{ level: 'fatal', message: e.message }] - }; - } - - try { - await replication.checkSourceConfiguration(pool); - } catch (e) { - return { - ...base, - connected: true, - errors: [{ level: 'fatal', message: e.message }] - }; - } - - return { - ...base, - connected: true, - errors: [] - }; -} +import { ServiceContext } from '../system/ServiceContext.js'; +import * as storage from '../storage/storage-index.js'; export interface DiagnosticsOptions { /** @@ -66,9 +24,11 @@ export interface DiagnosticsOptions { check_connection: boolean; } +export const DEFAULT_DATASOURCE_ID = 'default'; + export async function getSyncRulesStatus( sync_rules: storage.PersistedSyncRulesContent | null, - system: CorePowerSyncSystem, + service_context: ServiceContext, options: DiagnosticsOptions ): Promise { if (sync_rules == null) { @@ -92,21 +52,21 @@ export async function getSyncRulesStatus( }; } - const systemStorage = live_status ? await system.storage.getInstance(persisted) : undefined; + const { storage } = service_context; + const syncAPI = service_context.syncAPIProvider.getSyncAPI(); + + const systemStorage = live_status ? await storage.getInstance(persisted) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; let tables_flat: TableInfo[] = []; - if (check_connection) { - const pool = system.requirePgPool(); + const sourceConfig = await syncAPI.getSourceConfig(); + const tag = sourceConfig.tag ?? DEFAULT_TAG; + if (check_connection) { const source_table_patterns = rules.getSourceTables(); - const wc = new replication.WalConnection({ - db: pool, - sync_rules: rules - }); - const resolved_tables = await wc.getDebugTablesInfo(source_table_patterns); + const resolved_tables = await syncAPI.getDebugTablesInfo(source_table_patterns); tables_flat = resolved_tables.flatMap((info) => { if (info.table) { return [info.table]; @@ -119,19 +79,7 @@ export async function getSyncRulesStatus( if (systemStorage) { try { - const results = await util.retriedQuery(pool, { - statement: `SELECT - slot_name, - confirmed_flush_lsn, - pg_current_wal_lsn(), - (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance - FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, - params: [{ type: 'varchar', value: systemStorage!.slot_name }] - }); - const [row] = pgwireRows(results); - if (row) { - replication_lag_bytes = Number(row.lsn_distance); - } + replication_lag_bytes = await syncAPI.getReplicationLag(systemStorage.slot_name); } catch (e) { // Ignore logger.warn(`Unable to get replication lag`, e); @@ -139,7 +87,6 @@ export async function getSyncRulesStatus( } } else { const source_table_patterns = rules.getSourceTables(); - const tag = system.config.connection!.tag ?? DEFAULT_TAG; tables_flat = source_table_patterns.map((pattern): TableInfo => { if (pattern.isWildcard) { @@ -186,12 +133,13 @@ export async function getSyncRulesStatus( }) ); + return { content: include_content ? sync_rules.sync_rules_content : undefined, connections: [ { - id: system.config.connection!.id, - tag: system.config.connection!.tag ?? DEFAULT_TAG, + id: sourceConfig.id ?? DEFAULT_DATASOURCE_ID, + tag: sourceConfig.tag ?? DEFAULT_TAG, slot_name: sync_rules.slot_name, initial_replication_done: status?.snapshot_done ?? false, // TODO: Rename? diff --git a/packages/service-core/src/replication/WalConnection.ts b/packages/service-core/src/replication/WalConnection.ts index 5fa3cf8a7..559baad22 100644 --- a/packages/service-core/src/replication/WalConnection.ts +++ b/packages/service-core/src/replication/WalConnection.ts @@ -182,46 +182,3 @@ export interface PatternResult { tables?: TableInfo[]; table?: TableInfo; } - -export async function checkSourceConfiguration(db: pgwire.PgClient) { - // TODO: configurable - const publication_name = 'powersync'; - - // Check basic config - await util.retriedQuery( - db, - `DO $$ -BEGIN -if current_setting('wal_level') is distinct from 'logical' then -raise exception 'wal_level must be set to ''logical'', your database has it set to ''%''. Please edit your config file and restart PostgreSQL.', current_setting('wal_level'); -end if; -if (current_setting('max_replication_slots')::int >= 1) is not true then -raise exception 'Your max_replication_slots setting is too low, it must be greater than 1. Please edit your config file and restart PostgreSQL.'; -end if; -if (current_setting('max_wal_senders')::int >= 1) is not true then -raise exception 'Your max_wal_senders setting is too low, it must be greater than 1. Please edit your config file and restart PostgreSQL.'; -end if; -end; -$$ LANGUAGE plpgsql;` - ); - - // Check that publication exists - const rs = await util.retriedQuery(db, { - statement: `SELECT * FROM pg_publication WHERE pubname = $1`, - params: [{ type: 'varchar', value: publication_name }] - }); - const row = pgwireRows(rs)[0]; - if (row == null) { - throw new Error( - `Publication '${publication_name}' does not exist. Run: \`CREATE PUBLICATION ${publication_name} FOR ALL TABLES\`, or read the documentation for details.` - ); - } - if (row.pubinsert == false || row.pubupdate == false || row.pubdelete == false || row.pubtruncate == false) { - throw new Error( - `Publication '${publication_name}' does not publish all changes. Create a publication using \`WITH (publish = "insert, update, delete, truncate")\` (the default).` - ); - } - if (row.pubviaroot) { - throw new Error(`'${publication_name}' uses publish_via_partition_root, which is not supported.`); - } -} diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index 616c47a02..6dd34fe61 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -1,5 +1,5 @@ import { errors, router, schema } from '@powersync/lib-services-framework'; -import { SqlSyncRules, SqliteValue, StaticSchema, isJsonValue, toSyncRulesValue } from '@powersync/service-sync-rules'; +import { SqlSyncRules, SqliteValue, StaticSchema, isJsonValue } from '@powersync/service-sync-rules'; import { internal_routes } from '@powersync/service-types'; import * as api from '../../api/api-index.js'; @@ -37,47 +37,15 @@ export const executeSql = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.ExecuteSqlRequest, { allowAdditional: true }), handler: async (payload) => { - const connection = payload.context.system.config.connection; - if (connection == null || !connection.debug_api) { - return internal_routes.ExecuteSqlResponse.encode({ - results: { - columns: [], - rows: [] - }, - success: false, - error: 'SQL querying is not enabled' - }); - } - - const pool = payload.context.system.requirePgPool(); - - const { query, args } = payload.params.sql; - - try { - const result = await pool.query({ - statement: query, - params: args.map(util.autoParameter) - }); - - return internal_routes.ExecuteSqlResponse.encode({ - success: true, - results: { - columns: result.columns.map((c) => c.name), - rows: result.rows.map((row) => { - return row.map((value) => mapColumnValue(toSyncRulesValue(value))); - }) - } - }); - } catch (e) { - return internal_routes.ExecuteSqlResponse.encode({ - results: { - columns: [], - rows: [] - }, - success: false, - error: e.message - }); - } + const { + params: { + sql: { query, args } + } + } = payload; + return internal_routes.ExecuteSqlResponse.encode( + // TODO handle case where no sync API is registered + await payload.context.service_context.syncAPIProvider.getSyncAPI().executeQuery(query, args) + ); } }); @@ -87,17 +55,20 @@ export const diagnostics = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.DiagnosticsRequest, { allowAdditional: true }), handler: async (payload) => { + const { context } = payload; + const { service_context } = context; const include_content = payload.params.sync_rules_content ?? false; - const system = payload.context.system; - const status = await api.getConnectionStatus(system); + const syncAPI = service_context.syncAPIProvider.getSyncAPI(); + + const status = await syncAPI.getConnectionStatus(); if (status == null) { return internal_routes.DiagnosticsResponse.encode({ connections: [] }); } - const { storage } = system; + const { storage } = service_context; const active = await storage.getActiveSyncRulesContent(); const next = await storage.getNextSyncRulesContent(); diff --git a/packages/service-core/src/routes/router.ts b/packages/service-core/src/routes/router.ts index 6eae50531..88e4a9ea4 100644 --- a/packages/service-core/src/routes/router.ts +++ b/packages/service-core/src/routes/router.ts @@ -1,6 +1,7 @@ import { router } from '@powersync/lib-services-framework'; import * as auth from '../auth/auth-index.js'; import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; +import { ServiceContext } from '../system/ServiceContext.js'; /** * Common context for routes @@ -8,6 +9,7 @@ import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; export type Context = { user_id?: string; system: CorePowerSyncSystem; + service_context: ServiceContext; token_payload?: auth.JwtPayload; token_errors?: string[]; diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index c1d80a6b0..66b972964 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -23,7 +23,9 @@ export const dataSourceConfig = t.object({ // Unique string identifier for the data source type: t.string, /** Unique identifier for the connection - optional when a single connection is present. */ - id: t.string.optional() + id: t.string.optional(), + /** Additional meta tag for connection */ + tag: t.string.optional() }); export type DataSourceConfig = t.Decoded; From 7e626d7550bf92021a6c62f9040f0b04754ea323 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 23 Jul 2024 09:08:53 +0200 Subject: [PATCH 018/298] Removed connection from Adapter interfaces --- .../replication/core/ReplicationAdapter.ts | 84 ++++++++----------- .../src/replication/core/ReplicationEntity.ts | 32 ++----- .../src/replication/core/ReplicationModule.ts | 2 +- 3 files changed, 45 insertions(+), 73 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 83063aa62..3c685cda8 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -1,78 +1,68 @@ -import { TablePattern } from '@powersync/service-sync-rules'; +import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; import { ReplicationEntity } from './ReplicationEntity.js'; import { SaveOptions } from '../../storage/BucketStorage.js'; -/** - * Manage the creation and termination of connections for a datasource. - * For some data sources there will be no difference between snapshot and replication connections - */ -interface ConnectionManager { - /** - * Create a connection to use for replication. - */ - createReplicationConnection(): Promise; - - /** - * Create a connection to use for the initial snapshot replication. - * This connection should not be shared - */ - createConnection(): Promise; - - /** - * Map a datasource specific connection error to a general category of powersync errors - * @param error - */ - mapError(error: Error): ConnectionError; -} - /** * The ReplicationAdapter describes all the methods that are required by the * Replicator to replicate data from a datasource into the PowerSync Bucket storage */ -export interface ReplicationAdapter { +export interface ReplicationAdapter { /** * Unique name to identify this adapter in the PowerSync system - * Suggestion: datasource type + datasource name + * Suggestion: datasource type + datasource name ie. postgres-prod1 */ name(): string; /** - * Return a manager that can create connections to the DataSource + * Check that the configuration required for replication on the datasource is in place. + * If any configuration is missing or incorrect, an error should be thrown with the details. */ - createConnectionManager(): ConnectionManager; + checkPrerequisites(): Promise; /** - * Validate any configuration on the datasource that needs to be in place for - * replication. If any configuration is missing or incorrect, an error should be thrown with the details. + * Get all the fully qualified entities that match the provided pattern + * @param pattern // TODO: Need something more generic than TablePattern */ - validateConfiguration(connection: TConnection): void; + toReplicationEntities(pattern: TablePattern): Promise; /** - * Get all the fully qualified entities that match the provided pattern - * @param connection - * @param pattern // TODO: Need something more generic than TablePattern + * Get the number of entries for this Entity + * @param entity */ - toReplicationEntities(connection: TConnection, pattern: TablePattern): Promise[]>; + count(entity: ReplicationEntity): Promise; + + /** + * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. + * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. + * This LSN will be used as the starting point for the replication stream. + * @param options + */ + initializeData(options: InitializeDataOptions): Promise; /** * Start replicating data, assumes that initializeData has already finished running * Stream any changes back on the provided changeListener - * @param connection - * @param changeListener + * @param options */ - startReplication(connection: TConnection, changeListener: (change: SaveOptions) => {}): Promise; + startReplication(options: StartReplicationOptions): Promise; /** - * Immediately interrupt and stop any currently running replication. + * Clean up any configuration or state for the replication with the given identifier on the datasource. + * This assumes that the replication is not currently active. + * @param syncRuleId The id of the SyncRule that was used to configure the replication */ - terminateReplication(): Promise; + cleanupReplication(syncRuleId: number): Promise; +} + +export interface InitializeDataOptions { + entity: ReplicationEntity; + entry_consumer: (batch: SqliteRow[]) => {}; + abort_signal: AbortSignal; } -export enum ConnectionError { - INCORRECT_CREDENTIALS = 'INCORRECT_CREDENTIALS', - HOST_INACCESSIBLE = 'HOST_INACCESSIBLE', - CONNECTION_REFUSED = 'CONNECTION_REFUSED', - CONNECTION_CLOSED = 'CONNECTION_CLOSED', - RETRIEVAL_FAILED = 'RETRIEVAL_FAILED', - GENERAL = 'GENERAL' +export interface StartReplicationOptions { + entities: ReplicationEntity[]; + from_lsn: string; + change_listener: (change: SaveOptions) => {}; + abort_signal: AbortSignal; } diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts index 0f4d52af5..699ea96a5 100644 --- a/packages/service-core/src/replication/core/ReplicationEntity.ts +++ b/packages/service-core/src/replication/core/ReplicationEntity.ts @@ -2,7 +2,6 @@ import { SqliteRow } from '@powersync/service-sync-rules'; /** * Describes a replication entity, which is a logical representation of a table or collection in a data source. - * Extend this interface to add additional properties specific entities in the data source. */ export interface ReplicationEntityDescriptor { name: string; @@ -24,11 +23,12 @@ export interface ReplicationEntityDescriptor { * The field(s) that uniquely identify an entry/row in this replication entity */ primaryIdentifierFields: string[]; -} -export abstract class ReplicationEntity { - public descriptor: T; + // Add any additional properties specific to the entity here + additionalProperties: Record; +} +export class ReplicationEntity { /** * Defaults to true for tests. */ @@ -44,9 +44,9 @@ export abstract class ReplicationEntity { */ public snapshotComplete: boolean = false; - constructor(descriptor: T) { - this.descriptor = descriptor; - } + public snapshotLSN: string | null = null; + + constructor(public descriptor: ReplicationEntityDescriptor) {} public hasPrimaryIdentifierFields(): boolean { return this.descriptor.primaryIdentifierFields.length > 0; @@ -55,22 +55,4 @@ export abstract class ReplicationEntity { public syncAny() { return this.syncData || this.syncParameters; } - - /** - * Get the number of entries for this Entity - * @param connection - */ - public abstract count(connection: TConnection): Promise; - - /** - * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. - * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. - * This LSN will be used as the starting point for the replication stream. - * @param connection - * @param entryConsumer - */ - public abstract getSnapshot( - connection: TConnection, - entryConsumer: (batch: SqliteRow[]) => {} - ): Promise; } diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 751155e9b..d8ec2019e 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -35,7 +35,7 @@ export abstract class ReplicationModule extends AbstractModule { /** * Create the ReplicationAdapter to be used by PowerSync replicator. */ - protected abstract createReplicationAdapter(): ReplicationAdapter; + protected abstract createReplicationAdapter(): ReplicationAdapter; /** * Return the TS codec schema describing the required configuration values for this module. From a0d750c748ce8e34a87f9711dc61d753e5f9a3b1 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 23 Jul 2024 09:09:23 +0200 Subject: [PATCH 019/298] fix imports --- .../src/replication/PostgresSyncAPIAdapter.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts index 9b0362f8f..cfb07bb54 100644 --- a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts @@ -2,15 +2,11 @@ import * as pgwire from '@powersync/service-jpgwire'; import { api, replication } from '@powersync/service-core'; import { TablePattern } from '@powersync/service-sync-rules'; -import { DatabaseSchema, internal_routes } from '@powersync/service-types'; - -// TODO -import { ExecuteSqlResponse } from '@powersync/service-types/src/routes.js'; +import { configFile, DatabaseSchema, internal_routes } from '@powersync/service-types'; import * as pg_utils from '../utils/pgwire_utils.js'; import * as replication_utils from '../replication/replication-utils.js'; import { baseUri, ResolvedConnectionConfig } from '../types/types.js'; -import { DataSourceConfig } from '@powersync/service-types/src/config/PowerSyncConfig.js'; export class PostgresSyncAPIAdapter implements api.SyncAPI { // TODO manage lifecycle of this @@ -22,7 +18,7 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { }); } - async getSourceConfig(): Promise { + async getSourceConfig(): Promise { return this.config; } @@ -101,7 +97,7 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, throw new Error('Method not implemented.'); } - executeSQL(sql: string, params: any[]): Promise { + executeSQL(sql: string, params: any[]): Promise { throw new Error('Method not implemented.'); } } From c700818f5be143c9917259221dc140bbd12e5034 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 23 Jul 2024 13:12:27 +0200 Subject: [PATCH 020/298] wip: cleanup some implementations --- .../connection/PostgresConnectionManager.ts | 76 --------- .../src/module/PostgresModule.ts | 12 +- .../replication/PostgresReplicationAdapter.ts | 32 ++-- .../src/replication/PostgresSyncAPIAdapter.ts | 160 +++++++++++++++++- packages/service-core/src/api/SyncAPI.ts | 5 + .../replication/core/ReplicationAdapter.ts | 5 + .../src/replication/core/ReplicationModule.ts | 24 ++- packages/service-core/src/routes/router.ts | 2 - 8 files changed, 211 insertions(+), 105 deletions(-) delete mode 100644 modules/module-postgres/src/connection/PostgresConnectionManager.ts diff --git a/modules/module-postgres/src/connection/PostgresConnectionManager.ts b/modules/module-postgres/src/connection/PostgresConnectionManager.ts deleted file mode 100644 index 8bdb2bd26..000000000 --- a/modules/module-postgres/src/connection/PostgresConnectionManager.ts +++ /dev/null @@ -1,76 +0,0 @@ -import * as pgwire from '@powersync/service-jpgwire'; - -import { replication } from '@powersync/service-core'; -import { NormalizedPostgresConnectionConfig } from '../types/types.js'; - -export type PostgresConnection = pgwire.PgClient; - -export class PostgresConnectionManager implements replication.ConnectionManager { - /** - * Do not use this for any transactions. - */ - public readonly pool: pgwire.PgClient; - - private connectionPromises: Promise[] = []; - - constructor(public options: NormalizedPostgresConnectionConfig) { - // The pool is lazy - no connections are opened until a query is performed. - this.pool = pgwire.connectPgWirePool(this.options, {}); - } - - /** - * Create a new replication connection. - */ - async createReplicationConnection(): Promise { - const p = pgwire.connectPgWire(this.options, { type: 'replication' }); - this.connectionPromises.push(p); - return await p; - } - - /** - * Create a new standard connection, used for initial snapshot. - * - * This connection must not be shared between multiple async contexts. - */ - async createConnection(): Promise { - const p = pgwire.connectPgWire(this.options, { type: 'standard' }); - this.connectionPromises.push(p); - return await p; - } - - mapError(error: Error): replication.ConnectionError { - throw new Error('Method not implemented.'); - } - - // TODO need some way to end connections - // The generics don't cater for this - // Can't register an automatic termination callback without service context - - async end() { - for (let result of await Promise.allSettled([ - this.pool.end(), - ...this.connectionPromises.map((promise) => { - return promise.then((connection) => connection.end()); - }) - ])) { - // Throw the first error, if any - if (result.status == 'rejected') { - throw result.reason; - } - } - } - - async destroy() { - this.pool.destroy(); - for (let result of await Promise.allSettled([ - ...this.connectionPromises.map((promise) => { - return promise.then((connection) => connection.destroy()); - }) - ])) { - // Throw the first error, if any - if (result.status == 'rejected') { - throw result.reason; - } - } - } -} diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index ec1a9dab3..f505c5036 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -20,6 +20,10 @@ export class PostgresModule extends replication.ReplicationModule { return new PostgresReplicationAdapter(this.resolveConfig(config)); } + public teardown(): Promise { + throw new Error('Method not implemented.'); + } + /** * Combines base config with normalized connection settings */ @@ -29,12 +33,4 @@ export class PostgresModule extends replication.ReplicationModule { ...normalizeConnectionConfig(config) }; } - - public shutdown(): Promise { - throw new Error('Method not implemented.'); - } - - public teardown(): Promise { - throw new Error('Method not implemented.'); - } } diff --git a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts index 99f96f1e9..91b3f1d1e 100644 --- a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts @@ -1,32 +1,42 @@ -import { replication, storage } from '@powersync/service-core'; +import { replication } from '@powersync/service-core'; import * as sync_rules from '@powersync/service-sync-rules'; +import * as pgwire from '@powersync/service-jpgwire'; -import { PostgresConnection, PostgresConnectionManager } from '../connection/PostgresConnectionManager.js'; import { ResolvedConnectionConfig } from '../types/types.js'; -export class PostgresReplicationAdapter implements replication.ReplicationAdapter { +export type PostgresConnection = pgwire.PgClient; + +export class PostgresReplicationAdapter implements replication.ReplicationAdapter { constructor(protected config: ResolvedConnectionConfig) {} name(): string { return 'postgres'; } - createConnectionManager(): PostgresConnectionManager { - return new PostgresConnectionManager(this.config); + shutdown(): Promise { + throw new Error('Method not implemented.'); + } + + checkPrerequisites(): Promise { + throw new Error('Method not implemented.'); + } + + toReplicationEntities(pattern: sync_rules.TablePattern): Promise { + throw new Error('Method not implemented.'); } - validateConfiguration(connection: PostgresConnection): void { + count(entity: replication.ReplicationEntity): Promise { throw new Error('Method not implemented.'); } - toReplicationEntities( - connection: PostgresConnection, - pattern: sync_rules.TablePattern - ): Promise[]> { + initializeData(options: replication.InitializeDataOptions): Promise { throw new Error('Method not implemented.'); } - startReplication(connection: PostgresConnection, changeListener: (change: storage.SaveOptions) => {}): Promise { + startReplication(options: replication.StartReplicationOptions): Promise { + throw new Error('Method not implemented.'); + } + cleanupReplication(syncRuleId: number): Promise { throw new Error('Method not implemented.'); } diff --git a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts index cfb07bb54..1ef520ea0 100644 --- a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts @@ -1,23 +1,30 @@ import * as pgwire from '@powersync/service-jpgwire'; -import { api, replication } from '@powersync/service-core'; +import { api, replication, storage } from '@powersync/service-core'; -import { TablePattern } from '@powersync/service-sync-rules'; -import { configFile, DatabaseSchema, internal_routes } from '@powersync/service-types'; +import { DEFAULT_TAG, TablePattern } from '@powersync/service-sync-rules'; +import { configFile, DatabaseSchema, internal_routes, ReplicationError, TableInfo } from '@powersync/service-types'; import * as pg_utils from '../utils/pgwire_utils.js'; import * as replication_utils from '../replication/replication-utils.js'; import { baseUri, ResolvedConnectionConfig } from '../types/types.js'; export class PostgresSyncAPIAdapter implements api.SyncAPI { - // TODO manage lifecycle of this protected pool: pgwire.PgClient; + // TODO this should probably be configurable one day + connectionTag = DEFAULT_TAG; + publication_name = 'powersync'; + constructor(protected config: ResolvedConnectionConfig) { this.pool = pgwire.connectPgWirePool(config, { idleTimeout: 30_000 }); } + async shutdown(): Promise { + await this.pool.end(); + } + async getSourceConfig(): Promise { return this.config; } @@ -67,8 +74,149 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { throw new Error('Method not implemented.'); } - getDebugTablesInfo(tablePatterns: TablePattern[]): Promise { - throw new Error('Method not implemented.'); + async getDebugTablesInfo(tablePatterns: TablePattern[]): Promise { + let result: replication.PatternResult[] = []; + + for (let tablePattern of tablePatterns) { + const schema = tablePattern.schema; + + let patternResult: replication.PatternResult = { + schema: schema, + pattern: tablePattern.tablePattern, + wildcard: tablePattern.isWildcard + }; + result.push(patternResult); + + if (tablePattern.isWildcard) { + patternResult.tables = []; + const prefix = tablePattern.tablePrefix; + const results = await pg_utils.retriedQuery(this.pool, { + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname LIKE $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + + for (let row of pgwire.pgwireRows(results)) { + const name = row.table_name as string; + const relationId = row.relid as number; + if (!name.startsWith(prefix)) { + continue; + } + const details = await this.getDebugTableInfo(tablePattern, name, relationId); + patternResult.tables.push(details); + } + } else { + const results = await pg_utils.retriedQuery(this.pool, { + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname = $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + if (results.rows.length == 0) { + // Table not found + const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); + patternResult.table = details; + } else { + const row = pgwire.pgwireRows(results)[0]; + const name = row.table_name as string; + const relationId = row.relid as number; + patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId); + } + } + } + return result; + } + + protected async getDebugTableInfo( + tablePattern: TablePattern, + name: string, + relationId: number | null + ): Promise { + const schema = tablePattern.schema; + let id_columns_result: replication_utils.ReplicaIdentityResult | undefined = undefined; + let id_columns_error = null; + + if (relationId != null) { + try { + id_columns_result = await replication_utils.getReplicationIdentityColumns(this.pool, relationId); + } catch (e) { + id_columns_error = { level: 'fatal', message: e.message }; + } + } + + const id_columns = id_columns_result?.columns ?? []; + + const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); + + const syncData = this.sync_rules.tableSyncsData(sourceTable); + const syncParameters = this.sync_rules.tableSyncsParameters(sourceTable); + + if (relationId == null) { + return { + schema: schema, + name: name, + pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + replication_id: [], + data_queries: syncData, + parameter_queries: syncParameters, + // Also + errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] + }; + } + if (id_columns.length == 0 && id_columns_error == null) { + let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; + if (id_columns_result?.replicationIdentity == 'default') { + message += ' Configure a primary key on the table.'; + } + id_columns_error = { level: 'fatal', message }; + } + + let selectError = null; + try { + await pg_utils.retriedQuery(this.pool, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); + } catch (e) { + selectError = { level: 'fatal', message: e.message }; + } + + let replicateError = null; + + const publications = await pg_utils.retriedQuery(this.pool, { + statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, + params: [ + { type: 'varchar', value: this.publication_name }, + { type: 'varchar', value: tablePattern.schema }, + { type: 'varchar', value: name } + ] + }); + if (publications.rows.length == 0) { + replicateError = { + level: 'fatal', + message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` + }; + } + + return { + schema: schema, + name: name, + pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + replication_id: id_columns.map((c) => c.name), + data_queries: syncData, + parameter_queries: syncParameters, + errors: [id_columns_error, selectError, replicateError].filter((error) => error != null) as ReplicationError[] + }; } async getReplicationLag(slotName: string): Promise { diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index 799d8e0a6..00e32fc6e 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -62,6 +62,11 @@ export interface SyncAPI { getDemoCredentials(): Promise; //CRUD API : I don't think this is used besides maybe niche dev use cases + + /** + * Close any resources that need graceful termination. + */ + shutdown(): Promise; } export interface DemoCredentials { diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 3c685cda8..2952ca9d9 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -52,6 +52,11 @@ export interface ReplicationAdapter { * @param syncRuleId The id of the SyncRule that was used to configure the replication */ cleanupReplication(syncRuleId: number): Promise; + + /** + * Close any resources that need graceful termination. + */ + shutdown(): Promise; } export interface InitializeDataOptions { diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 191e3a56a..f80a21bde 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -17,6 +17,9 @@ export interface ReplicationModuleOptions extends AbstractModuleOptions { export abstract class ReplicationModule extends AbstractModule { protected type: string; + protected apiAdapters: Set; + protected replicationAdapters: Set; + /** * @protected * @param options @@ -24,6 +27,8 @@ export abstract class ReplicationModule extends AbstractModule { protected constructor(options: ReplicationModuleOptions) { super(options); this.type = options.type; + this.apiAdapters = new Set(); + this.replicationAdapters = new Set(); } /** @@ -65,8 +70,13 @@ export abstract class ReplicationModule extends AbstractModule { const decodedConfig = this.configSchema().decode(baseMatchingConfig); // If validation fails, log the error and continue, no replication will happen for this data source this.validateConfig(matchingConfig[0]); - context.replicationEngine.register(this.createReplicationAdapter(decodedConfig)); - context.syncAPIProvider.register(this.createSyncAPIAdapter(decodedConfig)); + const replicationAdapter = this.createReplicationAdapter(decodedConfig); + this.replicationAdapters.add(replicationAdapter); + context.replicationEngine.register(replicationAdapter); + + const apiAdapter = this.createSyncAPIAdapter(decodedConfig); + this.apiAdapters.add(apiAdapter); + context.syncAPIProvider.register(apiAdapter); } catch (e) { logger.error(e); } @@ -83,4 +93,14 @@ export abstract class ReplicationModule extends AbstractModule { throw new Error(`Failed to validate Module ${this.name} configuration: ${valid.errors.join(', ')}`); } } + + public async shutdown(): Promise { + for (const api of this.apiAdapters) { + await api.shutdown(); + } + + for (const replication of this.replicationAdapters) { + await replication.shutdown(); + } + } } diff --git a/packages/service-core/src/routes/router.ts b/packages/service-core/src/routes/router.ts index 88e4a9ea4..a2435278a 100644 --- a/packages/service-core/src/routes/router.ts +++ b/packages/service-core/src/routes/router.ts @@ -1,6 +1,5 @@ import { router } from '@powersync/lib-services-framework'; import * as auth from '../auth/auth-index.js'; -import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; import { ServiceContext } from '../system/ServiceContext.js'; /** @@ -8,7 +7,6 @@ import { ServiceContext } from '../system/ServiceContext.js'; */ export type Context = { user_id?: string; - system: CorePowerSyncSystem; service_context: ServiceContext; token_payload?: auth.JwtPayload; From e994e9a3cce301e5061b4aaa1279c402fe34ee3c Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 23 Jul 2024 14:16:34 +0200 Subject: [PATCH 021/298] update container service_context registration. Add more API functionality --- libs/lib-services/src/container.ts | 64 ++++++++-- .../src/replication/PostgresSyncAPIAdapter.ts | 110 +++++++++++++++--- packages/service-core/src/api/SyncAPI.ts | 15 +-- packages/service-core/src/api/diagnostics.ts | 14 +-- packages/service-core/src/api/schema.ts | 105 +++-------------- .../src/routes/endpoints/admin.ts | 45 ++++--- .../service-core/src/system/ServiceContext.ts | 12 +- .../service-core/src/system/system-index.ts | 1 + packages/types/src/definitions.ts | 9 ++ 9 files changed, 223 insertions(+), 152 deletions(-) diff --git a/libs/lib-services/src/container.ts b/libs/lib-services/src/container.ts index 0448a9f0d..4e015284f 100644 --- a/libs/lib-services/src/container.ts +++ b/libs/lib-services/src/container.ts @@ -23,14 +23,34 @@ export type ContainerImplementationDefaultGenerators = { [type in ContainerImplementation]: () => ContainerImplementationTypes[type]; }; +/** + * Helper for identifying constructors + */ +export interface Abstract { + prototype: T; +} +/** + * A basic class constructor + */ +export type Newable = new (...args: never[]) => T; + +/** + * Identifier used to get and register implementations + */ +export type ServiceIdentifier = string | symbol | Newable | Abstract | ContainerImplementation; + const DEFAULT_GENERATORS: ContainerImplementationDefaultGenerators = { [ContainerImplementation.REPORTER]: () => NoOpReporter, [ContainerImplementation.PROBES]: () => createFSProbe(), [ContainerImplementation.TERMINATION_HANDLER]: () => createTerminationHandler() }; +/** + * A container which provides means for registering and getting various + * function implementations. + */ export class Container { - protected implementations: Partial; + protected implementations: Map, any>; /** * Manager for system health probes @@ -54,13 +74,39 @@ export class Container { } constructor() { - this.implementations = {}; + this.implementations = new Map(); + } + + /** + * Gets an implementation given an identifier. + * An exception is thrown if the implementation has not been registered. + * Core [ContainerImplementation] identifiers are mapped to their respective implementation types. + * This also allows for getting generic implementations (unknown to the core framework) which have been registered. + */ + getImplementation(identifier: Newable | Abstract): T; + getImplementation(identifier: T): ContainerImplementationTypes[T]; + getImplementation(identifier: ServiceIdentifier): T; + getImplementation(identifier: ServiceIdentifier): T { + const implementation = this.implementations.get(identifier); + if (!implementation) { + throw new Error(`Implementation for ${String(identifier)} has not been registered.`); + } + return implementation; } - getImplementation(type: Type) { - const implementation = this.implementations[type]; + /** + * Gets an implementation given an identifier. + * Null is returned if the implementation has not been registered yet. + * Core [ContainerImplementation] identifiers are mapped to their respective implementation types. + * This also allows for getting generic implementations (unknown to the core framework) which have been registered. + */ + getOptional(identifier: Newable | Abstract): T | null; + getOptional(identifier: T): ContainerImplementationTypes[T] | null; + getOptional(identifier: ServiceIdentifier): T | null; + getOptional(identifier: ServiceIdentifier): T | null { + const implementation = this.implementations.get(identifier); if (!implementation) { - throw new Error(`Implementation for ${type} has not been registered.`); + return null; } return implementation; } @@ -71,15 +117,15 @@ export class Container { registerDefaults(options?: RegisterDefaultsOptions) { _.difference(Object.values(ContainerImplementation), options?.skip ?? []).forEach((type) => { const generator = DEFAULT_GENERATORS[type]; - this.implementations[type] = generator() as any; // :( + this.register(type, generator()); }); } /** - * Allows for overriding a default implementation + * Allows for registering core and generic implementations of services/helpers. */ - register(type: Type, implementation: ContainerImplementationTypes[Type]) { - this.implementations[type] = implementation; + register(identifier: ServiceIdentifier, implementation: T) { + this.implementations.set(identifier, implementation); } } diff --git a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts index 1ef520ea0..957494225 100644 --- a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts @@ -1,8 +1,15 @@ import * as pgwire from '@powersync/service-jpgwire'; import { api, replication, storage } from '@powersync/service-core'; -import { DEFAULT_TAG, TablePattern } from '@powersync/service-sync-rules'; -import { configFile, DatabaseSchema, internal_routes, ReplicationError, TableInfo } from '@powersync/service-types'; +import { DEFAULT_TAG, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; +import { + configFile, + ConnectionStatusV2, + DatabaseSchema, + internal_routes, + ReplicationError, + TableInfo +} from '@powersync/service-types'; import * as pg_utils from '../utils/pgwire_utils.js'; import * as replication_utils from '../replication/replication-utils.js'; @@ -29,10 +36,10 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { return this.config; } - async getConnectionStatus(): Promise { + async getConnectionStatus(): Promise { const base = { id: this.config.id, - postgres_uri: baseUri(this.config) + uri: baseUri(this.config) }; try { @@ -74,7 +81,10 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { throw new Error('Method not implemented.'); } - async getDebugTablesInfo(tablePatterns: TablePattern[]): Promise { + async getDebugTablesInfo( + tablePatterns: TablePattern[], + sqlSyncRules: SqlSyncRules + ): Promise { let result: replication.PatternResult[] = []; for (let tablePattern of tablePatterns) { @@ -109,7 +119,7 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { if (!name.startsWith(prefix)) { continue; } - const details = await this.getDebugTableInfo(tablePattern, name, relationId); + const details = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules); patternResult.tables.push(details); } } else { @@ -127,13 +137,13 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { }); if (results.rows.length == 0) { // Table not found - const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); + const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null, sqlSyncRules); patternResult.table = details; } else { const row = pgwire.pgwireRows(results)[0]; const name = row.table_name as string; const relationId = row.relid as number; - patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId); + patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules); } } } @@ -143,7 +153,8 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { protected async getDebugTableInfo( tablePattern: TablePattern, name: string, - relationId: number | null + relationId: number | null, + syncRules: SqlSyncRules ): Promise { const schema = tablePattern.schema; let id_columns_result: replication_utils.ReplicaIdentityResult | undefined = undefined; @@ -161,8 +172,8 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); - const syncData = this.sync_rules.tableSyncsData(sourceTable); - const syncParameters = this.sync_rules.tableSyncsParameters(sourceTable); + const syncData = syncRules.tableSyncsData(sourceTable); + const syncParameters = syncRules.tableSyncsParameters(sourceTable); if (relationId == null) { return { @@ -241,8 +252,81 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, throw new Error('Method not implemented.'); } - getConnectionSchema(): Promise { - throw new Error('Method not implemented.'); + async getConnectionSchema(): Promise { + // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts + const results = await pg_utils.retriedQuery( + this.pool, + `SELECT +tbl.schemaname, +tbl.tablename, +tbl.quoted_name, +json_agg(a ORDER BY attnum) as columns +FROM +( + SELECT + n.nspname as schemaname, + c.relname as tablename, + (quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as quoted_name + FROM + pg_catalog.pg_class c + JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace + WHERE + c.relkind = 'r' + AND n.nspname not in ('information_schema', 'pg_catalog', 'pg_toast') + AND n.nspname not like 'pg_temp_%' + AND n.nspname not like 'pg_toast_temp_%' + AND c.relnatts > 0 + AND has_schema_privilege(n.oid, 'USAGE') = true + AND has_table_privilege(quote_ident(n.nspname) || '.' || quote_ident(c.relname), 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') = true +) as tbl +LEFT JOIN ( + SELECT + attrelid, + attname, + format_type(atttypid, atttypmod) as data_type, + (SELECT typname FROM pg_catalog.pg_type WHERE oid = atttypid) as pg_type, + attnum, + attisdropped + FROM + pg_attribute +) as a ON ( + a.attrelid = tbl.quoted_name::regclass + AND a.attnum > 0 + AND NOT a.attisdropped + AND has_column_privilege(tbl.quoted_name, a.attname, 'SELECT, INSERT, UPDATE, REFERENCES') +) +GROUP BY schemaname, tablename, quoted_name` + ); + const rows = pgwire.pgwireRows(results); + + let schemas: Record = {}; + + for (let row of rows) { + const schema = (schemas[row.schemaname] ??= { + name: row.schemaname, + tables: [] + }); + const table = { + name: row.tablename, + columns: [] as any[] + }; + schema.tables.push(table); + + const columnInfo = JSON.parse(row.columns); + for (let column of columnInfo) { + let pg_type = column.pg_type as string; + if (pg_type.startsWith('_')) { + pg_type = `${pg_type.substring(1)}[]`; + } + table.columns.push({ + name: column.attname, + type: column.data_type, + pg_type: pg_type + }); + } + } + + return Object.values(schemas); } executeSQL(sql: string, params: any[]): Promise { diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index 00e32fc6e..c2c166601 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -1,4 +1,4 @@ -import { TablePattern } from '@powersync/service-sync-rules'; +import { SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; /** @@ -14,7 +14,7 @@ export interface SyncAPI { * Checks the current connection status of the datasource. * This is usually some test query to verify the source can be reached. */ - getConnectionStatus(): Promise; + getConnectionStatus(): Promise; /** * Generates replication table information from a given pattern of tables. @@ -27,7 +27,7 @@ export interface SyncAPI { * tables to ensure syncing should function according to the input * pattern. Debug errors and warnings are reported per table. */ - getDebugTablesInfo(tablePatterns: TablePattern[]): Promise; + getDebugTablesInfo(tablePatterns: TablePattern[], sqlSyncRules: SqlSyncRules): Promise; /** * @returns The replication lag: that is the amount of data which has not been @@ -82,15 +82,6 @@ interface PatternResult { table?: types.TableInfo; } -interface ErrorDescription { - level: string; - message: string; -} -export interface ConnectionStatusResponse { - connected: boolean; - errors?: ErrorDescription[]; -} - export interface QueryResults { columns: string[]; rows: (string | number | boolean | null)[][]; diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index a9b1eb697..623befaa1 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -1,9 +1,9 @@ import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/service-sync-rules'; import { SyncRulesStatus, TableInfo } from '@powersync/service-types'; +import { container, logger } from '@powersync/lib-services-framework'; -import { logger } from '@powersync/lib-services-framework'; -import { ServiceContext } from '../system/ServiceContext.js'; import * as storage from '../storage/storage-index.js'; +import { ServiceContext } from '../system/ServiceContext.js'; export interface DiagnosticsOptions { /** @@ -28,13 +28,14 @@ export const DEFAULT_DATASOURCE_ID = 'default'; export async function getSyncRulesStatus( sync_rules: storage.PersistedSyncRulesContent | null, - service_context: ServiceContext, options: DiagnosticsOptions ): Promise { if (sync_rules == null) { return undefined; } + const serviceContext = container.getImplementation(ServiceContext); + const include_content = options.include_content ?? false; const live_status = options.live_status ?? false; const check_connection = options.check_connection ?? false; @@ -52,8 +53,8 @@ export async function getSyncRulesStatus( }; } - const { storage } = service_context; - const syncAPI = service_context.syncAPIProvider.getSyncAPI(); + const { storage } = serviceContext; + const syncAPI = serviceContext.syncAPIProvider.getSyncAPI(); const systemStorage = live_status ? await storage.getInstance(persisted) : undefined; const status = await systemStorage?.getStatus(); @@ -66,7 +67,7 @@ export async function getSyncRulesStatus( if (check_connection) { const source_table_patterns = rules.getSourceTables(); - const resolved_tables = await syncAPI.getDebugTablesInfo(source_table_patterns); + const resolved_tables = await syncAPI.getDebugTablesInfo(source_table_patterns, rules); tables_flat = resolved_tables.flatMap((info) => { if (info.table) { return [info.table]; @@ -133,7 +134,6 @@ export async function getSyncRulesStatus( }) ); - return { content: include_content ? sync_rules.sync_rules_content : undefined, connections: [ diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index e3ffbb744..011cb786c 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -1,99 +1,28 @@ -import type * as pgwire from '@powersync/service-jpgwire'; -import { pgwireRows } from '@powersync/service-jpgwire'; -import { DatabaseSchema, internal_routes } from '@powersync/service-types'; +import { container } from '@powersync/lib-services-framework'; +import { internal_routes } from '@powersync/service-types'; -import * as util from '../util/util-index.js'; -import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; +import { ServiceContext } from '../system/ServiceContext.js'; +import { SyncAPI } from './SyncAPI.js'; -export async function getConnectionsSchema(system: CorePowerSyncSystem): Promise { - if (system.config.connection == null) { +export async function getConnectionsSchema(): Promise { + const { syncAPIProvider } = container.getImplementation(ServiceContext); + + let api: SyncAPI; + try { + api = syncAPIProvider.getSyncAPI(); + } catch (ex) { return { connections: [] }; } - const schemas = await getConnectionSchema(system.requirePgPool()); + + const baseConfig = await api.getSourceConfig(); + return { connections: [ { - schemas, - tag: system.config.connection!.tag, - id: system.config.connection!.id + schemas: await api.getConnectionSchema(), + tag: baseConfig.tag!, + id: baseConfig.id } ] }; } - -export async function getConnectionSchema(db: pgwire.PgClient): Promise { - // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts - const results = await util.retriedQuery( - db, - `SELECT - tbl.schemaname, - tbl.tablename, - tbl.quoted_name, - json_agg(a ORDER BY attnum) as columns -FROM - ( - SELECT - n.nspname as schemaname, - c.relname as tablename, - (quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as quoted_name - FROM - pg_catalog.pg_class c - JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace - WHERE - c.relkind = 'r' - AND n.nspname not in ('information_schema', 'pg_catalog', 'pg_toast') - AND n.nspname not like 'pg_temp_%' - AND n.nspname not like 'pg_toast_temp_%' - AND c.relnatts > 0 - AND has_schema_privilege(n.oid, 'USAGE') = true - AND has_table_privilege(quote_ident(n.nspname) || '.' || quote_ident(c.relname), 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') = true - ) as tbl - LEFT JOIN ( - SELECT - attrelid, - attname, - format_type(atttypid, atttypmod) as data_type, - (SELECT typname FROM pg_catalog.pg_type WHERE oid = atttypid) as pg_type, - attnum, - attisdropped - FROM - pg_attribute - ) as a ON ( - a.attrelid = tbl.quoted_name::regclass - AND a.attnum > 0 - AND NOT a.attisdropped - AND has_column_privilege(tbl.quoted_name, a.attname, 'SELECT, INSERT, UPDATE, REFERENCES') - ) -GROUP BY schemaname, tablename, quoted_name` - ); - const rows = pgwireRows(results); - - let schemas: Record = {}; - - for (let row of rows) { - const schema = (schemas[row.schemaname] ??= { - name: row.schemaname, - tables: [] - }); - const table = { - name: row.tablename, - columns: [] as any[] - }; - schema.tables.push(table); - - const columnInfo = JSON.parse(row.columns); - for (let column of columnInfo) { - let pg_type = column.pg_type as string; - if (pg_type.startsWith('_')) { - pg_type = `${pg_type.substring(1)}[]`; - } - table.columns.push({ - name: column.attname, - type: column.data_type, - pg_type: pg_type - }); - } - } - - return Object.values(schemas); -} diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index 6dd34fe61..ca3dc2af9 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -72,20 +72,26 @@ export const diagnostics = routeDefinition({ const active = await storage.getActiveSyncRulesContent(); const next = await storage.getNextSyncRulesContent(); - const active_status = await api.getSyncRulesStatus(active, system, { + const active_status = await api.getSyncRulesStatus(active, { include_content, check_connection: status.connected, live_status: true }); - const next_status = await api.getSyncRulesStatus(next, system, { + const next_status = await api.getSyncRulesStatus(next, { include_content, check_connection: status.connected, live_status: true }); return internal_routes.DiagnosticsResponse.encode({ - connections: [status], + connections: [ + { + ...status, + // TODO update this in future + postgres_uri: status.uri + } + ], active_sync_rules: active_status, deploying_sync_rules: next_status }); @@ -97,10 +103,8 @@ export const getSchema = routeDefinition({ method: router.HTTPMethod.POST, authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }), - handler: async (payload) => { - const system = payload.context.system; - - return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(system)); + handler: async () => { + return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema()); } }); @@ -110,9 +114,10 @@ export const reprocess = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.ReprocessRequest, { allowAdditional: true }), handler: async (payload) => { - const system = payload.context.system; - - const storage = system.storage; + const { + context: { service_context } + } = payload; + const storage = service_context.storage; const next = await storage.getNextSyncRules(); if (next != null) { throw new Error(`Busy processing sync rules - cannot reprocess`); @@ -131,11 +136,14 @@ export const reprocess = routeDefinition({ content: active.sync_rules.content }); + const api = service_context.syncAPIProvider.getSyncAPI(); + const baseConfig = await api.getSourceConfig(); + return internal_routes.ReprocessResponse.encode({ connections: [ { - tag: system.config.connection!.tag, - id: system.config.connection!.id, + tag: baseConfig.tag!, + id: baseConfig.id, slot_name: new_rules.slot_name } ] @@ -149,11 +157,12 @@ export const validate = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.ValidateRequest, { allowAdditional: true }), handler: async (payload) => { - const system = payload.context.system; - + const { + context: { service_context } + } = payload; const content = payload.params.sync_rules; - const schemaData = await api.getConnectionsSchema(system); + const schemaData = await api.getConnectionsSchema(); const schema = new StaticSchema(schemaData.connections); const sync_rules: PersistedSyncRulesContent = { @@ -173,7 +182,9 @@ export const validate = routeDefinition({ } }; - const connectionStatus = await api.getConnectionStatus(system); + const apiHandler = service_context.syncAPIProvider.getSyncAPI(); + + const connectionStatus = await apiHandler.getConnectionStatus(); if (connectionStatus == null) { return internal_routes.ValidateResponse.encode({ errors: [{ level: 'fatal', message: 'No connection configured' }], @@ -181,7 +192,7 @@ export const validate = routeDefinition({ }); } - const status = (await api.getSyncRulesStatus(sync_rules, system, { + const status = (await api.getSyncRulesStatus(sync_rules, { include_content: false, check_connection: connectionStatus?.connected, live_status: false diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index 478192944..e205a4a09 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -3,9 +3,9 @@ import { SyncAPIProvider } from '../api/SyncAPIProvider.js'; import { ResolvedPowerSyncConfig } from '../util/config/types.js'; import { BucketStorageFactory } from '../storage/BucketStorage.js'; -export type ServiceContext = { - configuration: ResolvedPowerSyncConfig; - storage: BucketStorageFactory; - replicationEngine: ReplicationEngine; - syncAPIProvider: SyncAPIProvider; -}; +export abstract class ServiceContext { + abstract configuration: ResolvedPowerSyncConfig; + abstract storage: BucketStorageFactory; + abstract replicationEngine: ReplicationEngine; + abstract syncAPIProvider: SyncAPIProvider; +} diff --git a/packages/service-core/src/system/system-index.ts b/packages/service-core/src/system/system-index.ts index f40b2a60e..a64b0b482 100644 --- a/packages/service-core/src/system/system-index.ts +++ b/packages/service-core/src/system/system-index.ts @@ -1 +1,2 @@ export * from './CorePowerSyncSystem.js'; +export * from './ServiceContext.js'; diff --git a/packages/types/src/definitions.ts b/packages/types/src/definitions.ts index d24aa76c9..35dc64177 100644 --- a/packages/types/src/definitions.ts +++ b/packages/types/src/definitions.ts @@ -82,6 +82,15 @@ export const ConnectionStatus = t.object({ }); export type ConnectionStatus = t.Encoded; +export const ConnectionStatusV2 = t.object({ + id: t.string, + uri: t.string, + connected: t.boolean, + /** Connection-level errors */ + errors: t.array(ReplicationError) +}); +export type ConnectionStatusV2 = t.Encoded; + export const DatabaseSchema = t.object({ name: t.string, tables: t.array( From 0efd5358c1b3fd16f98d5840b5c7d5275a1486f0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 23 Jul 2024 14:22:25 +0200 Subject: [PATCH 022/298] Keeping SourceTable in replication adapter interface for now --- .../src/replication/core/ReplicationAdapter.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 3c685cda8..7bdff26e6 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -1,6 +1,5 @@ import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; -import { ReplicationEntity } from './ReplicationEntity.js'; -import { SaveOptions } from '../../storage/BucketStorage.js'; +import * as storage from '../../storage/storage-index.js'; /** * The ReplicationAdapter describes all the methods that are required by the @@ -23,13 +22,13 @@ export interface ReplicationAdapter { * Get all the fully qualified entities that match the provided pattern * @param pattern // TODO: Need something more generic than TablePattern */ - toReplicationEntities(pattern: TablePattern): Promise; + resolveReplicationEntities(pattern: TablePattern): Promise; /** * Get the number of entries for this Entity * @param entity */ - count(entity: ReplicationEntity): Promise; + count(entity: storage.SourceTable): Promise; /** * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. @@ -55,14 +54,14 @@ export interface ReplicationAdapter { } export interface InitializeDataOptions { - entity: ReplicationEntity; + entity: storage.SourceTable; entry_consumer: (batch: SqliteRow[]) => {}; abort_signal: AbortSignal; } export interface StartReplicationOptions { - entities: ReplicationEntity[]; + entities: storage.SourceTable[]; from_lsn: string; - change_listener: (change: SaveOptions) => {}; + change_listener: (change: storage.SaveOptions) => {}; abort_signal: AbortSignal; } From 6822a9fb66ead5046f22e3f21918526d1f96b6d6 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 23 Jul 2024 17:01:15 +0200 Subject: [PATCH 023/298] update checkpointing and sync routes --- .../service-core/src/api/SyncAPIProvider.ts | 14 ++--- packages/service-core/src/api/diagnostics.ts | 17 +++--- packages/service-core/src/api/schema.ts | 11 ++-- .../src/routes/endpoints/admin.ts | 61 +++++++++++-------- .../src/routes/endpoints/checkpointing.ts | 57 +++++++++++++---- .../src/routes/endpoints/socket-route.ts | 5 +- .../src/routes/endpoints/sync-stream.ts | 4 +- .../service-core/src/system/ServiceContext.ts | 2 + packages/types/src/config/PowerSyncConfig.ts | 6 +- 9 files changed, 114 insertions(+), 63 deletions(-) diff --git a/packages/service-core/src/api/SyncAPIProvider.ts b/packages/service-core/src/api/SyncAPIProvider.ts index ac1cb276e..9fb098d68 100644 --- a/packages/service-core/src/api/SyncAPIProvider.ts +++ b/packages/service-core/src/api/SyncAPIProvider.ts @@ -6,7 +6,11 @@ import { logger } from '@powersync/lib-services-framework'; * Initially only one SyncAPI per DataSource type is supported */ export class SyncAPIProvider { - private api: SyncAPI | undefined; + private api: SyncAPI | null; + + constructor() { + this.api = null; + } public register(api: SyncAPI) { if (this.api) { @@ -16,11 +20,7 @@ export class SyncAPIProvider { this.api = api; } - public getSyncAPI(): SyncAPI { - if (!this.api) { - throw new Error('No SyncAPI has been registered yet.'); - } - + public getSyncAPI(): SyncAPI | null { return this.api; } -} \ No newline at end of file +} diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 623befaa1..f83d6eede 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -2,9 +2,8 @@ import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/serv import { SyncRulesStatus, TableInfo } from '@powersync/service-types'; import { container, logger } from '@powersync/lib-services-framework'; -import * as storage from '../storage/storage-index.js'; import { ServiceContext } from '../system/ServiceContext.js'; - +import * as storage from '../storage/storage-index.js'; export interface DiagnosticsOptions { /** * Include sync rules content in response. @@ -62,10 +61,11 @@ export async function getSyncRulesStatus( let tables_flat: TableInfo[] = []; - const sourceConfig = await syncAPI.getSourceConfig(); - const tag = sourceConfig.tag ?? DEFAULT_TAG; - if (check_connection) { + if (!syncAPI) { + throw new Error('No connection configured'); + } + const source_table_patterns = rules.getSourceTables(); const resolved_tables = await syncAPI.getDebugTablesInfo(source_table_patterns, rules); tables_flat = resolved_tables.flatMap((info) => { @@ -134,12 +134,15 @@ export async function getSyncRulesStatus( }) ); + const sourceConfig = await syncAPI?.getSourceConfig(); + const tag = sourceConfig?.tag ?? DEFAULT_TAG; + return { content: include_content ? sync_rules.sync_rules_content : undefined, connections: [ { - id: sourceConfig.id ?? DEFAULT_DATASOURCE_ID, - tag: sourceConfig.tag ?? DEFAULT_TAG, + id: sourceConfig?.id ?? DEFAULT_DATASOURCE_ID, + tag: sourceConfig?.tag ?? DEFAULT_TAG, slot_name: sync_rules.slot_name, initial_replication_done: status?.snapshot_done ?? false, // TODO: Rename? diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index 011cb786c..1e95ab021 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -2,16 +2,15 @@ import { container } from '@powersync/lib-services-framework'; import { internal_routes } from '@powersync/service-types'; import { ServiceContext } from '../system/ServiceContext.js'; -import { SyncAPI } from './SyncAPI.js'; export async function getConnectionsSchema(): Promise { const { syncAPIProvider } = container.getImplementation(ServiceContext); - let api: SyncAPI; - try { - api = syncAPIProvider.getSyncAPI(); - } catch (ex) { - return { connections: [] }; + const api = syncAPIProvider.getSyncAPI(); + if (!api) { + return { + connections: [] + }; } const baseConfig = await api.getSourceConfig(); diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index ca3dc2af9..396af0adb 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -3,7 +3,6 @@ import { SqlSyncRules, SqliteValue, StaticSchema, isJsonValue } from '@powersync import { internal_routes } from '@powersync/service-types'; import * as api from '../../api/api-index.js'; -import * as util from '../../util/util-index.js'; import { routeDefinition } from '../router.js'; import { PersistedSyncRulesContent } from '../../storage/BucketStorage.js'; @@ -17,17 +16,17 @@ const demoCredentials = routeDefinition({ allowAdditional: true }), handler: async (payload) => { - const connection = payload.context.system.config.connection; - if (connection == null || !connection.demo_database) { - return internal_routes.DemoCredentialsResponse.encode({}); - } - - const uri = util.buildDemoPgUri(connection); - return internal_routes.DemoCredentialsResponse.encode({ - credentials: { - postgres_uri: uri - } - }); + // TODO is this used? + // const connection = payload.context.system.config.connection; + // if (connection == null || !connection.demo_database) { + // return internal_routes.DemoCredentialsResponse.encode({}); + // } + // const uri = util.buildDemoPgUri(connection); + // return internal_routes.DemoCredentialsResponse.encode({ + // credentials: { + // postgres_uri: uri + // } + // }); } }); @@ -42,10 +41,21 @@ export const executeSql = routeDefinition({ sql: { query, args } } } = payload; - return internal_routes.ExecuteSqlResponse.encode( - // TODO handle case where no sync API is registered - await payload.context.service_context.syncAPIProvider.getSyncAPI().executeQuery(query, args) - ); + + const api = payload.context.service_context.syncAPIProvider.getSyncAPI(); + const sourceConfig = await api?.getSourceConfig(); + if (!sourceConfig?.debug_enabled) { + return internal_routes.ExecuteSqlResponse.encode({ + results: { + columns: [], + rows: [] + }, + success: false, + error: 'SQL querying is not enabled' + }); + } + + return internal_routes.ExecuteSqlResponse.encode(await api!.executeQuery(query, args)); } }); @@ -60,9 +70,8 @@ export const diagnostics = routeDefinition({ const include_content = payload.params.sync_rules_content ?? false; const syncAPI = service_context.syncAPIProvider.getSyncAPI(); - - const status = await syncAPI.getConnectionStatus(); - if (status == null) { + const status = await syncAPI?.getConnectionStatus(); + if (!status) { return internal_routes.DiagnosticsResponse.encode({ connections: [] }); @@ -137,13 +146,14 @@ export const reprocess = routeDefinition({ }); const api = service_context.syncAPIProvider.getSyncAPI(); - const baseConfig = await api.getSourceConfig(); + const baseConfig = await api?.getSourceConfig(); return internal_routes.ReprocessResponse.encode({ connections: [ { - tag: baseConfig.tag!, - id: baseConfig.id, + // Previously the connection was asserted with `!` + tag: baseConfig!.tag!, + id: baseConfig!.id, slot_name: new_rules.slot_name } ] @@ -183,9 +193,8 @@ export const validate = routeDefinition({ }; const apiHandler = service_context.syncAPIProvider.getSyncAPI(); - - const connectionStatus = await apiHandler.getConnectionStatus(); - if (connectionStatus == null) { + const connectionStatus = await apiHandler?.getConnectionStatus(); + if (!connectionStatus) { return internal_routes.ValidateResponse.encode({ errors: [{ level: 'fatal', message: 'No connection configured' }], connections: [] @@ -194,7 +203,7 @@ export const validate = routeDefinition({ const status = (await api.getSyncRulesStatus(sync_rules, { include_content: false, - check_connection: connectionStatus?.connected, + check_connection: connectionStatus.connected, live_status: false }))!; diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index a2664ed91..640ca9b64 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -1,7 +1,6 @@ import * as t from 'ts-codec'; -import { router, schema } from '@powersync/lib-services-framework'; +import { logger, router, schema } from '@powersync/lib-services-framework'; -import * as util from '../../util/util-index.js'; import { authUser } from '../auth.js'; import { routeDefinition } from '../router.js'; @@ -13,13 +12,36 @@ export const writeCheckpoint = routeDefinition({ authorize: authUser, validator: schema.createTsCodecValidator(WriteCheckpointRequest, { allowAdditional: true }), handler: async (payload) => { - const system = payload.context.system; - const storage = system.storage; + const { + context: { service_context } + } = payload; + const api = service_context.syncAPIProvider.getSyncAPI(); + if (!api) { + throw new Error('No connection API handler is available.'); + } - const checkpoint = await util.getClientCheckpoint(system.requirePgPool(), storage); - return { - checkpoint - }; + // This old API needs a persisted checkpoint id. + // Since we don't use LSNs anymore, the only way to get that is to wait. + const start = Date.now(); + + const head = String(await api.getCheckpoint()); + + const timeout = 50_000; + + logger.info(`Waiting for LSN checkpoint: ${head}`); + while (Date.now() - start < timeout) { + const cp = await service_context.storage.getActiveCheckpoint(); + if (!cp.hasSyncRules()) { + throw new Error('No sync rules available'); + } + if (cp.lsn >= head) { + logger.info(`Got write checkpoint: ${head} : ${cp.checkpoint}`); + return { checkpoint: cp.checkpoint }; + } + + await new Promise((resolve) => setTimeout(resolve, 30)); + } + throw new Error('Timeout while waiting for checkpoint'); } }); @@ -29,11 +51,22 @@ export const writeCheckpoint2 = routeDefinition({ authorize: authUser, validator: schema.createTsCodecValidator(WriteCheckpointRequest, { allowAdditional: true }), handler: async (payload) => { - const { user_id, system } = payload.context; - const storage = system.storage; - const write_checkpoint = await util.createWriteCheckpoint(system.requirePgPool(), storage, user_id!); + const { user_id, service_context } = payload.context; + + const api = service_context.syncAPIProvider.getSyncAPI(); + if (!api) { + throw new Error('No connection API handler is available.'); + } + + // Might want to call this something link replicationHead or something else + const currentCheckpoint = await api.getCheckpoint(); + const { storage } = service_context; + + const id = await storage.createWriteCheckpoint(user_id!, { '1': String(currentCheckpoint) }); + logger.info(`Write checkpoint 2: ${JSON.stringify({ currentCheckpoint, id: String(id) })}`); + return { - write_checkpoint: String(write_checkpoint) + write_checkpoint: String(id) }; } }); diff --git a/packages/service-core/src/routes/endpoints/socket-route.ts b/packages/service-core/src/routes/endpoints/socket-route.ts index 7fd91e75a..f1de4cfa0 100644 --- a/packages/service-core/src/routes/endpoints/socket-route.ts +++ b/packages/service-core/src/routes/endpoints/socket-route.ts @@ -18,7 +18,8 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => }, validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }), handler: async ({ context, params, responder, observer, initialN }) => { - const { system } = context; + const { service_context } = context; + const { system } = service_context; if (system.closed) { responder.onError( @@ -36,7 +37,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => const syncParams = new RequestParameters(context.token_payload!, params.parameters ?? {}); - const storage = system.storage; + const storage = service_context.storage; // Sanity check before we start the stream const cp = await storage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index f51ef6708..1a1121819 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -19,7 +19,8 @@ export const syncStreamed = routeDefinition({ authorize: authUser, validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }), handler: async (payload) => { - const system = payload.context.system; + const { service_context } = payload.context; + const { storage, system } = service_context; if (system.closed) { throw new errors.JourneyError({ @@ -32,7 +33,6 @@ export const syncStreamed = routeDefinition({ const params: util.StreamingSyncRequest = payload.params; const syncParams = new RequestParameters(payload.context.token_payload!, payload.params.parameters ?? {}); - const storage = system.storage; // Sanity check before we start the stream const cp = await storage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index e205a4a09..1bf80d2bf 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -2,10 +2,12 @@ import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; import { SyncAPIProvider } from '../api/SyncAPIProvider.js'; import { ResolvedPowerSyncConfig } from '../util/config/types.js'; import { BucketStorageFactory } from '../storage/BucketStorage.js'; +import { CorePowerSyncSystem } from './CorePowerSyncSystem.js'; export abstract class ServiceContext { abstract configuration: ResolvedPowerSyncConfig; abstract storage: BucketStorageFactory; abstract replicationEngine: ReplicationEngine; abstract syncAPIProvider: SyncAPIProvider; + abstract system: CorePowerSyncSystem; } diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index 66b972964..84a9bef11 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -25,7 +25,11 @@ export const dataSourceConfig = t.object({ /** Unique identifier for the connection - optional when a single connection is present. */ id: t.string.optional(), /** Additional meta tag for connection */ - tag: t.string.optional() + tag: t.string.optional(), + /** + * Allows for debug query execution + */ + debug_enabled: t.boolean.optional() }); export type DataSourceConfig = t.Decoded; From cf566523d1ee6c90b5a68ff31a3c8f48a4bd7255 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 23 Jul 2024 17:13:21 +0200 Subject: [PATCH 024/298] sync rules routes --- .../src/routes/endpoints/sync-rules.ts | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index a5eedfb56..7c6613f24 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -1,12 +1,11 @@ import * as t from 'ts-codec'; import type { FastifyPluginAsync } from 'fastify'; -import * as pgwire from '@powersync/service-jpgwire'; import { errors, router, schema } from '@powersync/lib-services-framework'; import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules'; -import * as replication from '../../replication/replication-index.js'; import { authApi } from '../auth.js'; import { routeDefinition } from '../router.js'; +import { ServiceContext } from '../../system/ServiceContext.js'; const DeploySyncRulesRequest = t.object({ content: t.string @@ -39,7 +38,10 @@ export const deploySyncRules = routeDefinition({ plugins: [yamlPlugin], validator: schema.createTsCodecValidator(DeploySyncRulesRequest, { allowAdditional: true }), handler: async (payload) => { - if (payload.context.system.config.sync_rules.present) { + const { + service_context: { storage, system } + } = payload.context; + if (system.config.sync_rules.present) { // If sync rules are configured via the config, disable deploy via the API. throw new errors.JourneyError({ status: 422, @@ -61,7 +63,7 @@ export const deploySyncRules = routeDefinition({ }); } - const sync_rules = await payload.context.system.storage.updateSyncRules({ + const sync_rules = await storage.updateSyncRules({ content: content }); @@ -85,7 +87,7 @@ export const validateSyncRules = routeDefinition({ handler: async (payload) => { const content = payload.params.content; - const info = await debugSyncRules(payload.context.system.requirePgPool(), content); + const info = await debugSyncRules(payload.context.service_context, content); return replyPrettyJson(info); } @@ -96,7 +98,8 @@ export const currentSyncRules = routeDefinition({ method: router.HTTPMethod.GET, authorize: authApi, handler: async (payload) => { - const storage = payload.context.system.storage; + const { service_context } = payload.context; + const { storage, system } = service_context; const sync_rules = await storage.getActiveSyncRulesContent(); if (!sync_rules) { throw new errors.JourneyError({ @@ -105,12 +108,10 @@ export const currentSyncRules = routeDefinition({ description: 'No active sync rules' }); } - const info = await debugSyncRules(payload.context.system.requirePgPool(), sync_rules.sync_rules_content); + const info = await debugSyncRules(service_context, sync_rules.sync_rules_content); const next = await storage.getNextSyncRulesContent(); - const next_info = next - ? await debugSyncRules(payload.context.system.requirePgPool(), next.sync_rules_content) - : null; + const next_info = next ? await debugSyncRules(service_context, next.sync_rules_content) : null; const response = { current: { @@ -140,7 +141,7 @@ export const reprocessSyncRules = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(ReprocessSyncRulesRequest), handler: async (payload) => { - const storage = payload.context.system.storage; + const { storage } = payload.context.service_context; const sync_rules = await storage.getActiveSyncRules(); if (sync_rules == null) { throw new errors.JourneyError({ @@ -169,15 +170,17 @@ function replyPrettyJson(payload: any) { }); } -async function debugSyncRules(db: pgwire.PgClient, sync_rules: string) { +async function debugSyncRules(serviceContext: ServiceContext, sync_rules: string) { try { const rules = SqlSyncRules.fromYaml(sync_rules); const source_table_patterns = rules.getSourceTables(); - const wc = new replication.WalConnection({ - db: db, - sync_rules: rules - }); - const resolved_tables = await wc.getDebugTablesInfo(source_table_patterns); + + const api = serviceContext.syncAPIProvider.getSyncAPI(); + if (!api) { + throw new Error('No API handler found'); + } + + const resolved_tables = await api.getDebugTablesInfo(source_table_patterns, rules); return { valid: true, From 83378c451ce95e132f06f526130bc3da06889a42 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 24 Jul 2024 18:52:25 +0200 Subject: [PATCH 025/298] wip: service context and module setup --- .../src/api/{SyncAPI.ts => RouteAPI.ts} | 9 +- .../service-core/src/api/SyncAPIProvider.ts | 26 ---- packages/service-core/src/api/api-index.ts | 3 +- packages/service-core/src/api/diagnostics.ts | 19 ++- packages/service-core/src/api/schema.ts | 4 +- .../service-core/src/routes/RouterEngine.ts | 80 ++++++++++ packages/service-core/src/routes/auth.ts | 34 +++-- .../src/routes/endpoints/admin.ts | 14 +- .../src/routes/endpoints/checkpointing.ts | 6 +- .../src/routes/endpoints/socket-route.ts | 7 +- .../src/routes/endpoints/sync-rules.ts | 18 +-- .../src/routes/endpoints/sync-stream.ts | 6 +- .../service-core/src/routes/routes-index.ts | 1 + .../src/storage/StorageProvider.ts | 17 +++ .../src/storage/mongo/MongoStorageProvider.ts | 27 ++++ .../src/system/CorePowerSyncSystem.ts | 64 -------- .../service-core/src/system/ServiceContext.ts | 79 ++++++++-- .../service-core/src/system/system-index.ts | 1 - packages/service-core/src/util/config.ts | 12 +- service/src/entry.ts | 10 +- service/src/routes/router-config.ts | 132 ++++++++++++++++ service/src/runners/server.ts | 141 +++--------------- service/src/system/PowerSyncSystem.ts | 55 ------- 23 files changed, 424 insertions(+), 341 deletions(-) rename packages/service-core/src/api/{SyncAPI.ts => RouteAPI.ts} (93%) delete mode 100644 packages/service-core/src/api/SyncAPIProvider.ts create mode 100644 packages/service-core/src/routes/RouterEngine.ts create mode 100644 packages/service-core/src/storage/StorageProvider.ts create mode 100644 packages/service-core/src/storage/mongo/MongoStorageProvider.ts delete mode 100644 packages/service-core/src/system/CorePowerSyncSystem.ts create mode 100644 service/src/routes/router-config.ts delete mode 100644 service/src/system/PowerSyncSystem.ts diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/RouteAPI.ts similarity index 93% rename from packages/service-core/src/api/SyncAPI.ts rename to packages/service-core/src/api/RouteAPI.ts index c2c166601..63dccac56 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -4,14 +4,14 @@ import * as types from '@powersync/service-types'; /** * Describes all the methods currently required to service the sync API endpoints. */ -export interface SyncAPI { +export interface RouteAPI { /** * @returns basic identification of the connection */ getSourceConfig(): Promise; /** - * Checks the current connection status of the datasource. + * Checks the current connection status of the data source. * This is usually some test query to verify the source can be reached. */ getConnectionStatus(): Promise; @@ -81,8 +81,3 @@ interface PatternResult { tables?: types.TableInfo[]; table?: types.TableInfo; } - -export interface QueryResults { - columns: string[]; - rows: (string | number | boolean | null)[][]; -} diff --git a/packages/service-core/src/api/SyncAPIProvider.ts b/packages/service-core/src/api/SyncAPIProvider.ts deleted file mode 100644 index 9fb098d68..000000000 --- a/packages/service-core/src/api/SyncAPIProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { SyncAPI } from './SyncAPI.js'; -import { logger } from '@powersync/lib-services-framework'; - -/** - * Serves as a registry from which SyncAPIs can be retrieved based on Replication DataSource type - * Initially only one SyncAPI per DataSource type is supported - */ -export class SyncAPIProvider { - private api: SyncAPI | null; - - constructor() { - this.api = null; - } - - public register(api: SyncAPI) { - if (this.api) { - logger.warn('A SyncAPI has already been registered. Overriding existing implementation'); - } - - this.api = api; - } - - public getSyncAPI(): SyncAPI | null { - return this.api; - } -} diff --git a/packages/service-core/src/api/api-index.ts b/packages/service-core/src/api/api-index.ts index 5b73c18b8..0f90b1738 100644 --- a/packages/service-core/src/api/api-index.ts +++ b/packages/service-core/src/api/api-index.ts @@ -1,4 +1,3 @@ export * from './diagnostics.js'; +export * from './RouteAPI.js'; export * from './schema.js'; -export * from './SyncAPI.js'; -export * from './SyncAPIProvider.js'; diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index f83d6eede..c66d53455 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -1,9 +1,9 @@ +import { container, logger } from '@powersync/lib-services-framework'; import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/service-sync-rules'; import { SyncRulesStatus, TableInfo } from '@powersync/service-types'; -import { container, logger } from '@powersync/lib-services-framework'; -import { ServiceContext } from '../system/ServiceContext.js'; import * as storage from '../storage/storage-index.js'; +import { ServiceContext } from '../system/ServiceContext.js'; export interface DiagnosticsOptions { /** * Include sync rules content in response. @@ -33,8 +33,6 @@ export async function getSyncRulesStatus( return undefined; } - const serviceContext = container.getImplementation(ServiceContext); - const include_content = options.include_content ?? false; const live_status = options.live_status ?? false; const check_connection = options.check_connection ?? false; @@ -52,22 +50,23 @@ export async function getSyncRulesStatus( }; } + const serviceContext = container.getImplementation(ServiceContext); const { storage } = serviceContext; - const syncAPI = serviceContext.syncAPIProvider.getSyncAPI(); + const api = serviceContext.routerEngine.getAPI(); - const systemStorage = live_status ? await storage.getInstance(persisted) : undefined; + const systemStorage = live_status ? storage.getInstance(persisted) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; let tables_flat: TableInfo[] = []; if (check_connection) { - if (!syncAPI) { + if (!api) { throw new Error('No connection configured'); } const source_table_patterns = rules.getSourceTables(); - const resolved_tables = await syncAPI.getDebugTablesInfo(source_table_patterns, rules); + const resolved_tables = await api.getDebugTablesInfo(source_table_patterns, rules); tables_flat = resolved_tables.flatMap((info) => { if (info.table) { return [info.table]; @@ -80,7 +79,7 @@ export async function getSyncRulesStatus( if (systemStorage) { try { - replication_lag_bytes = await syncAPI.getReplicationLag(systemStorage.slot_name); + replication_lag_bytes = await api.getReplicationLag(systemStorage.slot_name); } catch (e) { // Ignore logger.warn(`Unable to get replication lag`, e); @@ -134,7 +133,7 @@ export async function getSyncRulesStatus( }) ); - const sourceConfig = await syncAPI?.getSourceConfig(); + const sourceConfig = await api?.getSourceConfig(); const tag = sourceConfig?.tag ?? DEFAULT_TAG; return { diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index 1e95ab021..244bb7146 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -4,9 +4,9 @@ import { internal_routes } from '@powersync/service-types'; import { ServiceContext } from '../system/ServiceContext.js'; export async function getConnectionsSchema(): Promise { - const { syncAPIProvider } = container.getImplementation(ServiceContext); + const { routerEngine } = container.getImplementation(ServiceContext); - const api = syncAPIProvider.getSyncAPI(); + const api = routerEngine.getAPI(); if (!api) { return { connections: [] diff --git a/packages/service-core/src/routes/RouterEngine.ts b/packages/service-core/src/routes/RouterEngine.ts new file mode 100644 index 000000000..1c5e68098 --- /dev/null +++ b/packages/service-core/src/routes/RouterEngine.ts @@ -0,0 +1,80 @@ +import { container, logger } from '@powersync/lib-services-framework'; + +import * as api from '../api/api-index.js'; + +export type RouterSetupResponse = { + onShutDown: () => Promise; +}; + +export type RouterSetup = () => Promise; + +/** + * Serves as a registry from which SyncAPIs can be retrieved based on Replication DataSource type + * Initially only one SyncAPI per DataSource type is supported + */ +export class RouterEngine { + closed: boolean; + + protected stopHandlers: Set<() => void>; + protected routerSetup: RouterSetup | null; + + private api: api.RouteAPI | null; + + constructor() { + this.api = null; + this.routerSetup = null; + this.stopHandlers = new Set(); + this.closed = false; + + /** + * This adds a termination handler to the begining of the queue + * A server termination handler should be added to run after this one with + * `handleTerminationSignalLast` + */ + container.terminationHandler.handleTerminationSignal(async () => { + // Close open streams, so that they don't block the server from closing. + // Note: This does not work well when streaming requests are queued. In that case, the server still doesn't + // close in the 30-second timeout. + this.closed = true; + logger.info(`Closing ${this.stopHandlers.size} streams`); + for (let handler of this.stopHandlers) { + handler(); + } + }); + } + + public register(api: api.RouteAPI) { + if (this.api) { + logger.warn('A SyncAPI has already been registered. Overriding existing implementation'); + } + + this.api = api; + } + + public getAPI(): api.RouteAPI | null { + return this.api; + } + + async initialize() { + if (!this.routerSetup) { + throw new Error(`Router setup procedure has not been registered`); + } + const { onShutDown } = await this.routerSetup(); + + // This will cause the router shutdown to occur after the stop handlers have completed + container.terminationHandler.handleTerminationSignalLast(async () => { + await onShutDown(); + }); + } + + addStopHandler(handler: () => void): () => void { + if (this.closed) { + handler(); + return () => {}; + } + this.stopHandlers.add(handler); + return () => { + this.stopHandlers.delete(handler); + }; + } +} diff --git a/packages/service-core/src/routes/auth.ts b/packages/service-core/src/routes/auth.ts index 896811b71..30de07d6f 100644 --- a/packages/service-core/src/routes/auth.ts +++ b/packages/service-core/src/routes/auth.ts @@ -1,9 +1,10 @@ import * as jose from 'jose'; +import { container } from '@powersync/lib-services-framework'; import * as auth from '../auth/auth-index.js'; +import { ServiceContext } from '../system/ServiceContext.js'; import * as util from '../util/util-index.js'; import { BasicRouterRequest, Context, RequestEndpointHandlerPayload } from './router.js'; -import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; export function endpoint(req: BasicRouterRequest) { const protocol = req.headers['x-forwarded-proto'] ?? req.protocol; @@ -108,7 +109,7 @@ export async function authorizeUser(context: Context, authHeader: string = '') { }; } - const { context: tokenContext, errors } = await generateContext(context.system, token); + const { context: tokenContext, errors } = await generateContext(token); if (!tokenContext) { return { @@ -121,14 +122,14 @@ export async function authorizeUser(context: Context, authHeader: string = '') { return { authorized: true }; } -export async function generateContext(system: CorePowerSyncSystem, token: string) { - const config = system.config; +export async function generateContext(token: string) { + const { configuration } = container.getImplementation(ServiceContext); let tokenPayload: auth.JwtPayload; try { - const maxAge = config.token_max_expiration; - tokenPayload = await system.client_keystore.verifyJwt(token, { - defaultAudiences: config.jwt_audiences, + const maxAge = configuration.token_max_expiration; + tokenPayload = await configuration.client_keystore.verifyJwt(token, { + defaultAudiences: configuration.jwt_audiences, maxAge: maxAge }); return { @@ -149,9 +150,14 @@ export async function generateContext(system: CorePowerSyncSystem, token: string * @deprecated */ export const authDevUser = async (payload: RequestEndpointHandlerPayload) => { - const context = payload.context; + const { + context: { + service_context: { configuration } + } + } = payload; + const token = getTokenFromHeader(payload.request.headers.authorization as string); - if (!context.system.config.dev.demo_auth) { + if (!configuration.dev.demo_auth) { return { authorized: false, errors: ['Authentication disabled'] @@ -170,7 +176,7 @@ export const authDevUser = async (payload: RequestEndpointHandlerPayload) => { let tokenPayload: auth.JwtPayload; try { - tokenPayload = await context.system.dev_client_keystore.verifyJwt(token, { + tokenPayload = await configuration.dev_client_keystore.verifyJwt(token, { defaultAudiences: audience, maxAge: '31d' }); @@ -186,8 +192,12 @@ export const authDevUser = async (payload: RequestEndpointHandlerPayload) => { }; export const authApi = (payload: RequestEndpointHandlerPayload) => { - const context = payload.context; - const api_keys = context.system.config.api_tokens; + const { + context: { + service_context: { configuration } + } + } = payload; + const api_keys = configuration.api_tokens; if (api_keys.length == 0) { return { authorized: false, diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index 396af0adb..ed9aa1221 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -4,9 +4,9 @@ import { internal_routes } from '@powersync/service-types'; import * as api from '../../api/api-index.js'; -import { routeDefinition } from '../router.js'; import { PersistedSyncRulesContent } from '../../storage/BucketStorage.js'; import { authApi } from '../auth.js'; +import { routeDefinition } from '../router.js'; const demoCredentials = routeDefinition({ path: '/api/admin/v1/demo-credentials', @@ -42,7 +42,7 @@ export const executeSql = routeDefinition({ } } = payload; - const api = payload.context.service_context.syncAPIProvider.getSyncAPI(); + const api = payload.context.service_context.routerEngine.getAPI(); const sourceConfig = await api?.getSourceConfig(); if (!sourceConfig?.debug_enabled) { return internal_routes.ExecuteSqlResponse.encode({ @@ -69,8 +69,8 @@ export const diagnostics = routeDefinition({ const { service_context } = context; const include_content = payload.params.sync_rules_content ?? false; - const syncAPI = service_context.syncAPIProvider.getSyncAPI(); - const status = await syncAPI?.getConnectionStatus(); + const apiHandler = service_context.routerEngine.getAPI(); + const status = await apiHandler?.getConnectionStatus(); if (!status) { return internal_routes.DiagnosticsResponse.encode({ connections: [] @@ -145,8 +145,8 @@ export const reprocess = routeDefinition({ content: active.sync_rules.content }); - const api = service_context.syncAPIProvider.getSyncAPI(); - const baseConfig = await api?.getSourceConfig(); + const apiHandler = service_context.routerEngine.getAPI(); + const baseConfig = await apiHandler?.getSourceConfig(); return internal_routes.ReprocessResponse.encode({ connections: [ @@ -192,7 +192,7 @@ export const validate = routeDefinition({ } }; - const apiHandler = service_context.syncAPIProvider.getSyncAPI(); + const apiHandler = service_context.routerEngine.getAPI(); const connectionStatus = await apiHandler?.getConnectionStatus(); if (!connectionStatus) { return internal_routes.ValidateResponse.encode({ diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index 640ca9b64..9f2a50df3 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -1,5 +1,5 @@ -import * as t from 'ts-codec'; import { logger, router, schema } from '@powersync/lib-services-framework'; +import * as t from 'ts-codec'; import { authUser } from '../auth.js'; import { routeDefinition } from '../router.js'; @@ -15,7 +15,7 @@ export const writeCheckpoint = routeDefinition({ const { context: { service_context } } = payload; - const api = service_context.syncAPIProvider.getSyncAPI(); + const api = service_context.routerEngine.getAPI(); if (!api) { throw new Error('No connection API handler is available.'); } @@ -53,7 +53,7 @@ export const writeCheckpoint2 = routeDefinition({ handler: async (payload) => { const { user_id, service_context } = payload.context; - const api = service_context.syncAPIProvider.getSyncAPI(); + const api = service_context.routerEngine.getAPI(); if (!api) { throw new Error('No connection API handler is available.'); } diff --git a/packages/service-core/src/routes/endpoints/socket-route.ts b/packages/service-core/src/routes/endpoints/socket-route.ts index f1de4cfa0..063bf7eff 100644 --- a/packages/service-core/src/routes/endpoints/socket-route.ts +++ b/packages/service-core/src/routes/endpoints/socket-route.ts @@ -19,9 +19,8 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }), handler: async ({ context, params, responder, observer, initialN }) => { const { service_context } = context; - const { system } = service_context; - - if (system.closed) { + const { routerEngine } = service_context; + if (routerEngine.closed) { responder.onError( new errors.JourneyError({ status: 503, @@ -62,7 +61,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => } }); - const removeStopHandler = system.addStopHandler(() => { + const removeStopHandler = routerEngine.addStopHandler(() => { observer.triggerCancel(); }); diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 7c6613f24..e952b7f80 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -1,11 +1,11 @@ -import * as t from 'ts-codec'; -import type { FastifyPluginAsync } from 'fastify'; import { errors, router, schema } from '@powersync/lib-services-framework'; import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules'; +import type { FastifyPluginAsync } from 'fastify'; +import * as t from 'ts-codec'; +import { ServiceContext } from '../../system/ServiceContext.js'; import { authApi } from '../auth.js'; import { routeDefinition } from '../router.js'; -import { ServiceContext } from '../../system/ServiceContext.js'; const DeploySyncRulesRequest = t.object({ content: t.string @@ -38,10 +38,10 @@ export const deploySyncRules = routeDefinition({ plugins: [yamlPlugin], validator: schema.createTsCodecValidator(DeploySyncRulesRequest, { allowAdditional: true }), handler: async (payload) => { - const { - service_context: { storage, system } - } = payload.context; - if (system.config.sync_rules.present) { + const { service_context } = payload.context; + const { storage } = service_context; + + if (service_context.configuration.sync_rules.present) { // If sync rules are configured via the config, disable deploy via the API. throw new errors.JourneyError({ status: 422, @@ -99,7 +99,7 @@ export const currentSyncRules = routeDefinition({ authorize: authApi, handler: async (payload) => { const { service_context } = payload.context; - const { storage, system } = service_context; + const { storage } = service_context; const sync_rules = await storage.getActiveSyncRulesContent(); if (!sync_rules) { throw new errors.JourneyError({ @@ -175,7 +175,7 @@ async function debugSyncRules(serviceContext: ServiceContext, sync_rules: string const rules = SqlSyncRules.fromYaml(sync_rules); const source_table_patterns = rules.getSourceTables(); - const api = serviceContext.syncAPIProvider.getSyncAPI(); + const api = serviceContext.routerEngine.getAPI(); if (!api) { throw new Error('No API handler found'); } diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index 1a1121819..9b8eceaff 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -20,9 +20,9 @@ export const syncStreamed = routeDefinition({ validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }), handler: async (payload) => { const { service_context } = payload.context; - const { storage, system } = service_context; + const { routerEngine, storage } = service_context; - if (system.closed) { + if (routerEngine.closed) { throw new errors.JourneyError({ status: 503, code: 'SERVICE_UNAVAILABLE', @@ -60,7 +60,7 @@ export const syncStreamed = routeDefinition({ { objectMode: false, highWaterMark: 16 * 1024 } ); - const deregister = system.addStopHandler(() => { + const deregister = routerEngine.addStopHandler(() => { // This error is not currently propagated to the client controller.abort(); stream.destroy(new Error('Shutting down system')); diff --git a/packages/service-core/src/routes/routes-index.ts b/packages/service-core/src/routes/routes-index.ts index fe07904a6..7cc2d7dba 100644 --- a/packages/service-core/src/routes/routes-index.ts +++ b/packages/service-core/src/routes/routes-index.ts @@ -4,3 +4,4 @@ export * as hooks from './hooks.js'; export * from './route-register.js'; export * from './router-socket.js'; export * from './router.js'; +export * from './RouterEngine.js'; diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts new file mode 100644 index 000000000..1728c6d17 --- /dev/null +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -0,0 +1,17 @@ +import { BucketStorageFactory } from '../storage/BucketStorage.js'; + +export type GeneratedStorage = { + storage: BucketStorageFactory; + disposer: () => Promise; +}; + +export type BaseStorageConfig = { + slot_name_prefix: string; +}; + +export interface StorageProvider { + // The storage type + type: string; + + generate(config: StorageConfig): Promise; +} diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts new file mode 100644 index 000000000..5ba9ebad8 --- /dev/null +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -0,0 +1,27 @@ +import { configFile } from '@powersync/service-types'; + +import * as db from '../../db/db-index.js'; + +import { MongoBucketStorage } from '../MongoBucketStorage.js'; +import { BaseStorageConfig, GeneratedStorage, StorageProvider } from '../StorageProvider.js'; +import { PowerSyncMongo } from './db.js'; + +export type MongoStorageConfig = configFile.StorageConfig & BaseStorageConfig; +export class MongoStorageProvider implements StorageProvider { + get type() { + return 'mongo'; + } + + async generate(config: MongoStorageConfig): Promise { + const client = db.mongo.createMongoClient(config); + + const database = new PowerSyncMongo(client, { database: config.database }); + + return { + storage: new MongoBucketStorage(database, { + slot_name_prefix: config.slot_name_prefix + }), + disposer: () => client.close() + }; + } +} diff --git a/packages/service-core/src/system/CorePowerSyncSystem.ts b/packages/service-core/src/system/CorePowerSyncSystem.ts deleted file mode 100644 index 8c37f137a..000000000 --- a/packages/service-core/src/system/CorePowerSyncSystem.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import { LifeCycledSystem, container, logger } from '@powersync/lib-services-framework'; - -import * as storage from '../storage/storage-index.js'; -import * as utils from '../util/util-index.js'; - -export abstract class CorePowerSyncSystem extends LifeCycledSystem { - abstract storage: storage.BucketStorageFactory; - abstract pgwire_pool?: pgwire.PgClient; - closed: boolean; - - protected stopHandlers: Set<() => void> = new Set(); - - constructor(public config: utils.ResolvedPowerSyncConfig) { - super(); - this.closed = false; - } - - get client_keystore() { - return this.config.client_keystore; - } - - get dev_client_keystore() { - return this.config.dev_client_keystore; - } - - /** - * Adds a termination handler which will call handlers registered via - * [addStopHandler]. - * This should be called after the server is started and it's termination handler is added. - * This is so that the handler is run before the server's handler, allowing streams to be interrupted on exit - */ - addTerminationHandler() { - container.terminationHandler.handleTerminationSignal(async () => { - // Close open streams, so that they don't block the server from closing. - // Note: This does not work well when streaming requests are queued. In that case, the server still doesn't - // close in the 30-second timeout. - this.closed = true; - logger.info(`Closing ${this.stopHandlers.size} streams`); - for (let handler of this.stopHandlers) { - handler(); - } - }); - } - - addStopHandler(handler: () => void): () => void { - if (this.closed) { - handler(); - return () => {}; - } - this.stopHandlers.add(handler); - return () => { - this.stopHandlers.delete(handler); - }; - } - - requirePgPool() { - if (this.pgwire_pool == null) { - throw new Error('No source connection configured'); - } else { - return this.pgwire_pool!; - } - } -} diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index 1bf80d2bf..b16c81703 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -1,13 +1,72 @@ +import { LifeCycledSystem } from '@powersync/lib-services-framework'; import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; -import { SyncAPIProvider } from '../api/SyncAPIProvider.js'; -import { ResolvedPowerSyncConfig } from '../util/config/types.js'; +import { RouterEngine } from '../routes/RouterEngine.js'; import { BucketStorageFactory } from '../storage/BucketStorage.js'; -import { CorePowerSyncSystem } from './CorePowerSyncSystem.js'; - -export abstract class ServiceContext { - abstract configuration: ResolvedPowerSyncConfig; - abstract storage: BucketStorageFactory; - abstract replicationEngine: ReplicationEngine; - abstract syncAPIProvider: SyncAPIProvider; - abstract system: CorePowerSyncSystem; +import { StorageProvider } from '../storage/StorageProvider.js'; +import { MongoStorageProvider } from '../storage/mongo/MongoStorageProvider.js'; +import { ResolvedPowerSyncConfig } from '../util/config/types.js'; + +export class ServiceContext extends LifeCycledSystem { + private _storage: BucketStorageFactory | null; + private _replicationEngine: ReplicationEngine | null; + + protected storageProviders: Map; + + routerEngine: RouterEngine; + + constructor(public configuration: ResolvedPowerSyncConfig) { + super(); + this._storage = null; + this._replicationEngine = null; + this.storageProviders = new Map(); + this.routerEngine = new RouterEngine(); + + this.registerStorageProvider(new MongoStorageProvider()); + } + + get storage(): BucketStorageFactory { + if (!this._storage) { + throw new Error(`Attempt to use storage before [initialize] has been called`); + } + return this._storage; + } + + get replicationEngine(): ReplicationEngine { + if (!this._replicationEngine) { + throw new Error(`Attempt to use replicationEngine before [initialize] has been called`); + } + return this._replicationEngine; + } + + /** + * Register a provider which generates a {@link BucketStorageFactory} + * given the matching config specified in the loaded {@link ResolvedPowerSyncConfig} + */ + registerStorageProvider(provider: StorageProvider) { + this.storageProviders.set(provider.type, provider); + } + + async initialize() { + const { storage: storageConfig } = this.configuration; + const { type } = storageConfig; + const provider = this.storageProviders.get(type); + + if (!provider) { + throw new Error(`No storage provider registered for type: ${type}`); + } + + const { storage, disposer } = await provider.generate({ + ...storageConfig, + slot_name_prefix: this.configuration.slot_name_prefix + }); + + this._storage = storage; + this.withLifecycle(storage, { + stop: () => disposer() + }); + + this._replicationEngine = new ReplicationEngine({ + storage + }); + } } diff --git a/packages/service-core/src/system/system-index.ts b/packages/service-core/src/system/system-index.ts index a64b0b482..cf72dd126 100644 --- a/packages/service-core/src/system/system-index.ts +++ b/packages/service-core/src/system/system-index.ts @@ -1,2 +1 @@ -export * from './CorePowerSyncSystem.js'; export * from './ServiceContext.js'; diff --git a/packages/service-core/src/util/config.ts b/packages/service-core/src/util/config.ts index 36aa003d4..d5663cb92 100644 --- a/packages/service-core/src/util/config.ts +++ b/packages/service-core/src/util/config.ts @@ -1,11 +1,17 @@ import * as fs from 'fs/promises'; -import { ResolvedPowerSyncConfig, RunnerConfig } from './config/types.js'; +import { container } from '@powersync/lib-services-framework'; +import { ServiceContext } from '../system/ServiceContext.js'; import { CompoundConfigCollector } from './config/compound-config-collector.js'; +import { ResolvedPowerSyncConfig, RunnerConfig } from './config/types.js'; -export function loadConfig(runnerConfig: RunnerConfig = {}) { +export async function loadConfig(runnerConfig: RunnerConfig = {}) { const collector = new CompoundConfigCollector(); - return collector.collectConfig(runnerConfig); + const config = await collector.collectConfig(runnerConfig); + const serviceContext = new ServiceContext(config); + container.register(ServiceContext, serviceContext); + await serviceContext.initialize(); + return config; } export async function loadSyncRules(config: ResolvedPowerSyncConfig): Promise { diff --git a/service/src/entry.ts b/service/src/entry.ts index ae57cccd7..5e7d5096a 100644 --- a/service/src/entry.ts +++ b/service/src/entry.ts @@ -1,12 +1,18 @@ -import { entry, utils } from '@powersync/service-core'; +import { container, ContainerImplementation } from '@powersync/lib-services-framework'; +import { entry, modules, utils } from '@powersync/service-core'; import { startServer } from './runners/server.js'; import { startStreamWorker } from './runners/stream-worker.js'; -import { container, ContainerImplementation } from '@powersync/lib-services-framework'; import { createSentryReporter } from './util/alerting.js'; container.registerDefaults(); container.register(ContainerImplementation.REPORTER, createSentryReporter()); +const moduleManager = new modules.ModuleManager(); +container.register(modules.ModuleManager, moduleManager); + +// TODO register Postgres module +moduleManager.register([]); + // Generate Commander CLI entry point program const { execute } = entry.generateEntryProgram({ [utils.ServiceRunner.API]: startServer, diff --git a/service/src/routes/router-config.ts b/service/src/routes/router-config.ts new file mode 100644 index 000000000..05d071955 --- /dev/null +++ b/service/src/routes/router-config.ts @@ -0,0 +1,132 @@ +import cors from '@fastify/cors'; +import { container, errors, logger } from '@powersync/lib-services-framework'; +import * as core from '@powersync/service-core'; +import { RSocketRequestMeta } from '@powersync/service-rsocket-router'; +import { deserialize } from 'bson'; +import fastify from 'fastify'; +import { SocketRouter } from './router.js'; + +export const routerSetup: core.routes.RouterSetup = async () => { + const serviceContext = container.getImplementation(core.system.ServiceContext); + + const server = fastify.fastify(); + + /** + * Fastify creates an encapsulated context for each `.register` call. + * Creating a separate context here to separate the concurrency limits for Admin APIs + * and Sync Streaming routes. + * https://github.com/fastify/fastify/blob/main/docs/Reference/Encapsulation.md + */ + server.register(async function (childContext) { + core.routes.registerFastifyRoutes( + childContext, + async () => { + return { + user_id: undefined, + // Put here for convenience + service_context: serviceContext + }; + }, + [ + ...core.routes.endpoints.ADMIN_ROUTES, + ...core.routes.endpoints.CHECKPOINT_ROUTES, + ...core.routes.endpoints.DEV_ROUTES, + ...core.routes.endpoints.SYNC_RULES_ROUTES + ] + ); + // Limit the active concurrent requests + childContext.addHook( + 'onRequest', + core.routes.hooks.createRequestQueueHook({ + max_queue_depth: 20, + concurrency: 10 + }) + ); + }); + + // Create a separate context for concurrency queueing + server.register(async function (childContext) { + core.routes.registerFastifyRoutes( + childContext, + async () => { + return { + user_id: undefined, + // Put here for convenience + service_context: container.getImplementation(core.system.ServiceContext) + }; + }, + [...core.routes.endpoints.SYNC_STREAM_ROUTES] + ); + // Limit the active concurrent requests + childContext.addHook( + 'onRequest', + core.routes.hooks.createRequestQueueHook({ + max_queue_depth: 0, + concurrency: 200 + }) + ); + }); + + server.register(cors, { + origin: '*', + allowedHeaders: ['Content-Type', 'Authorization'], + exposedHeaders: ['Content-Type'], + // Cache time for preflight response + maxAge: 3600 + }); + + SocketRouter.applyWebSocketEndpoints(server.server, { + contextProvider: async (data: Buffer) => { + const { token } = core.routes.RSocketContextMeta.decode(deserialize(data) as any); + + if (!token) { + throw new errors.ValidationError('No token provided in context'); + } + + const serviceContext = container.getImplementation(core.system.ServiceContext); + + try { + const extracted_token = core.routes.auth.getTokenFromHeader(token); + if (extracted_token != null) { + const { context, errors } = await core.routes.auth.generateContext(extracted_token); + return { + token, + ...context, + token_errors: errors, + // Put here for convenience + service_context: serviceContext + }; + } + } catch (ex) { + logger.error(ex); + } + + return { + token, + service_context: serviceContext + }; + }, + endpoints: [core.routes.endpoints.syncStreamReactive(SocketRouter)], + metaDecoder: async (meta: Buffer) => { + return RSocketRequestMeta.decode(deserialize(meta) as any); + }, + payloadDecoder: async (rawData?: Buffer) => rawData && deserialize(rawData) + }); + + const { port } = serviceContext.configuration; + + await server.listen({ + host: '0.0.0.0', + port + }); + + logger.info(`Running on port ${port}`); + + return { + onShutDown: async () => { + logger.info('Shutting down HTTP server...'); + await server.close(); + logger.info('HTTP server stopped'); + } + }; +}; diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index bb525a26a..264c96fb3 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -1,142 +1,41 @@ -import { deserialize } from 'bson'; -import fastify from 'fastify'; -import cors from '@fastify/cors'; +import { container, logger } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; -import { container, errors, logger } from '@powersync/lib-services-framework'; -import { RSocketRequestMeta } from '@powersync/service-rsocket-router'; -import { PowerSyncSystem } from '../system/PowerSyncSystem.js'; -import { SocketRouter } from '../routes/router.js'; /** * Starts an API server */ export async function startServer(runnerConfig: core.utils.RunnerConfig) { logger.info('Booting'); - const config = await core.utils.loadConfig(runnerConfig); - const system = new PowerSyncSystem(config); + await core.utils.loadConfig(runnerConfig); - const server = fastify.fastify(); + // TODO init module manager - /** - * Fastify creates an encapsulated context for each `.register` call. - * Creating a separate context here to separate the concurrency limits for Admin APIs - * and Sync Streaming routes. - * https://github.com/fastify/fastify/blob/main/docs/Reference/Encapsulation.md - */ - server.register(async function (childContext) { - core.routes.registerFastifyRoutes( - childContext, - async () => { - return { - user_id: undefined, - system: system - }; - }, - [ - ...core.routes.endpoints.ADMIN_ROUTES, - ...core.routes.endpoints.CHECKPOINT_ROUTES, - ...core.routes.endpoints.DEV_ROUTES, - ...core.routes.endpoints.SYNC_RULES_ROUTES - ] - ); - // Limit the active concurrent requests - childContext.addHook( - 'onRequest', - core.routes.hooks.createRequestQueueHook({ - max_queue_depth: 20, - concurrency: 10 - }) - ); - }); - - // Create a separate context for concurrency queueing - server.register(async function (childContext) { - core.routes.registerFastifyRoutes( - childContext, - async () => { - return { - user_id: undefined, - system: system - }; - }, - [...core.routes.endpoints.SYNC_STREAM_ROUTES] - ); - // Limit the active concurrent requests - childContext.addHook( - 'onRequest', - core.routes.hooks.createRequestQueueHook({ - max_queue_depth: 0, - concurrency: 200 - }) - ); - }); - - server.register(cors, { - origin: '*', - allowedHeaders: ['Content-Type', 'Authorization'], - exposedHeaders: ['Content-Type'], - // Cache time for preflight response - maxAge: 3600 - }); - - SocketRouter.applyWebSocketEndpoints(server.server, { - contextProvider: async (data: Buffer) => { - const { token } = core.routes.RSocketContextMeta.decode(deserialize(data) as any); - - if (!token) { - throw new errors.ValidationError('No token provided in context'); - } - - try { - const extracted_token = core.routes.auth.getTokenFromHeader(token); - if (extracted_token != null) { - const { context, errors } = await core.routes.auth.generateContext(system, extracted_token); - return { - token, - ...context, - token_errors: errors, - system - }; - } - } catch (ex) { - logger.error(ex); - } - - return { - token, - system - }; - }, - endpoints: [core.routes.endpoints.syncStreamReactive(SocketRouter)], - metaDecoder: async (meta: Buffer) => { - return RSocketRequestMeta.decode(deserialize(meta) as any); + const serviceContext = container.getImplementation(core.system.ServiceContext); + serviceContext.withLifecycle(serviceContext.storage, { + async start(storage) { + const instanceId = await storage.getPowerSyncInstanceId(); + await core.Metrics.initialise({ + powersync_instance_id: instanceId, + disable_telemetry_sharing: serviceContext.configuration.telemetry.disable_telemetry_sharing, + internal_metrics_endpoint: serviceContext.configuration.telemetry.internal_service_endpoint + }); }, - payloadDecoder: async (rawData?: Buffer) => rawData && deserialize(rawData) + async stop() { + await core.Metrics.getInstance().shutdown(); + } }); - logger.info('Starting system'); - await system.start(); - logger.info('System started'); + logger.info('Starting service'); + await serviceContext.start(); core.Metrics.getInstance().configureApiMetrics(); - await server.listen({ - host: '0.0.0.0', - port: system.config.port - }); - - container.terminationHandler.handleTerminationSignal(async () => { - logger.info('Shutting down HTTP server...'); - await server.close(); - logger.info('HTTP server stopped'); - }); + // Start the router + await serviceContext.routerEngine.initialize(); - // MUST be after adding the termination handler above. - // This is so that the handler is run before the server's handler, allowing streams to be interrupted on exit - system.addTerminationHandler(); + logger.info('service started'); - logger.info(`Running on port ${system.config.port}`); await container.probes.ready(); // Enable in development to track memory usage: diff --git a/service/src/system/PowerSyncSystem.ts b/service/src/system/PowerSyncSystem.ts deleted file mode 100644 index 93024dca7..000000000 --- a/service/src/system/PowerSyncSystem.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { db, system, utils, storage, Metrics } from '@powersync/service-core'; -import * as pgwire from '@powersync/service-jpgwire'; - -export class PowerSyncSystem extends system.CorePowerSyncSystem { - storage: storage.BucketStorageFactory; - pgwire_pool?: pgwire.PgClient; - - constructor(public config: utils.ResolvedPowerSyncConfig) { - super(config); - - utils.setTags(config.metadata); - - const pgOptions = config.connection; - if (pgOptions != null) { - const pool = pgwire.connectPgWirePool(pgOptions, { - idleTimeout: 30_000 - }); - this.pgwire_pool = this.withLifecycle(pool, { - async start(pool) {}, - async stop(pool) { - await pool.end(); - } - }); - } - - if (config.storage.type == 'mongodb') { - const client = this.withLifecycle(db.mongo.createMongoClient(config.storage), { - async start(client) {}, - async stop(client) { - await client.close(); - } - }); - const database = new storage.PowerSyncMongo(client, { database: config.storage.database }); - this.storage = new storage.MongoBucketStorage(database, { - slot_name_prefix: config.slot_name_prefix - }); - } else { - throw new Error('No storage configured'); - } - - this.withLifecycle(this.storage, { - async start(storage) { - const instanceId = await storage.getPowerSyncInstanceId(); - await Metrics.initialise({ - powersync_instance_id: instanceId, - disable_telemetry_sharing: config.telemetry.disable_telemetry_sharing, - internal_metrics_endpoint: config.telemetry.internal_service_endpoint - }); - }, - async stop() { - await Metrics.getInstance().shutdown(); - } - }); - } -} From bea28dda45a4bd672604a8098b2644b97a7b4b2c Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 25 Jul 2024 10:02:42 +0200 Subject: [PATCH 026/298] wip: add service context to entry program --- modules/module-postgres/src/index.ts | 5 ++ .../src/module/PostgresModule.ts | 9 ++- packages/service-core/src/entry/cli-entry.ts | 10 ++- .../src/entry/commands/start-action.ts | 12 ++- .../service-core/src/migrations/migrations.ts | 21 +++--- .../service-core/src/routes/RouterEngine.ts | 48 +++++++++--- packages/service-core/src/routes/router.ts | 1 + .../service-core/src/system/ServiceContext.ts | 73 +++++++++++++++---- .../service-core/src/util/config/types.ts | 5 +- service/package.json | 1 + service/src/entry.ts | 20 +++-- service/src/routes/router-config.ts | 14 ++-- service/src/runners/server.ts | 11 +-- service/src/runners/stream-worker.ts | 31 +++----- 14 files changed, 171 insertions(+), 90 deletions(-) diff --git a/modules/module-postgres/src/index.ts b/modules/module-postgres/src/index.ts index e69de29bb..04e83e4e6 100644 --- a/modules/module-postgres/src/index.ts +++ b/modules/module-postgres/src/index.ts @@ -0,0 +1,5 @@ +import { PostgresModule } from './module/PostgresModule.js'; + +export const module = new PostgresModule(); + +export default module; diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index f505c5036..5021c079e 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -2,11 +2,18 @@ import * as t from 'ts-codec'; import { api, replication } from '@powersync/service-core'; -import { normalizeConnectionConfig, PostgresConnectionConfig, ResolvedConnectionConfig } from '../types/types.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; import { PostgresSyncAPIAdapter } from '../replication/PostgresSyncAPIAdapter.js'; +import { normalizeConnectionConfig, PostgresConnectionConfig, ResolvedConnectionConfig } from '../types/types.js'; export class PostgresModule extends replication.ReplicationModule { + constructor() { + super({ + name: 'Postgres', + type: 'postgres' + }); + } + protected configSchema(): t.AnyCodec { // Intersection types have some limitations in codec typing return PostgresConnectionConfig; diff --git a/packages/service-core/src/entry/cli-entry.ts b/packages/service-core/src/entry/cli-entry.ts index 2ebeb7549..e3dc6afe0 100644 --- a/packages/service-core/src/entry/cli-entry.ts +++ b/packages/service-core/src/entry/cli-entry.ts @@ -1,10 +1,11 @@ import { Command } from 'commander'; +import { logger } from '@powersync/lib-services-framework'; +import * as system from '../system/system-index.js'; import * as utils from '../util/util-index.js'; import { registerMigrationAction } from './commands/migrate-action.js'; import { registerTearDownAction } from './commands/teardown-action.js'; import { registerStartAction } from './entry-index.js'; -import { logger } from '@powersync/lib-services-framework'; /** * Generates a Commander program which serves as the entry point @@ -12,7 +13,10 @@ import { logger } from '@powersync/lib-services-framework'; * This registers standard actions for teardown and migrations. * Optionally registers the start command handlers. */ -export function generateEntryProgram(startHandlers?: Record) { +export function generateEntryProgram( + serviceContext: system.ServiceContext, + startHandlers?: Record +) { const entryProgram = new Command(); entryProgram.name('powersync-runner').description('CLI to initiate a PowerSync service runner'); @@ -20,7 +24,7 @@ export function generateEntryProgram(startHandlers?: Record) { +export function registerStartAction( + program: Command, + serviceContext: system.ServiceContext, + handlers: Record +) { const startCommand = program.command(COMMAND_NAME); wrapConfigCommand(startCommand); @@ -18,7 +23,10 @@ export function registerStartAction(program: Command, handlers: Record { + // load the config + await serviceContext.initialize(extractRunnerOptions(options)); + const runner = handlers[options.runnerType as utils.ServiceRunner]; - await runner(extractRunnerOptions(options)); + await runner(serviceContext); }); } diff --git a/packages/service-core/src/migrations/migrations.ts b/packages/service-core/src/migrations/migrations.ts index 3a1343844..dd4917fb0 100644 --- a/packages/service-core/src/migrations/migrations.ts +++ b/packages/service-core/src/migrations/migrations.ts @@ -2,13 +2,14 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { fileURLToPath } from 'url'; +import { logger } from '@powersync/lib-services-framework'; import * as db from '../db/db-index.js'; -import * as util from '../util/util-index.js'; import * as locks from '../locks/locks-index.js'; +import * as system from '../system/system-index.js'; +import * as util from '../util/util-index.js'; import { Direction } from './definitions.js'; -import { createMongoMigrationStore } from './store/migration-store.js'; import { execute, writeLogsToStore } from './executor.js'; -import { logger } from '@powersync/lib-services-framework'; +import { createMongoMigrationStore } from './store/migration-store.js'; const DEFAULT_MONGO_LOCK_COLLECTION = 'locks'; const MONGO_LOCK_PROCESS = 'migrations'; @@ -20,21 +21,21 @@ const MIGRATIONS_DIR = path.join(__dirname, '/db/migrations'); export type MigrationOptions = { direction: Direction; - runner_config: util.RunnerConfig; + serviceContext: system.ServiceContext; }; /** * Loads migrations and injects a custom context for loading the specified * runner configuration. */ -const loadMigrations = async (dir: string, runner_config: util.RunnerConfig) => { +const loadMigrations = async (dir: string, serviceContext: system.ServiceContext) => { const files = await fs.readdir(dir); const migrations = files.filter((file) => { return path.extname(file) === '.js'; }); const context: util.MigrationContext = { - runner_config + serviceContext }; return await Promise.all( @@ -53,15 +54,15 @@ const loadMigrations = async (dir: string, runner_config: util.RunnerConfig) => * Runs migration scripts exclusively using Mongo locks */ export const migrate = async (options: MigrationOptions) => { - const { direction, runner_config } = options; + const { direction, serviceContext } = options; /** * Try and get Mongo from config file. * But this might not be available in Journey Micro as we use the standard Mongo. */ - const config = await util.loadConfig(runner_config); - const { storage } = config; + const { configuration } = serviceContext; + const { storage } = configuration; const client = db.mongo.createMongoClient(storage); logger.info('Connecting to MongoDB'); @@ -96,7 +97,7 @@ export const migrate = async (options: MigrationOptions) => { try { logger.info('Loading migrations'); - const migrations = await loadMigrations(MIGRATIONS_DIR, runner_config); + const migrations = await loadMigrations(MIGRATIONS_DIR, serviceContext); // Use the provided config to connect to Mongo const store = createMongoMigrationStore(clientDB); diff --git a/packages/service-core/src/routes/RouterEngine.ts b/packages/service-core/src/routes/RouterEngine.ts index 1c5e68098..9749d4382 100644 --- a/packages/service-core/src/routes/RouterEngine.ts +++ b/packages/service-core/src/routes/RouterEngine.ts @@ -2,11 +2,23 @@ import { container, logger } from '@powersync/lib-services-framework'; import * as api from '../api/api-index.js'; +import { ADMIN_ROUTES } from './endpoints/admin.js'; +import { CHECKPOINT_ROUTES } from './endpoints/checkpointing.js'; +import { SYNC_RULES_ROUTES } from './endpoints/sync-rules.js'; +import { SYNC_STREAM_ROUTES } from './endpoints/sync-stream.js'; +import { RouteDefinition } from './router.js'; + export type RouterSetupResponse = { - onShutDown: () => Promise; + onShutdown: () => Promise; +}; + +export type RouterEngineRoutes = { + apiRoutes: RouteDefinition[]; + streamRoutes: RouteDefinition[]; + socketRoutes: RouteDefinition[]; }; -export type RouterSetup = () => Promise; +export type RouterSetup = (routes: RouterEngineRoutes) => Promise; /** * Serves as a registry from which SyncAPIs can be retrieved based on Replication DataSource type @@ -15,17 +27,29 @@ export type RouterSetup = () => Promise; export class RouterEngine { closed: boolean; - protected stopHandlers: Set<() => void>; - protected routerSetup: RouterSetup | null; + /** + * The reference itself is readonly, but users should eventually + * be able to add their own route definitions. + */ + readonly routes: RouterEngineRoutes; + protected stopHandlers: Set<() => void>; private api: api.RouteAPI | null; constructor() { this.api = null; - this.routerSetup = null; this.stopHandlers = new Set(); this.closed = false; + // Default routes + this.routes = { + apiRoutes: [...ADMIN_ROUTES, ...CHECKPOINT_ROUTES, ...SYNC_RULES_ROUTES], + streamRoutes: [...SYNC_STREAM_ROUTES], + socketRoutes: [ + // TODO + ] + }; + /** * This adds a termination handler to the begining of the queue * A server termination handler should be added to run after this one with @@ -43,7 +67,7 @@ export class RouterEngine { }); } - public register(api: api.RouteAPI) { + public registerAPI(api: api.RouteAPI) { if (this.api) { logger.warn('A SyncAPI has already been registered. Overriding existing implementation'); } @@ -55,15 +79,15 @@ export class RouterEngine { return this.api; } - async initialize() { - if (!this.routerSetup) { - throw new Error(`Router setup procedure has not been registered`); - } - const { onShutDown } = await this.routerSetup(); + /** + * Starts the router given the configuration provided + */ + async start(setup: RouterSetup) { + const { onShutdown } = await setup(this.routes); // This will cause the router shutdown to occur after the stop handlers have completed container.terminationHandler.handleTerminationSignalLast(async () => { - await onShutDown(); + await onShutdown(); }); } diff --git a/packages/service-core/src/routes/router.ts b/packages/service-core/src/routes/router.ts index a2435278a..f83f343af 100644 --- a/packages/service-core/src/routes/router.ts +++ b/packages/service-core/src/routes/router.ts @@ -36,6 +36,7 @@ export type RequestEndpointHandlerPayload< request: Request; }; +export type RouteDefinition = RequestEndpoint; /** * Helper function for making generics work well when defining routes */ diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index b16c81703..bdee334e8 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -1,29 +1,46 @@ -import { LifeCycledSystem } from '@powersync/lib-services-framework'; +import { LifeCycledSystem, ServiceIdentifier, container } from '@powersync/lib-services-framework'; import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; import { RouterEngine } from '../routes/RouterEngine.js'; import { BucketStorageFactory } from '../storage/BucketStorage.js'; import { StorageProvider } from '../storage/StorageProvider.js'; import { MongoStorageProvider } from '../storage/mongo/MongoStorageProvider.js'; -import { ResolvedPowerSyncConfig } from '../util/config/types.js'; +import { ResolvedPowerSyncConfig, RunnerConfig } from '../util/config/types.js'; +import { CompoundConfigCollector } from '../util/util-index.js'; +/** + * Context which allows for registering and getting implementations + * of various service engines. + * This controls registering, initializing and the lifecycle of various services. + */ export class ServiceContext extends LifeCycledSystem { private _storage: BucketStorageFactory | null; - private _replicationEngine: ReplicationEngine | null; + private _configuration: ResolvedPowerSyncConfig | null; protected storageProviders: Map; - routerEngine: RouterEngine; + // TODO metrics - constructor(public configuration: ResolvedPowerSyncConfig) { + constructor() { super(); + this._storage = null; - this._replicationEngine = null; + this._configuration = null; + this.storageProviders = new Map(); - this.routerEngine = new RouterEngine(); + container.register(RouterEngine, new RouterEngine()); + container.register(CompoundConfigCollector, new CompoundConfigCollector()); + // Mongo storage is available as an option by default this.registerStorageProvider(new MongoStorageProvider()); } + get configuration(): ResolvedPowerSyncConfig { + if (!this._configuration) { + throw new Error(`Attempt to use configuration before it has been collected`); + } + return this._configuration; + } + get storage(): BucketStorageFactory { if (!this._storage) { throw new Error(`Attempt to use storage before [initialize] has been called`); @@ -32,10 +49,30 @@ export class ServiceContext extends LifeCycledSystem { } get replicationEngine(): ReplicationEngine { - if (!this._replicationEngine) { - throw new Error(`Attempt to use replicationEngine before [initialize] has been called`); - } - return this._replicationEngine; + return container.getImplementation(ReplicationEngine); + } + + get routerEngine(): RouterEngine { + return container.getImplementation(RouterEngine); + } + + get configCollector(): CompoundConfigCollector { + return container.getImplementation(CompoundConfigCollector); + } + + /** + * Allows for registering core and generic implementations of services/helpers. + * This uses the framework container under the hood. + */ + register(identifier: ServiceIdentifier, implementation: T) { + container.register(identifier, implementation); + } + + /** + * Gets the implementation of an identifiable service. + */ + get(identifier: ServiceIdentifier) { + return container.getImplementation(identifier); } /** @@ -46,7 +83,10 @@ export class ServiceContext extends LifeCycledSystem { this.storageProviders.set(provider.type, provider); } - async initialize() { + async initialize(entryConfig: RunnerConfig) { + // Collect the config + this._configuration = await this.configCollector.collectConfig(entryConfig); + const { storage: storageConfig } = this.configuration; const { type } = storageConfig; const provider = this.storageProviders.get(type); @@ -65,8 +105,11 @@ export class ServiceContext extends LifeCycledSystem { stop: () => disposer() }); - this._replicationEngine = new ReplicationEngine({ - storage - }); + container.register( + ReplicationEngine, + new ReplicationEngine({ + storage + }) + ); } } diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index 22e5428fb..951ffa2e2 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -1,6 +1,7 @@ import { configFile } from '@powersync/service-types'; import { KeySpec } from '../../auth/KeySpec.js'; import { KeyStore } from '../../auth/KeyStore.js'; +import { ServiceContext } from '../../system/ServiceContext.js'; export enum ServiceRunner { UNIFIED = 'unified', @@ -15,10 +16,10 @@ export type RunnerConfig = { }; export type MigrationContext = { - runner_config: RunnerConfig; + serviceContext: ServiceContext; }; -export type Runner = (config: RunnerConfig) => Promise; +export type Runner = (serviceContext: ServiceContext) => Promise; export type SyncRulesConfig = { present: boolean; diff --git a/service/package.json b/service/package.json index c4dfe4036..be917078d 100644 --- a/service/package.json +++ b/service/package.json @@ -16,6 +16,7 @@ "@opentelemetry/sdk-metrics": "^1.17.0", "@powersync/service-core": "workspace:*", "@powersync/lib-services-framework": "workspace:*", + "@powersync/service-module-postgres": "workapce:*", "@powersync/service-jpgwire": "workspace:*", "@powersync/service-jsonbig": "workspace:*", "@powersync/service-rsocket-router": "workspace:*", diff --git a/service/src/entry.ts b/service/src/entry.ts index 5e7d5096a..1485810ea 100644 --- a/service/src/entry.ts +++ b/service/src/entry.ts @@ -1,25 +1,29 @@ import { container, ContainerImplementation } from '@powersync/lib-services-framework'; -import { entry, modules, utils } from '@powersync/service-core'; +import { entry, modules, system, utils } from '@powersync/service-core'; +import PostgresModule from '@powersync/service-module-postgres'; import { startServer } from './runners/server.js'; import { startStreamWorker } from './runners/stream-worker.js'; import { createSentryReporter } from './util/alerting.js'; +// Initialize framework components container.registerDefaults(); container.register(ContainerImplementation.REPORTER, createSentryReporter()); +const serviceContext = new system.ServiceContext(); const moduleManager = new modules.ModuleManager(); -container.register(modules.ModuleManager, moduleManager); -// TODO register Postgres module -moduleManager.register([]); +moduleManager.register([PostgresModule]); + +// TODO this can't happen here. The service context needs to be initialized first +await moduleManager.initialize(serviceContext); // Generate Commander CLI entry point program -const { execute } = entry.generateEntryProgram({ +const { execute } = entry.generateEntryProgram(serviceContext, { [utils.ServiceRunner.API]: startServer, [utils.ServiceRunner.SYNC]: startStreamWorker, - [utils.ServiceRunner.UNIFIED]: async (config: utils.RunnerConfig) => { - await startServer(config); - await startStreamWorker(config); + [utils.ServiceRunner.UNIFIED]: async (serviceContext) => { + await startServer(serviceContext); + await startStreamWorker(serviceContext); } }); diff --git a/service/src/routes/router-config.ts b/service/src/routes/router-config.ts index 05d071955..fa709a03e 100644 --- a/service/src/routes/router-config.ts +++ b/service/src/routes/router-config.ts @@ -6,7 +6,7 @@ import { deserialize } from 'bson'; import fastify from 'fastify'; import { SocketRouter } from './router.js'; -export const routerSetup: core.routes.RouterSetup = async () => { +export const routerSetup: core.routes.RouterSetup = async (routes) => { const serviceContext = container.getImplementation(core.system.ServiceContext); const server = fastify.fastify(); @@ -27,12 +27,7 @@ export const routerSetup: core.routes.RouterSetup = async () => { service_context: serviceContext }; }, - [ - ...core.routes.endpoints.ADMIN_ROUTES, - ...core.routes.endpoints.CHECKPOINT_ROUTES, - ...core.routes.endpoints.DEV_ROUTES, - ...core.routes.endpoints.SYNC_RULES_ROUTES - ] + [...routes.apiRoutes] ); // Limit the active concurrent requests childContext.addHook( @@ -55,7 +50,7 @@ export const routerSetup: core.routes.RouterSetup = async () => { service_context: container.getImplementation(core.system.ServiceContext) }; }, - [...core.routes.endpoints.SYNC_STREAM_ROUTES] + [...routes.streamRoutes] ); // Limit the active concurrent requests childContext.addHook( @@ -106,6 +101,7 @@ export const routerSetup: core.routes.RouterSetup = async () => { service_context: serviceContext }; }, + // TODO endpoints: [core.routes.endpoints.syncStreamReactive(SocketRouter)], metaDecoder: async (meta: Buffer) => { return RSocketRequestMeta.decode(deserialize(meta) as any); @@ -123,7 +119,7 @@ export const routerSetup: core.routes.RouterSetup = async () => { logger.info(`Running on port ${port}`); return { - onShutDown: async () => { + onShutdown: async () => { logger.info('Shutting down HTTP server...'); await server.close(); logger.info('HTTP server stopped'); diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 264c96fb3..f8b870d24 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -1,17 +1,13 @@ import { container, logger } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; +import { routerSetup } from '../routes/router-config.js'; /** * Starts an API server */ -export async function startServer(runnerConfig: core.utils.RunnerConfig) { +export async function startServer(serviceContext: core.system.ServiceContext) { logger.info('Booting'); - await core.utils.loadConfig(runnerConfig); - - // TODO init module manager - - const serviceContext = container.getImplementation(core.system.ServiceContext); serviceContext.withLifecycle(serviceContext.storage, { async start(storage) { const instanceId = await storage.getPowerSyncInstanceId(); @@ -27,12 +23,13 @@ export async function startServer(runnerConfig: core.utils.RunnerConfig) { }); logger.info('Starting service'); + // TODO cleanup the initialization of metrics await serviceContext.start(); core.Metrics.getInstance().configureApiMetrics(); // Start the router - await serviceContext.routerEngine.initialize(); + await serviceContext.routerEngine.start(routerSetup); logger.info('service started'); diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index 96efef381..3887bb6ff 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -1,39 +1,28 @@ -import { migrations, replication, utils, Metrics } from '@powersync/service-core'; import { container, logger } from '@powersync/lib-services-framework'; +import { Metrics, migrations, replication, system } from '@powersync/service-core'; -import { PowerSyncSystem } from '../system/PowerSyncSystem.js'; - -export async function startStreamWorker(runnerConfig: utils.RunnerConfig) { +export async function startStreamWorker(serviceContext: system.ServiceContext) { logger.info('Booting'); - const config = await utils.loadConfig(runnerConfig); - // Self hosted version allows for automatic migrations - if (!config.migrations?.disable_auto_migration) { + if (!serviceContext.configuration.migrations?.disable_auto_migration) { await migrations.migrate({ direction: migrations.Direction.Up, - runner_config: runnerConfig + serviceContext }); } - const system = new PowerSyncSystem(config); + // TODO use replication engine + serviceContext.withLifecycle(new replication.WalStreamManager(system), { + start: (manager) => manager.start(), + stop: (manager) => manager.stop() + }); logger.info('Starting system'); - await system.start(); + await serviceContext.start(); logger.info('System started'); Metrics.getInstance().configureReplicationMetrics(system); - const mngr = new replication.WalStreamManager(system); - mngr.start(); - - // MUST be after startServer. - // This is so that the handler is run before the server's handler, allowing streams to be interrupted on exit - system.addTerminationHandler(); - - container.terminationHandler.handleTerminationSignal(async () => { - await mngr.stop(); - }); - await container.probes.ready(); } From 0fc624b1fcf2847c111a87685e5c09ae32968528 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 9 Jul 2024 15:54:20 +0200 Subject: [PATCH 027/298] Use already existing type definitions from the sync-rules package in jpqwire --- packages/jpgwire/package.json | 1 + packages/jpgwire/src/pgwire_types.ts | 21 +------------------ packages/jpgwire/tsconfig.json | 3 +++ .../service-core/src/replication/WalStream.ts | 6 +++--- 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/packages/jpgwire/package.json b/packages/jpgwire/package.json index 746c2dcd6..12ff0976a 100644 --- a/packages/jpgwire/package.json +++ b/packages/jpgwire/package.json @@ -20,6 +20,7 @@ "dependencies": { "@powersync/service-jsonbig": "workspace:^", "@powersync/service-types": "workspace:^", + "@powersync/service-sync-rules": "workspace:^", "date-fns": "^3.6.0", "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87" } diff --git a/packages/jpgwire/src/pgwire_types.ts b/packages/jpgwire/src/pgwire_types.ts index aa21d7fd5..a93aeba66 100644 --- a/packages/jpgwire/src/pgwire_types.ts +++ b/packages/jpgwire/src/pgwire_types.ts @@ -3,6 +3,7 @@ import type { PgoutputRelation } from 'pgwire/mod.js'; import { dateToSqlite, lsnMakeComparable, timestampToSqlite, timestamptzToSqlite } from './util.js'; import { JsonContainer } from '@powersync/service-jsonbig'; +import { DatabaseInputRow } from '@powersync/service-sync-rules'; export class PgType { static decode(text: string, typeOid: number) { @@ -253,23 +254,3 @@ export function decodeTuple(relation: PgoutputRelation, tupleRaw: Record): Generator { + static *getQueryData(results: Iterable): Generator { for (let row of results) { yield toSyncRulesRow(row); } @@ -387,7 +387,7 @@ WHERE oid = $1::regclass`, } const rows = chunk.rows.map((row) => { - let q: pgwire.DatabaseInputRow = {}; + let q: DatabaseInputRow = {}; for (let c of columns) { q[c.name] = row[c.i]; } From 2b86e2cef3d82941374c746702a5eee16a5e8c2f Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 9 Jul 2024 15:57:06 +0200 Subject: [PATCH 028/298] Introduced general DataSource config type to the PowerSync Config. --- packages/types/src/config/PowerSyncConfig.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index a0bf765b7..bf7467ed3 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -19,7 +19,16 @@ export const portParser = { }) }; -export const postgresConnection = t.object({ +export const dataSourceConfig = t.object({ + // Unique string identifier for the data source + type: t.string, + /** Unique identifier for the connection - optional when a single connection is present. */ + id: t.string.optional(), +}); + +export type DataSourceConfig = t.Decoded; + +export const postgresConnection = dataSourceConfig.and(t.object({ type: t.literal('postgresql'), /** Unique identifier for the connection - optional when a single connection is present. */ id: t.string.optional(), @@ -49,7 +58,7 @@ export const postgresConnection = t.object({ * Prefix for the slot name. Defaults to "powersync_" */ slot_name_prefix: t.string.optional() -}); +})); export type PostgresConnection = t.Decoded; @@ -95,7 +104,8 @@ export type StorageConfig = t.Decoded; export const powerSyncConfig = t.object({ replication: t .object({ - connections: t.array(postgresConnection).optional() + connections: t.array(postgresConnection).optional(), + data_sources: t.array(dataSourceConfig).optional() }) .optional(), From 1024cb01822a5a278eafbc7d9af4f86f7cf9d5a7 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:48:09 +0200 Subject: [PATCH 029/298] Add the base classes and interfaces for general modules --- .../src/modules/AbstractModule.ts | 24 ++++++++++++ .../service-core/src/modules/ModuleManager.ts | 38 +++++++++++++++++++ .../service-core/src/system/ServiceContext.ts | 11 ++++++ 3 files changed, 73 insertions(+) create mode 100644 packages/service-core/src/modules/AbstractModule.ts create mode 100644 packages/service-core/src/modules/ModuleManager.ts create mode 100644 packages/service-core/src/system/ServiceContext.ts diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts new file mode 100644 index 000000000..dfad51f97 --- /dev/null +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -0,0 +1,24 @@ +import { ServiceContext } from '../system/ServiceContext.js'; + +export abstract class AbstractModule { + public name: string; + + protected constructor(name: string) { + this.name = name; + } + + /** + * Initialize the module using any required services from the ServiceContext + */ + public abstract initialize(context: ServiceContext): Promise; + + /** + * Finish processing any requests and gracefully shut down any resources managed by the module + */ + public abstract shutdown(): Promise; + + /** + * Terminate and clean up any resources managed by the module right away + */ + public abstract teardown(): Promise; +} diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts new file mode 100644 index 000000000..72a0c4921 --- /dev/null +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -0,0 +1,38 @@ +import { logger } from '@powersync/lib-services-framework'; +import { AbstractModule } from './AbstractModule.js'; +import { ServiceContext } from '../system/ServiceContext.js'; + +/** + * The module manager is responsible for managing the lifecycle of all modules in the system. + */ +export class ModuleManager { + private readonly modules: Map = new Map(); + + public register(modules: AbstractModule[]) { + for (const module of modules) { + if (this.modules.has(module.name)) { + logger.warn(`Module ${module.name} already registered, skipping...`); + } else { + this.modules.set(module.name, module); + } + } + } + + async initialize(context: ServiceContext) { + for (const module of this.modules.values()) { + await module.initialize(context); + } + } + + async shutDown() { + for (const module of this.modules.values()) { + await module.shutdown(); + } + } + + async tearDown() { + for (const module of this.modules.values()) { + await module.teardown(); + } + } +} diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts new file mode 100644 index 000000000..478192944 --- /dev/null +++ b/packages/service-core/src/system/ServiceContext.ts @@ -0,0 +1,11 @@ +import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; +import { SyncAPIProvider } from '../api/SyncAPIProvider.js'; +import { ResolvedPowerSyncConfig } from '../util/config/types.js'; +import { BucketStorageFactory } from '../storage/BucketStorage.js'; + +export type ServiceContext = { + configuration: ResolvedPowerSyncConfig; + storage: BucketStorageFactory; + replicationEngine: ReplicationEngine; + syncAPIProvider: SyncAPIProvider; +}; From 2cb1efa024610ec91fcf9414aade25af1ece381e Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:52:08 +0200 Subject: [PATCH 030/298] Add the SyncAPI interface describing the functionality required by the routes. --- packages/service-core/src/api/SyncAPI.ts | 67 +++++++++++++++++++ .../service-core/src/api/SyncAPIProvider.ts | 26 +++++++ packages/service-core/src/api/api-index.ts | 2 + 3 files changed, 95 insertions(+) create mode 100644 packages/service-core/src/api/SyncAPI.ts create mode 100644 packages/service-core/src/api/SyncAPIProvider.ts diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts new file mode 100644 index 000000000..c0771b9f0 --- /dev/null +++ b/packages/service-core/src/api/SyncAPI.ts @@ -0,0 +1,67 @@ +import { TablePattern } from '@powersync/service-sync-rules'; +import * as types from '@powersync/service-types'; + +/** + * Describes all the methods currently required to service the sync API endpoints. + * TODO: This interface needs to be cleaned up and made more generic. It describes the current functionality required by our API routes + */ +export interface SyncAPI { + /** + * Performs diagnostics on the "connection" + * This is usually some test query to verify the source can be reached. + */ + getDiagnostics(): Promise<{ + connected: boolean; + errors?: Array<{ level: string; message: string }>; + }>; + + /** + * Generates replication table information from a given pattern of tables. + * + * @param tablePatterns A set of table patterns which typically come from + * the tables listed in sync rules definitions. + * + * @returns A result of all the tables and columns which should be replicated + * based off the input patterns. Certain tests are executed on the + * tables to ensure syncing should function according to the input + * pattern. Debug errors and warnings are reported per table. + */ + getDebugTablesInfo(tablePatterns: TablePattern[]): Promise; + + /** + * @returns The replication lag: that is the amount of data which has not been + * replicated yet, in bytes. + */ + getReplicationLag(): Promise; + + /** + * Get the current LSN or equivalent replication position identifier + */ + getCheckpoint(): Promise; + + /** + * @returns The schema for tables inside the connected database. This is typically + * used to validate sync rules. + * Side Note: https://github.com/powersync-ja/powersync-service/blob/33bbb8c0ab1c48555956593f427fc674a8f15768/packages/types/src/definitions.ts#L100 + * contains `pg_type` which we might need to deprecate and add another generic + * type field - or just use this field as the connection specific type. + */ + getConnectionSchema(): Promise; + + /** + * Executes a SQL statement and returns the result. This is currently used in the + * admin API which is exposed in Collide. + */ + executeSQL(sql: string, params: any[]): Promise; + + //CRUD API : I don't think this is used besides maybe niche dev use cases +} + +// TODO: Export this when the existing definition in WALConnection is removed +interface PatternResult { + schema: string; + pattern: string; + wildcard: boolean; + tables?: types.TableInfo[]; + table?: types.TableInfo; +} diff --git a/packages/service-core/src/api/SyncAPIProvider.ts b/packages/service-core/src/api/SyncAPIProvider.ts new file mode 100644 index 000000000..ac1cb276e --- /dev/null +++ b/packages/service-core/src/api/SyncAPIProvider.ts @@ -0,0 +1,26 @@ +import { SyncAPI } from './SyncAPI.js'; +import { logger } from '@powersync/lib-services-framework'; + +/** + * Serves as a registry from which SyncAPIs can be retrieved based on Replication DataSource type + * Initially only one SyncAPI per DataSource type is supported + */ +export class SyncAPIProvider { + private api: SyncAPI | undefined; + + public register(api: SyncAPI) { + if (this.api) { + logger.warn('A SyncAPI has already been registered. Overriding existing implementation'); + } + + this.api = api; + } + + public getSyncAPI(): SyncAPI { + if (!this.api) { + throw new Error('No SyncAPI has been registered yet.'); + } + + return this.api; + } +} \ No newline at end of file diff --git a/packages/service-core/src/api/api-index.ts b/packages/service-core/src/api/api-index.ts index f6063e867..5b73c18b8 100644 --- a/packages/service-core/src/api/api-index.ts +++ b/packages/service-core/src/api/api-index.ts @@ -1,2 +1,4 @@ export * from './diagnostics.js'; export * from './schema.js'; +export * from './SyncAPI.js'; +export * from './SyncAPIProvider.js'; From 9531e6f13fc5009e335640cb3049eaf5eb571d0a Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:52:49 +0200 Subject: [PATCH 031/298] Added generic config definitions for the data sources --- .../service-core/src/util/config/compound-config-collector.ts | 1 + packages/service-core/src/util/config/types.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 3ae856604..45073b26a 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -99,6 +99,7 @@ export class CompoundConfigCollector { let config: ResolvedPowerSyncConfig = { connection: mapped[0], + data_sources: baseConfig.replication?.data_sources || [], storage: baseConfig.storage, client_keystore: keyStore, // Dev tokens only use the static keys, no external key sources diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index 94a09ba6c..52f34c7ef 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -30,6 +30,7 @@ export type SyncRulesConfig = { export type ResolvedPowerSyncConfig = { connection?: ResolvedConnection; + data_sources?: configFile.DataSourceConfig[]; storage: configFile.StorageConfig; dev: { demo_auth: boolean; From 9cb97fd94f6372a695494930beec3a667ee969dc Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:55:24 +0200 Subject: [PATCH 032/298] Added the initial definitions for the modular replication support --- .../replication/core/ReplicationAdapter.ts | 78 ++++++++++++++++++ .../src/replication/core/ReplicationEngine.ts | 43 ++++++++++ .../src/replication/core/ReplicationEntity.ts | 74 +++++++++++++++++ .../src/replication/core/ReplicationModule.ts | 81 +++++++++++++++++++ .../src/replication/core/Replicator.ts | 25 ++++++ 5 files changed, 301 insertions(+) create mode 100644 packages/service-core/src/replication/core/ReplicationAdapter.ts create mode 100644 packages/service-core/src/replication/core/ReplicationEngine.ts create mode 100644 packages/service-core/src/replication/core/ReplicationEntity.ts create mode 100644 packages/service-core/src/replication/core/ReplicationModule.ts create mode 100644 packages/service-core/src/replication/core/Replicator.ts diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts new file mode 100644 index 000000000..6485926bd --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -0,0 +1,78 @@ +import { TablePattern } from '@powersync/service-sync-rules'; +import { ReplicationEntity } from './ReplicationEntity.js'; +import { SaveOptions } from '../../storage/BucketStorage.js'; + +/** + * Manage the creation and termination of connections for a datasource. + * For some data sources there will be no difference between snapshot and replication connections + */ +interface ConnectionManager { + /** + * Create a connection to use for replication. + */ + createReplicationConnection(): Promise; + + /** + * Create a connection to use for the initial snapshot replication. + * This connection should not be shared + */ + createConnection(): Promise; + + /** + * Map a datasource specific connection error to a general category of powersync errors + * @param error + */ + mapError(error: Error): ConnectionError; +} + +/** + * The ReplicationAdapter describes all the methods that are required by the + * Replicator to replicate data from a datasource into the PowerSync Bucket storage + */ +export interface ReplicationAdapter { + /** + * Unique name to identify this adapter in the PowerSync system + * Suggestion: datasource type + datasource name + */ + name(): string; + + /** + * Return a manager that can create connections to the DataSource + */ + createConnectionManager(): ConnectionManager; + + /** + * Validate any configuration on the datasource that needs to be in place for + * replication. If any configuration is missing or incorrect, an error should be thrown with the details. + */ + validateConfiguration(connection: TConnection): void; + + /** + * Get all the fully qualified entities that match the provided pattern + * @param connection + * @param pattern // TODO: Need something more generic than TablePattern + */ + toReplicationEntities(connection: TConnection, pattern: TablePattern): Promise[]>; + + /** + * Start replicating data, assumes that initializeData has already finished running + * Stream any changes back on the provided changeListener + * @param connection + * @param changeListener + */ + startReplication(connection: TConnection, changeListener: (change: SaveOptions) => {}): Promise; + + /** + * Immediately interrupt and stop any currently running replication. + */ + terminateReplication(): Promise; +} + +export enum ConnectionError { + INCORRECT_CREDENTIALS, + HOST_INACCESSIBLE, + CONNECTION_REFUSED, + CONNECTION_CLOSED, + RETRIEVAL_FAILED, + GENERAL +} diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts new file mode 100644 index 000000000..56c392120 --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -0,0 +1,43 @@ +import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +import { Replicator } from './Replicator.js'; + +export class ReplicationEngine { + private readonly storage: BucketStorageFactory; + private readonly replicators: Map, Replicator> = new Map(); + + constructor(storage: BucketStorageFactory) { + this.storage = storage; + } + + /** + * Create a new Replicator from the provided ReplicationAdapter. Once started the Replicator will begin + * replicating data from the DataSource to PowerSync and keep it up to date. + * + * @param adapter + */ + public register(adapter: ReplicationAdapter) { + if (this.replicators.has(adapter)) { + throw new Error(`Replicator for type ${adapter.name} already registered`); + } + this.replicators.set(adapter, new Replicator(this.storage, adapter)); + } + + /** + * Start replication on all managed Replicators + */ + public async start(): Promise { + for (const replicator of this.replicators.values()) { + await replicator.start(); + } + } + + /** + * Stop replication on all managed Replicators + */ + public async stop(): Promise { + for (const replicator of this.replicators.values()) { + await replicator.stop(); + } + } +} diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts new file mode 100644 index 000000000..01d8f7b8f --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationEntity.ts @@ -0,0 +1,74 @@ +import { SourceTable } from '../../storage/SourceTable.js'; +import { SqliteRow } from '@powersync/service-sync-rules'; + +/** + * Describes a replication entity, which is a logical representation of a table or collection in a data source. + * Extend this interface to add additional properties specific entities in the data source. + */ +export interface ReplicationEntityDescriptor { + name: string; + + /** + * Fully qualified name of the entity, including any schema/namespace prefixes + */ + fullyQualifiedName: string; + + /** + * Identifier to uniquely identify the entity in PowerSync + */ + id: string; + /** + * The entity's native identifier in the data source + */ + dataSourceEntityId: string; + /** + * The field(s) that uniquely identify an entry/row in this replication entity + */ + primaryIdentifierFields: string[]; +} + +export abstract class ReplicationEntity { + public descriptor: T; + + /** + * Defaults to true for tests. + */ + public syncData: boolean = true; + + /** + * Defaults to true for tests. + */ + public syncParameters: boolean = true; + + /** + * Indicates whether the snapshot of the entity has already been completed + */ + public snapshotComplete: boolean = false; + + constructor(descriptor: T) { + this.descriptor = descriptor; + } + + public hasPrimaryIdentifierFields(): boolean { + return this.descriptor.primaryIdentifierFields.length > 0; + } + + public syncAny() { + return this.syncData || this.syncParameters; + } + + /** + * Get the number of entries for this Entity + * @param connection + */ + public abstract count(connection: TConnection): Promise; + + /** + * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. + * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. + * This LSN will be used as the starting point for the replication stream. + * @param connection + * @param entryConsumer + */ + public abstract getSnapshot(connection: TConnection, entryConsumer: (batch: SqliteRow[]) => {}): Promise; +} diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts new file mode 100644 index 000000000..70b93f338 --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -0,0 +1,81 @@ +import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSyncConfig.js'; +import * as t from 'ts-codec'; +import { logger, schema } from '@powersync/lib-services-framework'; +import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { SyncAPI } from '../../api/SyncAPI.js'; +import { AbstractModule } from '../../modules/AbstractModule.js'; +import { ServiceContext } from '../../system/ServiceContext.js'; + +/** + * A replication module describes all the functionality that PowerSync requires to + * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. + */ +export abstract class ReplicationModule extends AbstractModule { + protected type: string; + + /** + * @param name + * @param type The type of the data source + * @protected + */ + protected constructor(name: string, type: string) { + super(name); + this.type = type; + } + + /** + * Create the API adapter for the DataSource required by the sync API + * endpoints. + */ + protected abstract createSyncAPIAdapter(): SyncAPI; + + /** + * Create the ReplicationAdapter to be used by PowerSync replicator. + */ + protected abstract createReplicationAdapter(): ReplicationAdapter; + + /** + * Return the TS codec schema describing the required configuration values for this module. + */ + protected abstract configSchema(): t.ObjectCodec; + + /** + * Register this module's replication adapters and sync API providers if the required configuration is present. + */ + public async initialize(context: ServiceContext): Promise { + if (!context.configuration.data_sources) { + // No data source configuration found in the config skip for now + // TODO: Consider a mechanism to check for config in the ENV variables as well + return; + } + + const matchingConfig = context.configuration.data_sources.filter((dataSource) => dataSource.type === this.type); + + if (matchingConfig.length > 1) { + logger.warning( + `Multiple data sources of type ${this.type} found in the configuration. Only the first will be used.` + ); + } + + try { + // If validation fails, log the error and continue, no replication will happen for this data source + this.validateConfig(matchingConfig[0]); + context.replicationEngine.register(this.createReplicationAdapter()); + context.syncAPIProvider.register(this.createSyncAPIAdapter()); + } catch (e) { + logger.error(e); + } + } + + private validateConfig(config: DataSourceConfig): void { + const validator = schema + .parseJSONSchema(t.generateJSONSchema(this.configSchema(), { allowAdditional: true })) + .validator(); + + const valid = validator.validate(config); + + if (!valid.valid) { + throw new Error(`Failed to validate Module ${this.name} configuration: ${valid.errors.join(', ')}`); + } + } +} diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts new file mode 100644 index 000000000..12b05f062 --- /dev/null +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -0,0 +1,25 @@ +import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { BucketStorageFactory } from '../../storage/BucketStorage.js'; + +/** + * A replicator manages the mechanics for replicating data from a data source to a storage bucket. + * This includes copying across the original data set and then keeping it in sync with the data source. + * TODO: Implement this. This will replace the current WallStreamManager + */ +export class Replicator { + private readonly adapter: ReplicationAdapter; + private storage: BucketStorageFactory; + + constructor(storage: BucketStorageFactory, adapter: ReplicationAdapter) { + this.adapter = adapter; + this.storage = storage; + } + + public async start(): Promise { + // start the replication + } + + public async stop(): Promise { + // stop the replication + } +} From a4e7e9f000a67a20cc75cd93346e1c3269e05731 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 12 Jul 2024 16:55:41 +0200 Subject: [PATCH 033/298] Lock file update --- pnpm-lock.yaml | 9127 +++++++++++++++++++----------------------------- 1 file changed, 3612 insertions(+), 5515 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3134582d4..2c250d75f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '9.0' +lockfileVersion: '6.0' settings: autoInstallPeers: true @@ -10,7 +10,7 @@ importers: devDependencies: '@changesets/cli': specifier: ^2.27.3 - version: 2.27.3 + version: 2.27.7 '@types/node': specifier: 18.11.11 version: 18.11.11 @@ -19,13 +19,13 @@ importers: version: 3.2.5 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 concurrently: specifier: ^8.2.2 version: 8.2.2 inquirer: specifier: ^9.2.7 - version: 9.2.22 + version: 9.3.5 npm-check-updates: specifier: ^16.10.15 version: 16.14.20 @@ -52,19 +52,19 @@ importers: version: 5.2.2 ws: specifier: ^8.2.3 - version: 8.2.3 + version: 8.18.0 libs/lib-services: dependencies: ajv: specifier: ^8.12.0 - version: 8.14.0 + version: 8.16.0 better-ajv-errors: specifier: ^1.2.0 - version: 1.2.0(ajv@8.14.0) + version: 1.2.0(ajv@8.16.0) bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -76,14 +76,14 @@ importers: version: 1.2.2 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 zod: specifier: ^3.23.8 version: 3.23.8 devDependencies: '@types/lodash': specifier: ^4.17.5 - version: 4.17.5 + version: 4.17.6 vitest: specifier: ^0.34.6 version: 0.34.6 @@ -93,6 +93,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:^ version: link:../jsonbig + '@powersync/service-sync-rules': + specifier: workspace:^ + version: link:../sync-rules '@powersync/service-types': specifier: workspace:^ version: link:../types @@ -101,7 +104,7 @@ importers: version: 3.6.0 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 packages/jsonbig: dependencies: @@ -125,7 +128,7 @@ importers: version: 9.0.1 ws: specifier: ^8.17.0 - version: 8.17.0 + version: 8.18.0 devDependencies: '@types/uuid': specifier: ^9.0.4 @@ -135,7 +138,7 @@ importers: version: 8.2.3 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 rsocket-websocket-client: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 @@ -162,7 +165,7 @@ importers: version: 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': specifier: ^1.24.1 - version: 1.24.1(@opentelemetry/api@1.8.0) + version: 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': specifier: 1.24.1 version: 1.24.1(@opentelemetry/api@1.8.0) @@ -192,7 +195,7 @@ importers: version: 0.5.0 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 commander: specifier: ^12.0.0 version: 12.1.0 @@ -207,22 +210,22 @@ importers: version: 5.0.0 jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 lodash: specifier: ^4.17.21 version: 4.17.21 lru-cache: specifier: ^10.2.2 - version: 10.2.2 + version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) + version: 6.8.0 node-fetch: specifier: ^3.3.2 version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -231,17 +234,17 @@ importers: version: 9.0.1 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 yaml: specifier: ^2.3.2 - version: 2.4.2 + version: 2.4.5 devDependencies: '@types/async': specifier: ^3.2.24 version: 3.2.24 '@types/lodash': specifier: ^4.17.5 - version: 4.17.5 + version: 4.17.6 '@types/uuid': specifier: ^9.0.4 version: 9.0.8 @@ -253,10 +256,10 @@ importers: version: 4.5.1 typescript: specifier: ^5.2.2 - version: 5.4.5 + version: 5.2.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@18.11.11)) + version: 4.3.2(typescript@5.2.2) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -271,13 +274,13 @@ importers: version: 0.5.2 ajv: specifier: ^8.12.0 - version: 8.14.0 + version: 8.16.0 pgsql-ast-parser: specifier: ^11.1.0 version: 11.2.0 yaml: specifier: ^2.3.1 - version: 2.4.2 + version: 2.4.5 devDependencies: '@types/node': specifier: 18.11.11 @@ -332,13 +335,13 @@ importers: version: link:../packages/types '@sentry/node': specifier: ^8.9.2 - version: 8.9.2 + version: 8.17.0 async-mutex: specifier: ^0.5.0 version: 0.5.0 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 commander: specifier: ^12.0.0 version: 12.1.0 @@ -356,19 +359,19 @@ importers: version: 5.0.0 jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 lru-cache: specifier: ^10.0.1 - version: 10.2.2 + version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) + version: 6.8.0 node-fetch: specifier: ^3.3.2 version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -377,14 +380,14 @@ importers: version: 9.0.1 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 yaml: specifier: ^2.3.2 - version: 2.4.2 + version: 2.4.5 devDependencies: '@sentry/types': specifier: ^8.9.2 - version: 8.9.2 + version: 8.17.0 '@types/uuid': specifier: ^9.0.4 version: 9.0.8 @@ -393,7 +396,7 @@ importers: version: 2.4.1 nodemon: specifier: ^3.0.1 - version: 3.1.1 + version: 3.1.4 npm-check-updates: specifier: ^16.14.4 version: 16.14.20 @@ -409,4137 +412,778 @@ importers: packages: - '@babel/code-frame@7.24.6': - resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==} + /@babel/code-frame@7.24.7: + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + dev: false - '@babel/helper-validator-identifier@7.24.6': - resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==} + /@babel/helper-validator-identifier@7.24.7: + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} + dev: false - '@babel/highlight@7.24.6': - resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==} + /@babel/highlight@7.24.7: + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + dev: false - '@babel/runtime@7.24.6': - resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==} + /@babel/runtime@7.24.8: + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: true - '@changesets/apply-release-plan@7.0.1': - resolution: {integrity: sha512-aPdSq/R++HOyfEeBGjEe6LNG8gs0KMSyRETD/J2092OkNq8mOioAxyKjMbvVUdzgr/HTawzMOz7lfw339KnsCA==} + /@changesets/apply-release-plan@7.0.4: + resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/config': 3.0.2 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.6.2 + dev: true - '@changesets/assemble-release-plan@6.0.0': - resolution: {integrity: sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==} + /@changesets/assemble-release-plan@6.0.3: + resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.6.2 + dev: true - '@changesets/changelog-git@0.2.0': + /@changesets/changelog-git@0.2.0: resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + dependencies: + '@changesets/types': 6.0.0 + dev: true - '@changesets/cli@2.27.3': - resolution: {integrity: sha512-ve/VpWApILlSs8cr0okNx5C2LKRawI9XZgvfmf58S8sar2nhx5DPJREFXYZBahs0FeTfvH0rdVl+nGe8QF45Ig==} + /@changesets/cli@2.27.7: + resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} hasBin: true + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/apply-release-plan': 7.0.4 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.2 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/get-release-plan': 4.0.3 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/should-skip-package': 0.1.0 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.1 + '@manypkg/get-packages': 1.1.3 + '@types/semver': 7.5.8 + ansi-colors: 4.1.3 + chalk: 2.4.2 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + mri: 1.2.0 + outdent: 0.5.0 + p-limit: 2.3.0 + preferred-pm: 3.1.4 + resolve-from: 5.0.0 + semver: 7.6.2 + spawndamnit: 2.0.0 + term-size: 2.2.1 + dev: true - '@changesets/config@3.0.0': - resolution: {integrity: sha512-o/rwLNnAo/+j9Yvw9mkBQOZySDYyOr/q+wptRLcAVGlU6djOeP9v1nlalbL9MFsobuBVQbZCTp+dIzdq+CLQUA==} + /@changesets/config@3.0.2: + resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.1 + '@changesets/logger': 0.1.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.7 + dev: true - '@changesets/errors@0.2.0': + /@changesets/errors@0.2.0: resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + dependencies: + extendable-error: 0.1.7 + dev: true - '@changesets/get-dependents-graph@2.0.0': - resolution: {integrity: sha512-cafUXponivK4vBgZ3yLu944mTvam06XEn2IZGjjKc0antpenkYANXiiE6GExV/yKdsCnE8dXVZ25yGqLYZmScA==} + /@changesets/get-dependents-graph@2.1.1: + resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + chalk: 2.4.2 + fs-extra: 7.0.1 + semver: 7.6.2 + dev: true - '@changesets/get-release-plan@4.0.0': - resolution: {integrity: sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w==} + /@changesets/get-release-plan@4.0.3: + resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/assemble-release-plan': 6.0.3 + '@changesets/config': 3.0.2 + '@changesets/pre': 2.0.0 + '@changesets/read': 0.6.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + dev: true - '@changesets/get-version-range-type@0.4.0': + /@changesets/get-version-range-type@0.4.0: resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + dev: true - '@changesets/git@3.0.0': + /@changesets/git@3.0.0: resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.7 + spawndamnit: 2.0.0 + dev: true - '@changesets/logger@0.1.0': + /@changesets/logger@0.1.0: resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + dependencies: + chalk: 2.4.2 + dev: true - '@changesets/parse@0.4.0': + /@changesets/parse@0.4.0: resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + dev: true - '@changesets/pre@2.0.0': + /@changesets/pre@2.0.0: resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + dev: true - '@changesets/read@0.6.0': + /@changesets/read@0.6.0: resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/git': 3.0.0 + '@changesets/logger': 0.1.0 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + chalk: 2.4.2 + fs-extra: 7.0.1 + p-filter: 2.1.0 + dev: true + + /@changesets/should-skip-package@0.1.0: + resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + dev: true - '@changesets/types@4.1.0': + /@changesets/types@4.1.0: resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + dev: true - '@changesets/types@6.0.0': + /@changesets/types@6.0.0: resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + dev: true - '@changesets/write@0.3.1': + /@changesets/write@0.3.1: resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + dev: true - '@colors/colors@1.5.0': + /@colors/colors@1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} + requiresBuild: true + dev: true + optional: true - '@colors/colors@1.6.0': + /@colors/colors@1.6.0: resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} + dev: false - '@cspotcode/source-map-support@0.8.1': + /@cspotcode/source-map-support@0.8.1: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true - '@dabh/diagnostics@2.0.3': + /@dabh/diagnostics@2.0.3: resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + dependencies: + colorspace: 1.1.4 + enabled: 2.0.0 + kuler: 2.0.0 + dev: false - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + /@esbuild/aix-ppc64@0.21.5: + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + /@esbuild/android-arm64@0.21.5: + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + /@esbuild/android-arm@0.21.5: + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + /@esbuild/android-x64@0.21.5: + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] + requiresBuild: true + dev: true + optional: true - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + /@esbuild/darwin-arm64@0.21.5: + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + /@esbuild/darwin-x64@0.21.5: + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] + requiresBuild: true + dev: true + optional: true - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + /@esbuild/freebsd-arm64@0.21.5: + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + /@esbuild/freebsd-x64@0.21.5: + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + /@esbuild/linux-arm64@0.21.5: + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + /@esbuild/linux-arm@0.21.5: + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + /@esbuild/linux-ia32@0.21.5: + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + /@esbuild/linux-loong64@0.21.5: + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + /@esbuild/linux-mips64el@0.21.5: + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + /@esbuild/linux-ppc64@0.21.5: + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + /@esbuild/linux-riscv64@0.21.5: + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + /@esbuild/linux-s390x@0.21.5: + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + /@esbuild/linux-x64@0.21.5: + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] + requiresBuild: true + dev: true + optional: true - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + /@esbuild/netbsd-x64@0.21.5: + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + /@esbuild/openbsd-x64@0.21.5: + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] + requiresBuild: true + dev: true + optional: true - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + /@esbuild/sunos-x64@0.21.5: + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + /@esbuild/win32-arm64@0.21.5: + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + /@esbuild/win32-ia32@0.21.5: + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] + requiresBuild: true + dev: true + optional: true - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + /@esbuild/win32-x64@0.21.5: + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] + requiresBuild: true + dev: true + optional: true - '@fastify/ajv-compiler@3.5.0': - resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} + /@fastify/ajv-compiler@3.6.0: + resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} + dependencies: + ajv: 8.16.0 + ajv-formats: 2.1.1(ajv@8.16.0) + fast-uri: 2.4.0 - '@fastify/cors@8.4.1': + /@fastify/cors@8.4.1: resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} + dependencies: + fastify-plugin: 4.5.1 + mnemonist: 0.39.5 + dev: false - '@fastify/error@3.4.1': + /@fastify/error@3.4.1: resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} - '@fastify/fast-json-stringify-compiler@4.3.0': + /@fastify/fast-json-stringify-compiler@4.3.0: resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + dependencies: + fast-json-stringify: 5.16.1 - '@fastify/merge-json-schemas@0.1.1': + /@fastify/merge-json-schemas@0.1.1: resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + dependencies: + fast-deep-equal: 3.1.3 - '@gar/promisify@1.1.3': + /@gar/promisify@1.1.3: resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + dev: true - '@humanwhocodes/momoa@2.0.4': + /@humanwhocodes/momoa@2.0.4: resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} engines: {node: '>=10.10.0'} + dev: false - '@inquirer/figures@1.0.2': - resolution: {integrity: sha512-4F1MBwVr3c/m4bAUef6LgkvBfSjzwH+OfldgHqcuacWwSUetFebM2wi58WfG9uk1rR98U6GwLed4asLJbwdV5w==} + /@inquirer/figures@1.0.3: + resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} engines: {node: '>=18'} + dev: true - '@isaacs/cliui@8.0.2': + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: true - '@jest/schemas@29.6.3': + /@jest/schemas@29.6.3: resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true - '@jridgewell/resolve-uri@3.1.2': + /@jridgewell/resolve-uri@3.1.2: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} + dev: true - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + /@jridgewell/sourcemap-codec@1.5.0: + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + dev: true - '@jridgewell/trace-mapping@0.3.9': + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true - '@js-sdsl/ordered-set@4.4.2': + /@js-sdsl/ordered-set@4.4.2: resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} + dev: false - '@ljharb/through@2.3.13': - resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} - engines: {node: '>= 0.4'} - - '@manypkg/find-root@1.1.0': + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + dependencies: + '@babel/runtime': 7.24.8 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + dev: true - '@manypkg/get-packages@1.1.3': + /@manypkg/get-packages@1.1.3: resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + dependencies: + '@babel/runtime': 7.24.8 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + dev: true - '@mongodb-js/saslprep@1.1.7': + /@mongodb-js/saslprep@1.1.7: resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + dependencies: + sparse-bitfield: 3.0.3 + dev: false - '@nodelib/fs.scandir@2.1.5': + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true - '@nodelib/fs.stat@2.0.5': + /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} + dev: true - '@nodelib/fs.walk@1.2.8': + /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + dev: true - '@npmcli/fs@2.1.2': + /@npmcli/fs@2.1.2: resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + '@gar/promisify': 1.1.3 + semver: 7.6.2 + dev: true - '@npmcli/fs@3.1.1': + /@npmcli/fs@3.1.1: resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + semver: 7.6.2 + dev: true - '@npmcli/git@4.1.0': + /@npmcli/git@4.1.0: resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + '@npmcli/promise-spawn': 6.0.2 + lru-cache: 7.18.3 + npm-pick-manifest: 8.0.2 + proc-log: 3.0.0 + promise-inflight: 1.0.1 + promise-retry: 2.0.1 + semver: 7.6.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + dev: true - '@npmcli/installed-package-contents@2.1.0': + /@npmcli/installed-package-contents@2.1.0: resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true + dependencies: + npm-bundled: 3.0.1 + npm-normalize-package-bin: 3.0.1 + dev: true - '@npmcli/move-file@2.0.1': + /@npmcli/move-file@2.0.1: resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This functionality has been moved to @npmcli/fs + dependencies: + mkdirp: 1.0.4 + rimraf: 3.0.2 + dev: true - '@npmcli/node-gyp@3.0.0': + /@npmcli/node-gyp@3.0.0: resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - '@npmcli/promise-spawn@6.0.2': + /@npmcli/promise-spawn@6.0.2: resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + which: 3.0.1 + dev: true - '@npmcli/run-script@6.0.2': + /@npmcli/run-script@6.0.2: resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dependencies: + '@npmcli/node-gyp': 3.0.0 + '@npmcli/promise-spawn': 6.0.2 + node-gyp: 9.4.1 + read-package-json-fast: 3.0.2 + which: 3.0.1 + transitivePeerDependencies: + - bluebird + - supports-color + dev: true - '@opentelemetry/api-logs@0.51.1': + /@opentelemetry/api-logs@0.51.1: resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} engines: {node: '>=14'} + dependencies: + '@opentelemetry/api': 1.8.0 + dev: false - '@opentelemetry/api-logs@0.52.0': - resolution: {integrity: sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q==} + /@opentelemetry/api-logs@0.52.1: + resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} engines: {node: '>=14'} + dependencies: + '@opentelemetry/api': 1.6.0 + dev: false - '@opentelemetry/api@1.6.0': + /@opentelemetry/api@1.6.0: resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} engines: {node: '>=8.0.0'} + dev: false - '@opentelemetry/api@1.8.0': + /@opentelemetry/api@1.8.0: resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} engines: {node: '>=8.0.0'} + dev: false - '@opentelemetry/api@1.9.0': + /@opentelemetry/api@1.9.0: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + dev: false - '@opentelemetry/context-async-hooks@1.25.0': - resolution: {integrity: sha512-sBW313mnMyFg0cp/40BRzrZBWG+581s2j5gIsa5fgGadswyILk4mNFATsqrCOpAx945RDuZ2B7ThQLgor9OpfA==} + /@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + dev: false - '@opentelemetry/core@1.17.0': + /@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0): resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.7.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/semantic-conventions': 1.17.0 + dev: false - '@opentelemetry/core@1.24.1': + /@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0): resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.9.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/core@1.25.0': - resolution: {integrity: sha512-n0B3s8rrqGrasTgNkXLKXzN0fXo+6IYP7M5b7AMsrZM33f/y6DS6kJ0Btd7SespASWq8bgL3taLo0oe0vB52IQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/exporter-metrics-otlp-http@0.51.1': - resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} + /@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.9.0' + dependencies: + '@opentelemetry/api': 1.8.0 + '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/exporter-prometheus@0.43.0': - resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} + /@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.9.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/exporter-prometheus@0.51.1': - resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} + /@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/instrumentation-connect@0.37.0': - resolution: {integrity: sha512-SeQktDIH5rNzjiEiazWiJAIXkmnLOnNV7wwHpahrqE0Ph+Z3heqMfxRtoMtbdJSIYLfcNZYO51AjxZ00IXufdw==} + /@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.8.0 + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/instrumentation-express@0.40.1': - resolution: {integrity: sha512-+RKMvVe2zw3kIXRup9c1jFu3T4d0fs5aKy015TpiMyoCKX1UMu3Z0lfgYtuyiSTANvg5hZnDbWmQmqSPj9VTvg==} + /@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} engines: {node: '>=14'} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/instrumentation-fastify@0.37.0': - resolution: {integrity: sha512-WRjwzNZgupSzbEYvo9s+QuHJRqZJjVdNxSEpGBwWK8RKLlHGwGVAu0gcc2gPamJWUJsGqPGvahAPWM18ZkWj6A==} + /@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-graphql@0.41.0': - resolution: {integrity: sha512-R/gXeljgIhaRDKquVkKYT5QHPnFouM8ooyePZEP0kqyaVAedtR1V7NfAUJbxfTG5fBQa5wdmLjvu63+tzRXZCA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-hapi@0.39.0': - resolution: {integrity: sha512-ik2nA9Yj2s2ay+aNY+tJsKCsEx6Tsc2g/MK0iWBW5tibwrWKTy1pdVt5sB3kd5Gkimqj23UV5+FH2JFcQLeKug==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-http@0.52.0': - resolution: {integrity: sha512-E6ywZuxTa4LnVXZGwL1oj3e2Eog1yIaNqa8KjKXoGkDNKte9/SjQnePXOmhQYI0A9nf0UyFbP9aKd+yHrkJXUA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-ioredis@0.41.0': - resolution: {integrity: sha512-rxiLloU8VyeJGm5j2fZS8ShVdB82n7VNP8wTwfUQqDwRfHCnkzGr+buKoxuhGD91gtwJ91RHkjHA1Eg6RqsUTg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-koa@0.41.0': - resolution: {integrity: sha512-mbPnDt7ELvpM2S0vixYUsde7122lgegLOJQxx8iJQbB8YHal/xnTh9v7IfArSVzIDo+E+080hxZyUZD4boOWkw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongodb@0.45.0': - resolution: {integrity: sha512-xnZP9+ayeB1JJyNE9cIiwhOJTzNEsRhXVdLgfzmrs48Chhhk026mQdM5CITfyXSCfN73FGAIB8d91+pflJEfWQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongoose@0.39.0': - resolution: {integrity: sha512-J1r66A7zJklPPhMtrFOO7/Ud2p0Pv5u8+r23Cd1JUH6fYPmftNJVsLp2urAt6PHK4jVqpP/YegN8wzjJ2mZNPQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql2@0.39.0': - resolution: {integrity: sha512-Iypuq2z6TCfriAXCIZjRq8GTFCKhQv5SpXbmI+e60rYdXw8NHtMH4NXcGF0eKTuoCsC59IYSTUvDQYDKReaszA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql@0.39.0': - resolution: {integrity: sha512-8snHPh83rhrDf31v9Kq0Nf+ts8hdr7NguuszRqZomZBHgE0+UyXZSkXHAAFZoBPPRMGyM68uaFE5hVtFl+wOcA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-nestjs-core@0.38.0': - resolution: {integrity: sha512-M381Df1dM8aqihZz2yK+ugvMFK5vlHG/835dc67Sx2hH4pQEQYDA2PpFPTgc9AYYOydQaj7ClFQunESimjXDgg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-pg@0.42.0': - resolution: {integrity: sha512-sjgcM8CswYy8zxHgXv4RAZ09DlYhQ+9TdlourUs63Df/ek5RrB1ZbjznqW7PB6c3TyJJmX6AVtPTjAsROovEjA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-redis-4@0.40.0': - resolution: {integrity: sha512-0ieQYJb6yl35kXA75LQUPhHtGjtQU9L85KlWa7d4ohBbk/iQKZ3X3CFl5jC5vNMq/GGPB3+w3IxNvALlHtrp7A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation@0.43.0': - resolution: {integrity: sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation@0.51.1': - resolution: {integrity: sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation@0.52.0': - resolution: {integrity: sha512-LPwSIrw+60cheWaXsfGL8stBap/AppKQJFE+qqRvzYrgttXFH2ofoIMxWadeqPTq4BYOXM/C7Bdh/T+B60xnlQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/otlp-exporter-base@0.51.1': - resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.0.0 - - '@opentelemetry/otlp-transformer@0.51.1': - resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' - - '@opentelemetry/redis-common@0.36.2': - resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} - engines: {node: '>=14'} - - '@opentelemetry/resources@1.17.0': - resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.7.0' - - '@opentelemetry/resources@1.24.1': - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' - - '@opentelemetry/resources@1.25.0': - resolution: {integrity: sha512-iHjydPMYJ+Li1auveJCq2rp5U2h6Mhq8BidiyE0jfVlDTFyR1ny8AfJHfmFzJ/RAM8vT8L7T21kcmGybxZC7lQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/sdk-logs@0.51.1': - resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.9.0' - '@opentelemetry/api-logs': '>=0.39.1' - - '@opentelemetry/sdk-metrics@1.17.0': - resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.7.0' - - '@opentelemetry/sdk-metrics@1.24.1': - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' - - '@opentelemetry/sdk-trace-base@1.24.1': - resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' - - '@opentelemetry/sdk-trace-base@1.25.0': - resolution: {integrity: sha512-6+g2fiRQUG39guCsKVeY8ToeuUf3YUnPkN6DXRA1qDmFLprlLvZm9cS6+chgbW70cZJ406FTtSCDnJwxDC5sGQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/semantic-conventions@1.17.0': - resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} - engines: {node: '>=14'} - - '@opentelemetry/semantic-conventions@1.24.1': - resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} - engines: {node: '>=14'} - - '@opentelemetry/semantic-conventions@1.25.0': - resolution: {integrity: sha512-M+kkXKRAIAiAP6qYyesfrC5TOmDpDVtsxuGfPcqd9B/iBrac+E14jYwrgm0yZBUIbIP2OnqC3j+UgkXLm1vxUQ==} - engines: {node: '>=14'} - - '@opentelemetry/sql-common@0.40.1': - resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.1.0 - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@pnpm/config.env-replace@1.1.0': - resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} - engines: {node: '>=12.22.0'} - - '@pnpm/network.ca-file@1.0.2': - resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} - engines: {node: '>=12.22.0'} - - '@pnpm/npm-conf@2.2.2': - resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} - engines: {node: '>=12'} - - '@prisma/instrumentation@5.15.0': - resolution: {integrity: sha512-fCWOOOajTKOUEp43gRmBqwt6oN9bPJcLiloi2OG/2ED0N5z62Cuza6FDrlm3SJHQAXYlXqLE0HLdEE5WcUkOzg==} - - '@rollup/rollup-android-arm-eabi@4.18.0': - resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.18.0': - resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.18.0': - resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.18.0': - resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.18.0': - resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.18.0': - resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.18.0': - resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.18.0': - resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.18.0': - resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-win32-arm64-msvc@4.18.0': - resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.18.0': - resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.18.0': - resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} - cpu: [x64] - os: [win32] - - '@sentry/core@8.9.2': - resolution: {integrity: sha512-ixm8NISFlPlEo3FjSaqmq4nnd13BRHoafwJ5MG+okCz6BKGZ1SexEggP42/QpGvDprUUHnfncG6WUMgcarr1zA==} - engines: {node: '>=14.18'} - - '@sentry/node@8.9.2': - resolution: {integrity: sha512-Q+JBpR4yx3eUyyhwgugucfRtPg65gYvzJGEmjzcnDJXJqX8ms4HPpNv9o2Om7A4014JxIibUdrQ+p5idcT7SZA==} - engines: {node: '>=14.18'} - - '@sentry/opentelemetry@8.9.2': - resolution: {integrity: sha512-Q6SHDQhrsBPcMi7ejqVdNTkt6SCTIhpGsFN8QR7daH3uvM0X2O7ciCuO9gRNRTEkflEINV4SBZEjANYH7BkRAg==} - engines: {node: '>=14.18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/core': ^1.25.0 - '@opentelemetry/instrumentation': ^0.52.0 - '@opentelemetry/sdk-trace-base': ^1.25.0 - '@opentelemetry/semantic-conventions': ^1.25.0 - - '@sentry/types@8.9.2': - resolution: {integrity: sha512-+LFOyQGl+zk5SZRGZD2MEURf7i5RHgP/mt3s85Rza+vz8M211WJ0YsjkIGUJFSY842nged5QLx4JysLaBlLymg==} - engines: {node: '>=14.18'} - - '@sentry/utils@8.9.2': - resolution: {integrity: sha512-A4srR9mEBFdVXwSEKjQ94msUbVkMr8JeFiEj9ouOFORw/Y/ux/WV2bWVD/ZI9wq0TcTNK8L1wBgU8UMS5lIq3A==} - engines: {node: '>=14.18'} - - '@sigstore/bundle@1.1.0': - resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sigstore/protobuf-specs@0.2.1': - resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sigstore/sign@1.0.0': - resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sigstore/tuf@1.0.3': - resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - - '@sindresorhus/is@5.6.0': - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} - - '@syncpoint/wkx@0.5.2': - resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} - - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} - - '@tootallnate/once@2.0.0': - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@tufjs/canonical-json@1.0.0': - resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@tufjs/models@1.0.4': - resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - '@types/accepts@1.3.7': - resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} - - '@types/async@3.2.24': - resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - - '@types/chai-subset@1.3.5': - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} - - '@types/chai@4.3.16': - resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} - - '@types/connect@3.4.36': - resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/content-disposition@0.5.8': - resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} - - '@types/cookies@0.9.0': - resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} - - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - - '@types/express-serve-static-core@4.19.1': - resolution: {integrity: sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==} - - '@types/express@4.17.21': - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} - - '@types/http-assert@1.5.5': - resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} - - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - - '@types/keygrip@1.0.6': - resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} - - '@types/koa-compose@3.2.8': - resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} - - '@types/koa@2.14.0': - resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==} - - '@types/koa__router@12.0.3': - resolution: {integrity: sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==} - - '@types/lodash@4.17.5': - resolution: {integrity: sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==} - - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - - '@types/minimist@1.2.5': - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - - '@types/mysql@2.15.22': - resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} - - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - - '@types/node@13.13.52': - resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} - - '@types/node@15.14.9': - resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - - '@types/node@18.11.11': - resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - - '@types/pg-pool@2.0.4': - resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} - - '@types/pg@8.6.1': - resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} - - '@types/qs@6.9.15': - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/semver-utils@1.1.3': - resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} - - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - - '@types/shimmer@1.0.5': - resolution: {integrity: sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==} - - '@types/strip-bom@3.0.0': - resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - - '@types/strip-json-comments@0.0.30': - resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - - '@types/triple-beam@1.3.5': - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - - '@types/uuid@9.0.8': - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - - '@types/webidl-conversions@7.0.3': - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - - '@types/whatwg-url@11.0.5': - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} - - '@types/ws@8.2.3': - resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} - - '@vitest/expect@0.34.6': - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} - - '@vitest/runner@0.34.6': - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} - - '@vitest/snapshot@0.34.6': - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} - - '@vitest/spy@0.34.6': - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} - - '@vitest/utils@0.34.6': - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} - - abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - - abstract-logging@2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - - acorn-import-assertions@1.9.0: - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} - peerDependencies: - acorn: ^8 - - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} - engines: {node: '>=0.4.0'} - - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} - - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv@8.14.0: - resolution: {integrity: sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==} - - ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - - are-we-there-yet@3.0.1: - resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - - arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - - async-mutex@0.5.0: - resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} - - async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - avvio@8.3.2: - resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - better-ajv-errors@1.2.0: - resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - ajv: 4.11.8 - 8 - - better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - boxen@7.1.1: - resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} - engines: {node: '>=14.16'} - - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - breakword@1.0.6: - resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} - - bson@6.7.0: - resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==} - engines: {node: '>=16.20.1'} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - - cacache@16.1.3: - resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - cacache@17.1.4: - resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} - - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - - camelcase-keys@6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - - chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} - - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.3.1: - resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} - - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - - cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} - - cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - - color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} - - colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} - hasBin: true - - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - - config-chain@1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - - configstore@6.0.0: - resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} - engines: {node: '>=12'} - - console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - - copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} - - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - - crypto-random-string@4.0.0: - resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} - engines: {node: '>=12'} - - csv-generate@3.4.3: - resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} - - csv-parse@4.16.3: - resolution: {integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==} - - csv-stringify@5.6.5: - resolution: {integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==} - - csv@5.5.3: - resolution: {integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==} - engines: {node: '>= 0.1.90'} - - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} - - date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} - - date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-eql@4.1.3: - resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} - engines: {node: '>=6'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - discontinuous-range@1.0.0: - resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} - - dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} - - dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - - duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - - dynamic-dedupe@0.3.0: - resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - - encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - - esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} - engines: {node: '>=12'} - hasBin: true - - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - - escape-goat@4.0.0: - resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} - engines: {node: '>=12'} - - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - event-stream@3.3.4: - resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - exponential-backoff@3.1.1: - resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} - - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - - fast-content-type-parse@1.1.0: - resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} - - fast-decode-uri-component@1.0.1: - resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - - fast-json-stringify@5.16.0: - resolution: {integrity: sha512-A4bg6E15QrkuVO3f0SwIASgzMzR6XC4qTyTqhf3hYXy0iazbAdZKwkE+ox4WgzKyzM6ygvbdq3r134UjOaaAnA==} - - fast-memoize@2.5.2: - resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} - - fast-querystring@1.1.2: - resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} - - fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} - - fast-uri@2.3.0: - resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} - - fastify-plugin@4.5.1: - resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} - - fastify@4.23.2: - resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} - - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - - fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-my-way@7.7.0: - resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} - engines: {node: '>=14'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - find-yarn-workspace-root2@1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} - - fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} - engines: {node: '>=14'} - - form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fp-and-or@0.1.4: - resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} - engines: {node: '>=10'} - - from@0.1.7: - resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} - - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - gauge@4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - - get-stdin@8.0.0: - resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} - engines: {node: '>=10'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@10.4.1: - resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} - engines: {node: '>=16 || 14 >=14.18'} - hasBin: true - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported - - global-dirs@3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - - got@12.6.1: - resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} - engines: {node: '>=14.16'} - - graceful-fs@4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - - hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} - - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - - has-yarn@3.0.0: - resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - hosted-git-info@5.2.1: - resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - hosted-git-info@6.1.1: - resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - - http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - - http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - - ignore-walk@6.0.5: - resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - - import-in-the-middle@1.4.2: - resolution: {integrity: sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==} - - import-in-the-middle@1.7.4: - resolution: {integrity: sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==} - - import-in-the-middle@1.8.0: - resolution: {integrity: sha512-/xQjze8szLNnJ5rvHSzn+dcVXqCAU6Plbk4P24U/jwPmg1wy7IIp9OjKIO5tYue8GSPhDpPDiApQjvBUmWwhsQ==} - - import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - infer-owner@1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - ini@2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - - ini@4.1.3: - resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - inquirer@9.2.22: - resolution: {integrity: sha512-SqLLa/Oe5rZUagTR9z+Zd6izyatHglbmbvVofo1KzuVB54YHleWzeHNLoR7FOICGOeQSqeLh1cordb3MzhGcEw==} - engines: {node: '>=18'} - - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} - engines: {node: '>= 10'} - - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} - - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-installed-globally@0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} - - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - - is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-npm@6.0.0: - resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - - is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - is-yarn-global@0.4.1: - resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} - engines: {node: '>=12'} - - isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - ix@5.0.0: - resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} - - jackspeak@3.1.2: - resolution: {integrity: sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==} - engines: {node: '>=14'} - - jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - - jose@4.15.5: - resolution: {integrity: sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - json-parse-helpfulerror@1.0.3: - resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} - - json-schema-ref-resolver@1.0.1: - resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonlines@0.1.1: - resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} - - jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - - jsonpointer@5.0.1: - resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} - engines: {node: '>=0.10.0'} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - - latest-version@7.0.0: - resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} - engines: {node: '>=14.16'} - - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - light-my-request@5.13.0: - resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - load-yaml-file@0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} - - local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - logform@2.6.0: - resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} - engines: {node: '>= 12.0.0'} - - lossless-json@2.0.11: - resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} - - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - - lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lru-cache@10.2.2: - resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} - engines: {node: 14 || >=16.14} - - lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} - - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - - magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - make-fetch-happen@10.2.1: - resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - make-fetch-happen@11.1.1: - resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - - map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - - map-stream@0.1.0: - resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} - - memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - - meow@6.1.1: - resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} - engines: {node: '>=8'} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} - - minipass-fetch@2.1.2: - resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - minipass-fetch@3.0.5: - resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} - - minipass-json-stream@1.0.1: - resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} - - minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - - minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mixme@0.5.10: - resolution: {integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==} - engines: {node: '>= 8.0.0'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - mlly@1.7.0: - resolution: {integrity: sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==} - - mnemonist@0.39.5: - resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} - - module-details-from-path@1.0.3: - resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} - - mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} - - mongodb@6.7.0: - resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true - - moo@0.5.2: - resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} - - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nearley@2.20.1: - resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} - hasBin: true - - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - node-cleanup@2.1.2: - resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} - - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - node-gyp@9.4.1: - resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} - engines: {node: ^12.13 || ^14.13 || >=16} - hasBin: true - - nodemon@3.1.1: - resolution: {integrity: sha512-k43xGaDtaDIcufn0Fc6fTtsdKSkV/hQzoQFigNH//GaKta28yoKVYXCnV+KXRqfT/YzsFaQU9VdeEG+HEyxr6A==} - engines: {node: '>=10'} - hasBin: true - - noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} - - nopt@6.0.0: - resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true - - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-package-data@5.0.0: - resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} - engines: {node: '>=14.16'} - - npm-bundled@3.0.1: - resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-check-updates@16.14.20: - resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} - engines: {node: '>=14.14'} - hasBin: true - - npm-install-checks@6.3.0: - resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-package-arg@10.1.0: - resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-packlist@7.0.4: - resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-pick-manifest@8.0.2: - resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npm-registry-fetch@14.0.5: - resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - - obliterator@2.0.4: - resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} - - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - opentelemetry-instrumentation-fetch-node@1.2.0: - resolution: {integrity: sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA==} - engines: {node: '>18.0.0'} - - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - - os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-json@8.1.1: - resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} - engines: {node: '>=14.16'} - - pacote@15.2.0: - resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - parse-github-url@1.0.2: - resolution: {integrity: sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==} - engines: {node: '>=0.10.0'} - hasBin: true - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - - pause-stream@0.0.11: - resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} - - pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - - pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} - - pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} - - pgsql-ast-parser@11.2.0: - resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} - - pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: - resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} - version: 0.7.0 - engines: {node: '>=14.18.0'} - - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - pino-abstract-transport@1.2.0: - resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} - - pino-std-serializers@6.2.2: - resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} - - pino@8.21.0: - resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} - hasBin: true - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - - pkg-types@1.1.1: - resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} - - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} - engines: {node: ^10 || ^12 || >=14} - - postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - - postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - - postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - - postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} - - preferred-pm@3.1.3: - resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} - engines: {node: '>=10'} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process-warning@2.3.2: - resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} - - process-warning@3.0.0: - resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - - promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - - prompts-ncu@3.0.0: - resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} - engines: {node: '>= 14'} - - proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - ps-tree@1.2.0: - resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} - engines: {node: '>= 0.10'} - hasBin: true - - pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - - pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - pupa@3.1.0: - resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} - engines: {node: '>=12.20'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - quick-lru@4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - railroad-diagrams@1.0.0: - resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} - - randexp@0.4.6: - resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} - engines: {node: '>=0.12'} - - rc-config-loader@4.1.3: - resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - read-package-json@6.0.4: - resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - deprecated: This package is no longer supported. Please use @npmcli/package-json instead. - - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - - readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - - registry-auth-token@5.0.2: - resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} - engines: {node: '>=14'} - - registry-url@6.0.1: - resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} - engines: {node: '>=12'} - - remote-git-tags@3.0.0: - resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} - engines: {node: '>=8'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - require-in-the-middle@7.3.0: - resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} - engines: {node: '>=8.6.0'} - - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - - responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - - ret@0.1.15: - resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} - engines: {node: '>=0.12'} - - ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} - - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rfdc@1.3.1: - resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} - - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - rimraf@5.0.7: - resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} - engines: {node: '>=14.18'} - hasBin: true - - rollup@4.18.0: - resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rsocket-core@1.0.0-alpha.3: - resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} - - rsocket-websocket-client@1.0.0-alpha.3: - resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} - - run-async@3.0.0: - resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} - engines: {node: '>=0.12.0'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - - safe-regex2@2.0.0: - resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} - - safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} - engines: {node: '>=10'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - - semver-diff@4.0.0: - resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} - engines: {node: '>=12'} - - semver-utils@1.1.4: - resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - - semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - - shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sigstore@1.9.0: - resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - - simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - smartwrap@2.0.2: - resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} - engines: {node: '>=6'} - hasBin: true - - socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} - - socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - - sonic-boom@3.8.1: - resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} - - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} - - spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - - spawn-please@2.0.2: - resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} - engines: {node: '>=14'} - - spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - split@0.3.3: - resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - - ssri@10.0.6: - resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - ssri@9.0.1: - resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - - stream-combiner@0.0.4: - resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} - - stream-transform@2.1.3: - resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} - - string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - - string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - strip-json-comments@5.0.1: - resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} - engines: {node: '>=14.16'} - - strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - - text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - - thread-stream@2.7.0: - resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} - - through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - - tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - - tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} - - tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toad-cache@3.7.0: - resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} - engines: {node: '>=12'} - - touch@3.1.1: - resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} - hasBin: true - - tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} - - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - - trim-newlines@3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - - triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - - ts-codec@1.2.2: - resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} - - ts-node-dev@2.0.0: - resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} - engines: {node: '>=0.8.0'} - hasBin: true - peerDependencies: - node-notifier: '*' - typescript: '*' - peerDependenciesMeta: - node-notifier: - optional: true - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - tsc-watch@6.2.0: - resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} - engines: {node: '>=12.12.0'} - hasBin: true - peerDependencies: - typescript: '*' - - tsconfck@3.0.3: - resolution: {integrity: sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - - tsconfig@7.0.0: - resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} - - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - - tty-table@4.2.3: - resolution: {integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA==} - engines: {node: '>=8.0.0'} - hasBin: true - - tuf-js@1.1.7: - resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} - - typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - - typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} - engines: {node: '>=14.17'} - hasBin: true - - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} - engines: {node: '>=14.17'} - hasBin: true - - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - - undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - - unique-filename@2.0.1: - resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - unique-slug@3.0.0: - resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - - unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - unique-string@3.0.0: - resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} - engines: {node: '>=12'} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - - update-notifier@6.0.2: - resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} - engines: {node: '>=14.16'} - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - vite-node@0.34.6: - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true - - vite-tsconfig-paths@4.3.2: - resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true - - vite@5.2.11: - resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} - - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which-pm@2.0.0: - resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} - engines: {node: '>=8.15'} - - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true - - why-is-node-running@2.2.2: - resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} - engines: {node: '>=8'} - hasBin: true - - wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - - widest-line@4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} - - winston-transport@4.7.0: - resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} - engines: {node: '>= 12.0.0'} - - winston@3.13.0: - resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} - engines: {node: '>= 12.0.0'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - - ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.2.3: - resolution: {integrity: sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xdg-basedir@5.1.0: - resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} - engines: {node: '>=12'} - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml@2.4.2: - resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==} - engines: {node: '>= 14'} - hasBin: true - - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} - engines: {node: '>=12.20'} - - zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - -snapshots: - - '@babel/code-frame@7.24.6': - dependencies: - '@babel/highlight': 7.24.6 - picocolors: 1.0.1 - - '@babel/helper-validator-identifier@7.24.6': {} - - '@babel/highlight@7.24.6': - dependencies: - '@babel/helper-validator-identifier': 7.24.6 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 - - '@babel/runtime@7.24.6': - dependencies: - regenerator-runtime: 0.14.1 - - '@changesets/apply-release-plan@7.0.1': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/config': 3.0.0 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.6.2 - - '@changesets/assemble-release-plan@6.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.6.2 - - '@changesets/changelog-git@0.2.0': - dependencies: - '@changesets/types': 6.0.0 - - '@changesets/cli@2.27.3': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/apply-release-plan': 7.0.1 - '@changesets/assemble-release-plan': 6.0.0 - '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.0 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/get-release-plan': 4.0.0 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 - '@changesets/types': 6.0.0 - '@changesets/write': 0.3.1 - '@manypkg/get-packages': 1.1.3 - '@types/semver': 7.5.8 - ansi-colors: 4.1.3 - chalk: 2.4.2 - ci-info: 3.9.0 - enquirer: 2.4.1 - external-editor: 3.1.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - meow: 6.1.1 - outdent: 0.5.0 - p-limit: 2.3.0 - preferred-pm: 3.1.3 - resolve-from: 5.0.0 - semver: 7.6.2 - spawndamnit: 2.0.0 - term-size: 2.2.1 - tty-table: 4.2.3 - - '@changesets/config@3.0.0': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/logger': 0.1.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.7 - - '@changesets/errors@0.2.0': - dependencies: - extendable-error: 0.1.7 - - '@changesets/get-dependents-graph@2.0.0': - dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - chalk: 2.4.2 - fs-extra: 7.0.1 - semver: 7.6.2 - - '@changesets/get-release-plan@4.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/assemble-release-plan': 6.0.0 - '@changesets/config': 3.0.0 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} - - '@changesets/git@3.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.7 - spawndamnit: 2.0.0 - - '@changesets/logger@0.1.0': - dependencies: - chalk: 2.4.2 - - '@changesets/parse@0.4.0': - dependencies: - '@changesets/types': 6.0.0 - js-yaml: 3.14.1 - - '@changesets/pre@2.0.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - - '@changesets/read@0.6.0': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 - '@changesets/parse': 0.4.0 - '@changesets/types': 6.0.0 - chalk: 2.4.2 - fs-extra: 7.0.1 - p-filter: 2.1.0 - - '@changesets/types@4.1.0': {} - - '@changesets/types@6.0.0': {} - - '@changesets/write@0.3.1': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - prettier: 2.8.8 - - '@colors/colors@1.5.0': - optional: true - - '@colors/colors@1.6.0': {} - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@dabh/diagnostics@2.0.3': - dependencies: - colorspace: 1.1.4 - enabled: 2.0.0 - kuler: 2.0.0 - - '@esbuild/aix-ppc64@0.20.2': - optional: true - - '@esbuild/android-arm64@0.20.2': - optional: true - - '@esbuild/android-arm@0.20.2': - optional: true - - '@esbuild/android-x64@0.20.2': - optional: true - - '@esbuild/darwin-arm64@0.20.2': - optional: true - - '@esbuild/darwin-x64@0.20.2': - optional: true - - '@esbuild/freebsd-arm64@0.20.2': - optional: true - - '@esbuild/freebsd-x64@0.20.2': - optional: true - - '@esbuild/linux-arm64@0.20.2': - optional: true - - '@esbuild/linux-arm@0.20.2': - optional: true - - '@esbuild/linux-ia32@0.20.2': - optional: true - - '@esbuild/linux-loong64@0.20.2': - optional: true - - '@esbuild/linux-mips64el@0.20.2': - optional: true - - '@esbuild/linux-ppc64@0.20.2': - optional: true - - '@esbuild/linux-riscv64@0.20.2': - optional: true - - '@esbuild/linux-s390x@0.20.2': - optional: true - - '@esbuild/linux-x64@0.20.2': - optional: true - - '@esbuild/netbsd-x64@0.20.2': - optional: true - - '@esbuild/openbsd-x64@0.20.2': - optional: true - - '@esbuild/sunos-x64@0.20.2': - optional: true - - '@esbuild/win32-arm64@0.20.2': - optional: true - - '@esbuild/win32-ia32@0.20.2': - optional: true - - '@esbuild/win32-x64@0.20.2': - optional: true - - '@fastify/ajv-compiler@3.5.0': - dependencies: - ajv: 8.14.0 - ajv-formats: 2.1.1(ajv@8.14.0) - fast-uri: 2.3.0 - - '@fastify/cors@8.4.1': - dependencies: - fastify-plugin: 4.5.1 - mnemonist: 0.39.5 - - '@fastify/error@3.4.1': {} - - '@fastify/fast-json-stringify-compiler@4.3.0': - dependencies: - fast-json-stringify: 5.16.0 - - '@fastify/merge-json-schemas@0.1.1': - dependencies: - fast-deep-equal: 3.1.3 - - '@gar/promisify@1.1.3': {} - - '@humanwhocodes/momoa@2.0.4': {} - - '@inquirer/figures@1.0.2': {} - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.4.15': {} - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - - '@js-sdsl/ordered-set@4.4.2': {} - - '@ljharb/through@2.3.13': - dependencies: - call-bind: 1.0.7 - - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.24.6 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.24.6 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - - '@mongodb-js/saslprep@1.1.7': - dependencies: - sparse-bitfield: 3.0.3 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - - '@npmcli/fs@2.1.2': - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.6.2 - - '@npmcli/fs@3.1.1': - dependencies: - semver: 7.6.2 - - '@npmcli/git@4.1.0': - dependencies: - '@npmcli/promise-spawn': 6.0.2 - lru-cache: 7.18.3 - npm-pick-manifest: 8.0.2 - proc-log: 3.0.0 - promise-inflight: 1.0.1 - promise-retry: 2.0.1 - semver: 7.6.2 - which: 3.0.1 - transitivePeerDependencies: - - bluebird - - '@npmcli/installed-package-contents@2.1.0': - dependencies: - npm-bundled: 3.0.1 - npm-normalize-package-bin: 3.0.1 - - '@npmcli/move-file@2.0.1': - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - - '@npmcli/node-gyp@3.0.0': {} - - '@npmcli/promise-spawn@6.0.2': - dependencies: - which: 3.0.1 - - '@npmcli/run-script@6.0.2': - dependencies: - '@npmcli/node-gyp': 3.0.0 - '@npmcli/promise-spawn': 6.0.2 - node-gyp: 9.4.1 - read-package-json-fast: 3.0.2 - which: 3.0.1 - transitivePeerDependencies: - - bluebird - - supports-color - - '@opentelemetry/api-logs@0.51.1': - dependencies: - '@opentelemetry/api': 1.8.0 - - '@opentelemetry/api-logs@0.52.0': - dependencies: - '@opentelemetry/api': 1.8.0 - - '@opentelemetry/api@1.6.0': {} - - '@opentelemetry/api@1.8.0': {} - - '@opentelemetry/api@1.9.0': {} - - '@opentelemetry/context-async-hooks@1.25.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/semantic-conventions': 1.17.0 - - '@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/semantic-conventions': 1.24.1 - - '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/semantic-conventions': 1.24.1 - - '@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.24.1 - - '@opentelemetry/core@1.25.0(@opentelemetry/api@1.8.0)': - dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/semantic-conventions': 1.25.0 - - '@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.25.0 - - '@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) @@ -4547,201 +1191,327 @@ snapshots: '@opentelemetry/otlp-transformer': 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0)': + /@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/sdk-metrics': 1.17.0(@opentelemetry/api@1.6.0) + dev: false - '@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/instrumentation-connect@0.37.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@types/connect': 3.4.36 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-express@0.40.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-fastify@0.37.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-graphql@0.41.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-hapi@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-http@0.52.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 semver: 7.6.2 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-ioredis@0.41.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-koa@0.41.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@types/koa': 2.14.0 - '@types/koa__router': 12.0.3 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mongodb@0.45.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mongoose@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mysql2@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-mysql@0.39.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@types/mysql': 2.15.22 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-nestjs-core@0.38.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-pg@0.42.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) '@types/pg': 8.6.1 '@types/pg-pool': 2.0.4 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation-redis-4@0.40.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.8.0)': + /@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} + engines: {node: '>=14'} + requiresBuild: true + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: - '@opentelemetry/api': 1.8.0 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.4.2 + '@opentelemetry/api': 1.9.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.7.1 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color + dev: false optional: true - '@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 + require-in-the-middle: 7.3.0 + semver: 7.6.2 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/api-logs': 0.51.1 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.7.4 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.52.0 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.8.0 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color + dev: false - '@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 @@ -4750,632 +1520,944 @@ snapshots: '@opentelemetry/sdk-logs': 0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/redis-common@0.36.2': {} + /@opentelemetry/redis-common@0.36.2: + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0)': + /@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.17.0 + dev: false - '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0)': + /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false + + /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + dependencies: + '@opentelemetry/api': 1.6.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/resources@1.25.0(@opentelemetry/api@1.8.0)': + /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/resources@1.25.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.9.0' + '@opentelemetry/api-logs': '>=0.39.1' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) + dev: false - '@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0)': + /@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.7.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0)': + /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.9.0) lodash.merge: 4.6.2 + dev: false - '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 + dev: false - '@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.8.0)': + /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0): + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/api': 1.6.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0)': + /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0): + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/api': 1.8.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/semantic-conventions': 1.25.1 + dev: false - '@opentelemetry/semantic-conventions@1.17.0': {} + /@opentelemetry/semantic-conventions@1.17.0: + resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/semantic-conventions@1.24.1': {} + /@opentelemetry/semantic-conventions@1.24.1: + resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/semantic-conventions@1.25.0': {} + /@opentelemetry/semantic-conventions@1.25.1: + resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} + engines: {node: '>=14'} + dev: false - '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': + /@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + dev: false - '@pkgjs/parseargs@0.11.0': + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true optional: true - '@pnpm/config.env-replace@1.1.0': {} + /@pnpm/config.env-replace@1.1.0: + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + dev: true - '@pnpm/network.ca-file@1.0.2': + /@pnpm/network.ca-file@1.0.2: + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} dependencies: graceful-fs: 4.2.10 + dev: true - '@pnpm/npm-conf@2.2.2': + /@pnpm/npm-conf@2.2.2: + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + dev: true - '@prisma/instrumentation@5.15.0': + /@prisma/instrumentation@5.16.1: + resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.8.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.8.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.8.0) transitivePeerDependencies: - supports-color + dev: false - '@rollup/rollup-android-arm-eabi@4.18.0': + /@rollup/rollup-android-arm-eabi@4.18.1: + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-android-arm64@4.18.0': + /@rollup/rollup-android-arm64@4.18.1: + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-darwin-arm64@4.18.0': + /@rollup/rollup-darwin-arm64@4.18.1: + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-darwin-x64@4.18.0': + /@rollup/rollup-darwin-x64@4.18.1: + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + /@rollup/rollup-linux-arm-gnueabihf@4.18.1: + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm-musleabihf@4.18.0': + /@rollup/rollup-linux-arm-musleabihf@4.18.1: + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm64-gnu@4.18.0': + /@rollup/rollup-linux-arm64-gnu@4.18.1: + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-arm64-musl@4.18.0': + /@rollup/rollup-linux-arm64-musl@4.18.1: + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-riscv64-gnu@4.18.0': + /@rollup/rollup-linux-riscv64-gnu@4.18.1: + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-s390x-gnu@4.18.0': + /@rollup/rollup-linux-s390x-gnu@4.18.1: + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-x64-gnu@4.18.0': + /@rollup/rollup-linux-x64-gnu@4.18.1: + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-linux-x64-musl@4.18.0': + /@rollup/rollup-linux-x64-musl@4.18.1: + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-win32-arm64-msvc@4.18.0': + /@rollup/rollup-win32-arm64-msvc@4.18.1: + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-win32-ia32-msvc@4.18.0': + /@rollup/rollup-win32-ia32-msvc@4.18.1: + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true optional: true - '@rollup/rollup-win32-x64-msvc@4.18.0': + /@rollup/rollup-win32-x64-msvc@4.18.1: + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true optional: true - '@sentry/core@8.9.2': + /@sentry/core@8.17.0: + resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} + engines: {node: '>=14.18'} dependencies: - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 + dev: false - '@sentry/node@8.9.2': + /@sentry/node@8.17.0: + resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} + engines: {node: '>=14.18'} dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.37.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.40.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fastify': 0.37.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.45.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-nestjs-core': 0.38.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.42.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis-4': 0.40.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@prisma/instrumentation': 5.15.0 - '@sentry/core': 8.9.2 - '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 + '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.39.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@prisma/instrumentation': 5.16.1 + '@sentry/core': 8.17.0 + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 optionalDependencies: - opentelemetry-instrumentation-fetch-node: 1.2.0 + opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color + dev: false - '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': + /@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1): + resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} + engines: {node: '>=14.18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.25.1 + '@opentelemetry/instrumentation': ^0.52.1 + '@opentelemetry/sdk-trace-base': ^1.25.1 + '@opentelemetry/semantic-conventions': ^1.25.1 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@sentry/core': 8.9.2 - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 - - '@sentry/types@8.9.2': {} + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@sentry/core': 8.17.0 + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 + dev: false + + /@sentry/types@8.17.0: + resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} + engines: {node: '>=14.18'} - '@sentry/utils@8.9.2': + /@sentry/utils@8.17.0: + resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} + engines: {node: '>=14.18'} dependencies: - '@sentry/types': 8.9.2 + '@sentry/types': 8.17.0 + dev: false - '@sigstore/bundle@1.1.0': + /@sigstore/bundle@1.1.0: + resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@sigstore/protobuf-specs': 0.2.1 + dev: true - '@sigstore/protobuf-specs@0.2.1': {} + /@sigstore/protobuf-specs@0.2.1: + resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - '@sigstore/sign@1.0.0': + /@sigstore/sign@1.0.0: + resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color + dev: true - '@sigstore/tuf@1.0.3': + /@sigstore/tuf@1.0.3: + resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@sigstore/protobuf-specs': 0.2.1 tuf-js: 1.1.7 transitivePeerDependencies: - supports-color + dev: true - '@sinclair/typebox@0.27.8': {} + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true - '@sindresorhus/is@5.6.0': {} + /@sindresorhus/is@5.6.0: + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + dev: true - '@syncpoint/wkx@0.5.2': + /@syncpoint/wkx@0.5.2: + resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} dependencies: '@types/node': 15.14.9 + dev: false - '@szmarczak/http-timer@5.0.1': + /@szmarczak/http-timer@5.0.1: + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} dependencies: defer-to-connect: 2.0.1 + dev: true - '@tootallnate/once@2.0.0': {} + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: true - '@tsconfig/node10@1.0.11': {} + /@tsconfig/node10@1.0.11: + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + dev: true - '@tsconfig/node12@1.0.11': {} + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true - '@tsconfig/node14@1.0.3': {} + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true - '@tsconfig/node16@1.0.4': {} + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true - '@tufjs/canonical-json@1.0.0': {} + /@tufjs/canonical-json@1.0.0: + resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - '@tufjs/models@1.0.4': + /@tufjs/models@1.0.4: + resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@tufjs/canonical-json': 1.0.0 - minimatch: 9.0.4 - - '@types/accepts@1.3.7': - dependencies: - '@types/node': 18.11.11 - - '@types/async@3.2.24': {} + minimatch: 9.0.5 + dev: true - '@types/body-parser@1.19.5': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 18.11.11 + /@types/async@3.2.24: + resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} + dev: true - '@types/chai-subset@1.3.5': + /@types/chai-subset@1.3.5: + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} dependencies: '@types/chai': 4.3.16 + dev: true - '@types/chai@4.3.16': {} - - '@types/connect@3.4.36': - dependencies: - '@types/node': 18.11.11 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 18.11.11 - - '@types/content-disposition@0.5.8': {} - - '@types/cookies@0.9.0': - dependencies: - '@types/connect': 3.4.38 - '@types/express': 4.17.21 - '@types/keygrip': 1.0.6 - '@types/node': 18.11.11 - - '@types/estree@1.0.5': {} - - '@types/express-serve-static-core@4.19.1': - dependencies: - '@types/node': 18.11.11 - '@types/qs': 6.9.15 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - - '@types/express@4.17.21': - dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.1 - '@types/qs': 6.9.15 - '@types/serve-static': 1.15.7 - - '@types/http-assert@1.5.5': {} - - '@types/http-cache-semantics@4.0.4': {} - - '@types/http-errors@2.0.4': {} - - '@types/keygrip@1.0.6': {} - - '@types/koa-compose@3.2.8': - dependencies: - '@types/koa': 2.14.0 + /@types/chai@4.3.16: + resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + dev: true - '@types/koa@2.14.0': + /@types/connect@3.4.36: + resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} dependencies: - '@types/accepts': 1.3.7 - '@types/content-disposition': 0.5.8 - '@types/cookies': 0.9.0 - '@types/http-assert': 1.5.5 - '@types/http-errors': 2.0.4 - '@types/keygrip': 1.0.6 - '@types/koa-compose': 3.2.8 '@types/node': 18.11.11 + dev: false - '@types/koa__router@12.0.3': - dependencies: - '@types/koa': 2.14.0 - - '@types/lodash@4.17.5': {} + /@types/estree@1.0.5: + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + dev: true - '@types/mime@1.3.5': {} + /@types/http-cache-semantics@4.0.4: + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + dev: true - '@types/minimist@1.2.5': {} + /@types/lodash@4.17.6: + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + dev: true - '@types/mysql@2.15.22': + /@types/mysql@2.15.22: + resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} dependencies: '@types/node': 18.11.11 + dev: false - '@types/node@12.20.55': {} - - '@types/node@13.13.52': {} + /@types/node@12.20.55: + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + dev: true - '@types/node@15.14.9': {} + /@types/node@13.13.52: + resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} + dev: false - '@types/node@18.11.11': {} + /@types/node@15.14.9: + resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} + dev: false - '@types/normalize-package-data@2.4.4': {} + /@types/node@18.11.11: + resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} - '@types/pg-pool@2.0.4': + /@types/pg-pool@2.0.4: + resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} dependencies: '@types/pg': 8.6.1 + dev: false - '@types/pg@8.6.1': + /@types/pg@8.6.1: + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} dependencies: '@types/node': 18.11.11 pg-protocol: 1.6.1 pg-types: 2.2.0 + dev: false - '@types/qs@6.9.15': {} - - '@types/range-parser@1.2.7': {} - - '@types/semver-utils@1.1.3': {} - - '@types/semver@7.5.8': {} - - '@types/send@0.17.4': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 18.11.11 + /@types/semver-utils@1.1.3: + resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} + dev: true - '@types/serve-static@1.15.7': - dependencies: - '@types/http-errors': 2.0.4 - '@types/node': 18.11.11 - '@types/send': 0.17.4 + /@types/semver@7.5.8: + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + dev: true - '@types/shimmer@1.0.5': {} + /@types/shimmer@1.2.0: + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + dev: false - '@types/strip-bom@3.0.0': {} + /@types/strip-bom@3.0.0: + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + dev: true - '@types/strip-json-comments@0.0.30': {} + /@types/strip-json-comments@0.0.30: + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + dev: true - '@types/triple-beam@1.3.5': {} + /@types/triple-beam@1.3.5: + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + dev: false - '@types/uuid@9.0.8': {} + /@types/uuid@9.0.8: + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + dev: true - '@types/webidl-conversions@7.0.3': {} + /@types/webidl-conversions@7.0.3: + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + dev: false - '@types/whatwg-url@11.0.5': + /@types/whatwg-url@11.0.5: + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} dependencies: '@types/webidl-conversions': 7.0.3 + dev: false - '@types/ws@8.2.3': + /@types/ws@8.2.3: + resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} dependencies: '@types/node': 18.11.11 + dev: true - '@vitest/expect@0.34.6': + /@vitest/expect@0.34.6: + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} dependencies: '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 chai: 4.4.1 + dev: true - '@vitest/runner@0.34.6': + /@vitest/runner@0.34.6: + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.2 + dev: true - '@vitest/snapshot@0.34.6': + /@vitest/snapshot@0.34.6: + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 + dev: true - '@vitest/spy@0.34.6': + /@vitest/spy@0.34.6: + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} dependencies: tinyspy: 2.2.1 + dev: true - '@vitest/utils@0.34.6': + /@vitest/utils@0.34.6: + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} dependencies: diff-sequences: 29.6.3 loupe: 2.3.7 pretty-format: 29.7.0 + dev: true - abbrev@1.1.1: {} + /abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: true - abort-controller@3.0.0: + /abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} dependencies: event-target-shim: 5.0.1 - abstract-logging@2.0.1: {} + /abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} - acorn-import-assertions@1.9.0(acorn@8.11.3): + /acorn-import-assertions@1.9.0(acorn@8.12.1): + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + requiresBuild: true + peerDependencies: + acorn: ^8 dependencies: - acorn: 8.11.3 + acorn: 8.12.1 + dev: false optional: true - acorn-import-attributes@1.9.5(acorn@8.11.3): + /acorn-import-attributes@1.9.5(acorn@8.12.1): + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 dependencies: - acorn: 8.11.3 + acorn: 8.12.1 + dev: false - acorn-walk@8.3.2: {} + /acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + dependencies: + acorn: 8.12.1 + dev: true - acorn@8.11.3: {} + /acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true - agent-base@6.0.2: + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color + dev: true - agentkeepalive@4.5.0: + /agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} dependencies: humanize-ms: 1.2.1 + dev: true - aggregate-error@3.1.0: + /aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 + dev: true - ajv-formats@2.1.1(ajv@8.14.0): - optionalDependencies: - ajv: 8.14.0 + /ajv-formats@2.1.1(ajv@8.16.0): + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.16.0 - ajv-formats@3.0.1(ajv@8.14.0): - optionalDependencies: - ajv: 8.14.0 + /ajv-formats@3.0.1(ajv@8.16.0): + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + dependencies: + ajv: 8.16.0 - ajv@8.14.0: + /ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - ansi-align@3.0.1: + /ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} dependencies: string-width: 4.2.3 + dev: true - ansi-colors@4.1.3: {} + /ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + dev: true - ansi-escapes@4.3.2: + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} dependencies: type-fest: 0.21.3 + dev: true - ansi-regex@5.0.1: {} + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true - ansi-regex@6.0.1: {} + /ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + dev: true - ansi-styles@3.2.1: + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - ansi-styles@4.3.0: + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true - ansi-styles@6.2.1: {} + /ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + dev: true - anymatch@3.1.3: + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 + dev: true - aproba@2.0.0: {} + /aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + dev: true - are-we-there-yet@3.0.1: + /are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. dependencies: delegates: 1.0.0 readable-stream: 3.6.2 + dev: true - arg@4.1.3: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - - array-union@2.1.0: {} + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true - array.prototype.flat@1.3.2: + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 + sprintf-js: 1.0.3 + dev: true - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true - arrify@1.0.1: {} + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true - assertion-error@1.1.0: {} + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true - async-mutex@0.5.0: + /async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} dependencies: - tslib: 2.6.2 - - async@3.2.5: {} + tslib: 2.6.3 + dev: false - atomic-sleep@1.0.0: {} + /async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 + /atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} - avvio@8.3.2: + /avvio@8.3.2: + resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} dependencies: '@fastify/error': 3.4.1 fastq: 1.17.1 - balanced-match@1.0.2: {} + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true - base64-js@1.5.1: {} + /base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - better-ajv-errors@1.2.0(ajv@8.14.0): + /better-ajv-errors@1.2.0(ajv@8.16.0): + resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + ajv: 4.11.8 - 8 dependencies: - '@babel/code-frame': 7.24.6 + '@babel/code-frame': 7.24.7 '@humanwhocodes/momoa': 2.0.4 - ajv: 8.14.0 + ajv: 8.16.0 chalk: 4.1.2 jsonpointer: 5.0.1 leven: 3.1.0 + dev: false - better-path-resolve@1.0.0: + /better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} dependencies: is-windows: 1.0.2 + dev: true - binary-extensions@2.3.0: {} + /binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + dev: true - bl@4.1.0: + /bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + dev: true - boxen@7.1.1: + /boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 @@ -5385,41 +2467,57 @@ snapshots: type-fest: 2.19.0 widest-line: 4.0.1 wrap-ansi: 8.1.0 + dev: true - brace-expansion@1.1.11: + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 + dev: true - brace-expansion@2.0.1: + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 + dev: true - braces@3.0.3: + /braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} dependencies: fill-range: 7.1.1 + dev: true - breakword@1.0.6: - dependencies: - wcwidth: 1.0.1 - - bson@6.7.0: {} + /bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} - buffer-from@1.1.2: {} + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true - buffer@5.7.1: + /buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + dev: true - buffer@6.0.3: + /buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - cac@6.7.14: {} + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true - cacache@16.1.3: + /cacache@16.1.3: + resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: '@npmcli/fs': 2.1.2 '@npmcli/move-file': 2.0.1 @@ -5441,12 +2539,15 @@ snapshots: unique-filename: 2.0.1 transitivePeerDependencies: - bluebird + dev: true - cacache@17.1.4: + /cacache@17.1.4: + resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@npmcli/fs': 3.1.1 fs-minipass: 3.0.3 - glob: 10.4.1 + glob: 10.4.5 lru-cache: 7.18.3 minipass: 7.1.2 minipass-collect: 1.0.2 @@ -5456,10 +2557,16 @@ snapshots: ssri: 10.0.6 tar: 6.2.1 unique-filename: 3.0.0 + dev: true - cacheable-lookup@7.0.0: {} + /cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + dev: true - cacheable-request@10.2.14: + /cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} dependencies: '@types/http-cache-semantics': 4.0.4 get-stream: 6.0.1 @@ -5468,55 +2575,59 @@ snapshots: mimic-response: 4.0.0 normalize-url: 8.0.1 responselike: 3.0.0 + dev: true - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - - camelcase-keys@6.2.2: - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - - camelcase@5.3.1: {} - - camelcase@7.0.1: {} + /camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + dev: true - chai@4.4.1: + /chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} dependencies: assertion-error: 1.1.0 check-error: 1.0.3 - deep-eql: 4.1.3 + deep-eql: 4.1.4 get-func-name: 2.0.2 loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 + dev: true - chalk@2.4.2: + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - chalk@4.1.2: + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.3.0: {} + /chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + dev: true - chardet@0.7.0: {} + /chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + dev: true - check-error@1.0.3: + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} dependencies: get-func-name: 2.0.2 + dev: true - chokidar@3.6.0: + /chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -5527,89 +2638,145 @@ snapshots: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 + dev: true - chownr@2.0.0: {} + /chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + dev: true - ci-info@3.9.0: {} + /ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + dev: true - cjs-module-lexer@1.3.1: {} + /cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + dev: false - clean-stack@2.2.0: {} + /clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + dev: true - cli-boxes@3.0.0: {} + /cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + dev: true - cli-cursor@3.1.0: + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} dependencies: restore-cursor: 3.1.0 + dev: true - cli-spinners@2.9.2: {} + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: true - cli-table3@0.6.5: + /cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} dependencies: string-width: 4.2.3 optionalDependencies: '@colors/colors': 1.5.0 + dev: true - cli-width@4.1.0: {} - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 + /cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + dev: true - cliui@7.0.4: + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + dev: true - cliui@8.0.1: + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + dev: true - clone@1.0.4: {} + /clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + dev: true - color-convert@1.9.3: + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - color-convert@2.0.1: + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - color-name@1.1.3: {} + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: {} + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: + /color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 + dev: false - color-support@1.1.3: {} + /color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + dev: true - color@3.2.1: + /color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} dependencies: color-convert: 1.9.3 color-string: 1.9.1 + dev: false - colorspace@1.1.4: + /colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} dependencies: color: 3.2.1 text-hex: 1.0.0 + dev: false - commander@10.0.1: {} + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true - commander@12.1.0: {} + /commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + dev: false - commander@2.20.3: {} + /commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: false - concat-map@0.0.1: {} + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true - concurrently@8.2.2: + /concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true dependencies: chalk: 4.1.2 date-fns: 2.30.0 @@ -5620,27 +2787,41 @@ snapshots: supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 + dev: true - confbox@0.1.7: {} + /confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + dev: true - config-chain@1.1.13: + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} dependencies: ini: 1.3.8 proto-list: 1.2.4 + dev: true - configstore@6.0.0: + /configstore@6.0.0: + resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} + engines: {node: '>=12'} dependencies: dot-prop: 6.0.1 graceful-fs: 4.2.11 unique-string: 3.0.0 write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 + dev: true - console-control-strings@1.1.0: {} + /console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + dev: true - cookie@0.6.0: {} + /cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} - copyfiles@2.4.1: + /copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true dependencies: glob: 7.2.3 minimatch: 3.1.2 @@ -5649,274 +2830,252 @@ snapshots: through2: 2.0.5 untildify: 4.0.0 yargs: 16.2.0 + dev: true - core-util-is@1.0.3: {} + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true - cors@2.8.5: + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} dependencies: object-assign: 4.1.1 vary: 1.1.2 + dev: false - create-require@1.1.1: {} + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true - cross-spawn@5.1.0: + /cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 + dev: true - cross-spawn@7.0.3: + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + dev: true - crypto-random-string@4.0.0: + /crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} dependencies: type-fest: 1.4.0 + dev: true - csv-generate@3.4.3: {} - - csv-parse@4.16.3: {} - - csv-stringify@5.6.5: {} - - csv@5.5.3: - dependencies: - csv-generate: 3.4.3 - csv-parse: 4.16.3 - csv-stringify: 5.6.5 - stream-transform: 2.1.3 - - data-uri-to-buffer@4.0.1: {} - - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 + /data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + dev: false - date-fns@2.30.0: + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} dependencies: - '@babel/runtime': 7.24.6 + '@babel/runtime': 7.24.8 + dev: true - date-fns@3.6.0: {} + /date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dev: false - debug@4.3.4(supports-color@5.5.0): + /debug@4.3.5(supports-color@5.5.0): + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true dependencies: ms: 2.1.2 - optionalDependencies: supports-color: 5.5.0 - decamelize-keys@1.1.1: - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - - decamelize@1.2.0: {} - - decompress-response@6.0.0: + /decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 + dev: true - deep-eql@4.1.3: + /deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} dependencies: type-detect: 4.0.8 + dev: true - deep-extend@0.6.0: {} + /deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + dev: true - defaults@1.0.4: + /defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 + dev: true - defer-to-connect@2.0.1: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 + /defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + dev: true - delegates@1.0.0: {} + /delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + dev: true - detect-indent@6.1.0: {} + /detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true - diff-sequences@29.6.3: {} + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true - diff@4.0.2: {} + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true - dir-glob@3.0.1: + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} dependencies: path-type: 4.0.0 + dev: true - discontinuous-range@1.0.0: {} + /discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + dev: false - dot-prop@6.0.1: + /dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} dependencies: is-obj: 2.0.0 + dev: true - dotenv@16.4.5: {} + /dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + dev: false - duplexer@0.1.2: {} + /duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + dev: true - dynamic-dedupe@0.3.0: + /dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} dependencies: xtend: 4.0.2 + dev: true - eastasianwidth@0.2.0: {} + /eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + dev: true - emoji-regex@8.0.0: {} + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true - emoji-regex@9.2.2: {} + /emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + dev: true - enabled@2.0.0: {} + /enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + dev: false - encoding@0.1.13: + /encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + requiresBuild: true dependencies: iconv-lite: 0.6.3 + dev: true optional: true - enquirer@2.4.1: + /enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 + dev: true - env-paths@2.2.1: {} - - err-code@2.0.3: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - es-abstract@1.23.3: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-errors@1.3.0: {} - - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 + /env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + dev: true - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 + /err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + dev: true - esbuild@0.20.2: + /esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 - - escalade@3.1.2: {} - - escape-goat@4.0.0: {} - - escape-string-regexp@1.0.5: {} - - esprima@4.0.1: {} - - event-stream@3.3.4: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + dev: true + + /escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + dev: true + + /escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} dependencies: duplexer: 0.1.2 from: 0.1.7 @@ -5925,172 +3084,246 @@ snapshots: split: 0.3.3 stream-combiner: 0.0.4 through: 2.3.8 + dev: true - event-target-shim@5.0.1: {} + /event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} - events@3.3.0: {} + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} - exponential-backoff@3.1.1: {} + /exponential-backoff@3.1.1: + resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + dev: true - extendable-error@0.1.7: {} + /extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + dev: true - external-editor@3.1.0: + /external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 + dev: true - fast-content-type-parse@1.1.0: {} + /fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} - fast-decode-uri-component@1.0.1: {} + /fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} - fast-deep-equal@3.1.3: {} + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.2: + /fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.7 + dev: true - fast-json-stringify@5.16.0: + /fast-json-stringify@5.16.1: + resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} dependencies: '@fastify/merge-json-schemas': 0.1.1 - ajv: 8.14.0 - ajv-formats: 3.0.1(ajv@8.14.0) + ajv: 8.16.0 + ajv-formats: 3.0.1(ajv@8.16.0) fast-deep-equal: 3.1.3 - fast-uri: 2.3.0 + fast-uri: 2.4.0 json-schema-ref-resolver: 1.0.1 - rfdc: 1.3.1 + rfdc: 1.4.1 - fast-memoize@2.5.2: {} + /fast-memoize@2.5.2: + resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + dev: true - fast-querystring@1.1.2: + /fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} dependencies: fast-decode-uri-component: 1.0.1 - fast-redact@3.5.0: {} + /fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} - fast-uri@2.3.0: {} + /fast-uri@2.4.0: + resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} - fastify-plugin@4.5.1: {} + /fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} - fastify@4.23.2: + /fastify@4.23.2: + resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} dependencies: - '@fastify/ajv-compiler': 3.5.0 + '@fastify/ajv-compiler': 3.6.0 '@fastify/error': 3.4.1 '@fastify/fast-json-stringify-compiler': 4.3.0 abstract-logging: 2.0.1 avvio: 8.3.2 fast-content-type-parse: 1.1.0 - fast-json-stringify: 5.16.0 + fast-json-stringify: 5.16.1 find-my-way: 7.7.0 light-my-request: 5.13.0 pino: 8.21.0 process-warning: 2.3.2 proxy-addr: 2.0.7 - rfdc: 1.3.1 + rfdc: 1.4.1 secure-json-parse: 2.7.0 semver: 7.6.2 toad-cache: 3.7.0 - fastq@1.17.1: + /fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} dependencies: reusify: 1.0.4 - fecha@4.2.3: {} + /fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + dev: false - fetch-blob@3.2.0: + /fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + dev: false - fill-range@7.1.1: + /fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 + dev: true - find-my-way@7.7.0: + /find-my-way@7.7.0: + resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} + engines: {node: '>=14'} dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 safe-regex2: 2.0.0 - find-up@4.1.0: + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} dependencies: locate-path: 5.0.0 path-exists: 4.0.0 + dev: true - find-up@5.0.0: + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} dependencies: locate-path: 6.0.0 path-exists: 4.0.0 + dev: true - find-yarn-workspace-root2@1.2.16: + /find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} dependencies: micromatch: 4.0.7 pkg-dir: 4.2.0 + dev: true - fn.name@1.1.0: {} - - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 + /fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + dev: false - foreground-child@3.1.1: + /foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 + dev: true - form-data-encoder@2.1.4: {} + /form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + dev: true - formdata-polyfill@4.0.10: + /formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} dependencies: fetch-blob: 3.2.0 + dev: false - forwarded@0.2.0: {} + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} - fp-and-or@0.1.4: {} + /fp-and-or@0.1.4: + resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} + engines: {node: '>=10'} + dev: true - from@0.1.7: {} + /from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + dev: true - fs-extra@7.0.1: + /fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 + dev: true - fs-extra@8.1.0: + /fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 + dev: true - fs-minipass@2.1.0: + /fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 + dev: true - fs-minipass@3.0.3: + /fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: minipass: 7.1.2 + dev: true - fs.realpath@1.0.0: {} + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true - fsevents@2.3.3: + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true optional: true - function-bind@1.1.2: {} - - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - functions-have-names: 1.2.3 - - functions-have-names@1.2.3: {} + /function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gauge@4.0.4: + /gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. dependencies: aproba: 2.0.0 color-support: 1.1.3 @@ -6100,42 +3333,49 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wide-align: 1.1.5 + dev: true - get-caller-file@2.0.5: {} - - get-func-name@2.0.2: {} - - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true - get-stdin@8.0.0: {} + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true - get-stream@6.0.1: {} + /get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + dev: true - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true - glob-parent@5.1.2: + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 + dev: true - glob@10.4.1: + /glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true dependencies: - foreground-child: 3.1.1 - jackspeak: 3.1.2 - minimatch: 9.0.4 + foreground-child: 3.2.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 minipass: 7.1.2 + package-json-from-dist: 1.0.0 path-scurry: 1.11.1 + dev: true - glob@7.2.3: + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -6143,25 +3383,30 @@ snapshots: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 + dev: true - glob@8.1.0: + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 minimatch: 5.1.6 once: 1.4.0 + dev: true - global-dirs@3.0.1: + /global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} dependencies: ini: 2.0.0 + dev: true - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.0.1 - - globby@11.1.0: + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -6169,14 +3414,15 @@ snapshots: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 + dev: true - globrex@0.1.2: {} - - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 + /globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true - got@12.6.1: + /got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} dependencies: '@sindresorhus/is': 5.6.0 '@szmarczak/http-timer': 5.0.1 @@ -6189,151 +3435,203 @@ snapshots: lowercase-keys: 3.0.0 p-cancelable: 3.0.0 responselike: 3.0.0 + dev: true - graceful-fs@4.2.10: {} - - graceful-fs@4.2.11: {} - - grapheme-splitter@1.0.4: {} - - hard-rejection@2.1.0: {} - - has-bigints@1.0.2: {} - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 + /graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + dev: true - has-proto@1.0.3: {} + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true - has-symbols@1.0.3: {} + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} - has-unicode@2.0.1: {} + /has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + dev: true - has-yarn@3.0.0: {} + /has-yarn@3.0.0: + resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - hasown@2.0.2: + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} dependencies: function-bind: 1.1.2 - hosted-git-info@2.8.9: {} - - hosted-git-info@5.2.1: + /hosted-git-info@5.2.1: + resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: lru-cache: 7.18.3 + dev: true - hosted-git-info@6.1.1: + /hosted-git-info@6.1.1: + resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: lru-cache: 7.18.3 + dev: true - http-cache-semantics@4.1.1: {} + /http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + dev: true - http-proxy-agent@5.0.0: + /http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color + dev: true - http2-wrapper@2.2.1: + /http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 + dev: true - https-proxy-agent@5.0.1: + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color + dev: true - human-id@1.0.2: {} + /human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + dev: true - humanize-ms@1.2.1: + /humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} dependencies: ms: 2.1.3 + dev: true - iconv-lite@0.4.24: + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 + dev: true - iconv-lite@0.6.3: + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + requiresBuild: true dependencies: safer-buffer: 2.1.2 + dev: true optional: true - ieee754@1.2.1: {} + /ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore-by-default@1.0.1: {} + /ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + dev: true - ignore-walk@6.0.5: + /ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: - minimatch: 9.0.4 + minimatch: 9.0.5 + dev: true - ignore@5.3.1: {} + /ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + dev: true - import-in-the-middle@1.4.2: + /import-in-the-middle@1.7.1: + resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} + requiresBuild: true dependencies: - acorn: 8.11.3 - acorn-import-assertions: 1.9.0(acorn@8.11.3) + acorn: 8.12.1 + acorn-import-assertions: 1.9.0(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 + dev: false optional: true - import-in-the-middle@1.7.4: - dependencies: - acorn: 8.11.3 - acorn-import-attributes: 1.9.5(acorn@8.11.3) - cjs-module-lexer: 1.3.1 - module-details-from-path: 1.0.3 - - import-in-the-middle@1.8.0: + /import-in-the-middle@1.9.0: + resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} dependencies: - acorn: 8.11.3 - acorn-import-attributes: 1.9.5(acorn@8.11.3) + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 + dev: false - import-lazy@4.0.0: {} + /import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + dev: true - imurmurhash@0.1.4: {} + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true - indent-string@4.0.0: {} + /indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + dev: true - infer-owner@1.0.4: {} + /infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + dev: true - inflight@1.0.6: + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. dependencies: once: 1.4.0 wrappy: 1.0.2 + dev: true - inherits@2.0.4: {} + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ini@1.3.8: {} + /ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true - ini@2.0.0: {} + /ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + dev: true - ini@4.1.3: {} + /ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - inquirer@9.2.22: + /inquirer@9.3.5: + resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} + engines: {node: '>=18'} dependencies: - '@inquirer/figures': 1.0.2 - '@ljharb/through': 2.3.13 + '@inquirer/figures': 1.0.3 ansi-escapes: 4.3.2 - chalk: 5.3.0 - cli-cursor: 3.1.0 cli-width: 4.1.0 external-editor: 3.1.0 - lodash: 4.17.21 mute-stream: 1.0.0 ora: 5.4.1 run-async: 3.0.0 @@ -6341,251 +3639,327 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + dev: true - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.0.6 - - ip-address@9.0.5: + /ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 + dev: true - ipaddr.js@1.9.1: {} - - ipaddr.js@2.2.0: {} - - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-arrayish@0.2.1: {} + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} - is-arrayish@0.3.2: {} + /ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + dev: false - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 + /is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + dev: false - is-binary-path@2.1.0: + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} dependencies: binary-extensions: 2.3.0 + dev: true - is-boolean-object@1.1.2: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-ci@3.0.1: + /is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true dependencies: ci-info: 3.9.0 + dev: true - is-core-module@2.13.1: + /is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} dependencies: hasown: 2.0.2 - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.2 - - is-extglob@2.1.1: {} + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true - is-fullwidth-code-point@3.0.0: {} + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true - is-glob@4.0.3: + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 + dev: true - is-installed-globally@0.4.0: + /is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} dependencies: global-dirs: 3.0.1 is-path-inside: 3.0.3 + dev: true - is-interactive@1.0.0: {} - - is-lambda@1.0.1: {} - - is-negative-zero@2.0.3: {} - - is-npm@6.0.0: {} - - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - - is-obj@2.0.0: {} + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true - is-path-inside@3.0.3: {} + /is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + dev: true - is-plain-obj@1.1.0: {} + /is-npm@6.0.0: + resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - is-regex@1.1.4: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 + /is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + dev: true - is-stream@2.0.1: {} + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.2 + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: false - is-subdir@1.2.0: + /is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} dependencies: better-path-resolve: 1.0.0 + dev: true - is-symbol@1.0.4: - dependencies: - has-symbols: 1.0.3 - - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - - is-typedarray@1.0.0: {} - - is-unicode-supported@0.1.0: {} - - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.7 + /is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + dev: true - is-windows@1.0.2: {} + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true - is-yarn-global@0.4.1: {} + /is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + dev: true - isarray@0.0.1: {} + /is-yarn-global@0.4.1: + resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} + engines: {node: '>=12'} + dev: true - isarray@1.0.0: {} + /isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + dev: true - isarray@2.0.5: {} + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true - isexe@2.0.0: {} + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true - ix@5.0.0: + /ix@5.0.0: + resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} dependencies: '@types/node': 13.13.52 - tslib: 2.6.2 + tslib: 2.6.3 + dev: false - jackspeak@3.1.2: + /jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 + dev: true - jju@1.4.0: {} + /jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + dev: true - jose@4.15.5: {} + /jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + dev: false - js-tokens@4.0.0: {} + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: false - js-yaml@3.14.1: + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true dependencies: argparse: 1.0.10 esprima: 4.0.1 + dev: true - js-yaml@4.1.0: + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true dependencies: argparse: 2.0.1 + dev: true - jsbn@1.1.0: {} - - json-buffer@3.0.1: {} + /jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + dev: true - json-parse-even-better-errors@2.3.1: {} + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true - json-parse-even-better-errors@3.0.2: {} + /json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - json-parse-helpfulerror@1.0.3: + /json-parse-helpfulerror@1.0.3: + resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} dependencies: jju: 1.4.0 + dev: true - json-schema-ref-resolver@1.0.1: + /json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} dependencies: fast-deep-equal: 3.1.3 - json-schema-traverse@1.0.0: {} + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json5@2.2.3: {} + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true - jsonfile@4.0.0: + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} optionalDependencies: graceful-fs: 4.2.11 + dev: true - jsonlines@0.1.1: {} + /jsonlines@0.1.1: + resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} + dev: true - jsonparse@1.3.1: {} + /jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + dev: true - jsonpointer@5.0.1: {} + /jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + dev: false - keyv@4.5.4: + /keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: json-buffer: 3.0.1 + dev: true - kind-of@6.0.3: {} - - kleur@4.1.5: {} + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true - kuler@2.0.0: {} + /kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + dev: false - latest-version@7.0.0: + /latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} dependencies: package-json: 8.1.1 + dev: true - leven@3.1.0: {} + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: false - light-my-request@5.13.0: + /light-my-request@5.13.0: + resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} dependencies: cookie: 0.6.0 process-warning: 3.0.0 set-cookie-parser: 2.6.0 - lines-and-columns@1.2.4: {} - - load-yaml-file@0.2.0: + /load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 + dev: true - local-pkg@0.4.3: {} + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true - locate-path@5.0.0: + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} dependencies: p-locate: 4.1.0 + dev: true - locate-path@6.0.0: + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} dependencies: p-locate: 5.0.0 + dev: true - lodash.merge@4.6.2: {} + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: false - lodash.startcase@4.4.0: {} + /lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + dev: true - lodash@4.17.21: {} + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - log-symbols@4.1.0: + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 + dev: true - logform@2.6.0: + /logform@2.6.1: + resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} + engines: {node: '>= 12.0.0'} dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -6593,31 +3967,51 @@ snapshots: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 + dev: false - lossless-json@2.0.11: {} + /lossless-json@2.0.11: + resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} + dev: false - loupe@2.3.7: + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} dependencies: get-func-name: 2.0.2 + dev: true - lowercase-keys@3.0.0: {} + /lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - lru-cache@10.2.2: {} + /lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@4.1.5: + /lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: pseudomap: 1.0.2 yallist: 2.1.2 + dev: true - lru-cache@7.18.3: {} + /lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + dev: true - magic-string@0.30.10: + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 + dev: true - make-error@1.3.6: {} + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true - make-fetch-happen@10.2.1: + /make-fetch-happen@10.2.1: + resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: agentkeepalive: 4.5.0 cacache: 16.1.3 @@ -6638,8 +4032,11 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - make-fetch-happen@11.1.1: + /make-fetch-happen@11.1.1: + resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: agentkeepalive: 4.5.0 cacache: 17.1.4 @@ -6658,174 +4055,277 @@ snapshots: ssri: 10.0.6 transitivePeerDependencies: - supports-color + dev: true - map-obj@1.0.1: {} - - map-obj@4.3.0: {} - - map-stream@0.1.0: {} - - memory-pager@1.5.0: {} + /map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + dev: true - meow@6.1.1: - dependencies: - '@types/minimist': 1.2.5 - camelcase-keys: 6.2.2 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 2.5.0 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.13.1 - yargs-parser: 18.1.3 + /memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + dev: false - merge2@1.4.1: {} + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true - micromatch@4.0.7: + /micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} dependencies: braces: 3.0.3 picomatch: 2.3.1 + dev: true - mimic-fn@2.1.0: {} - - mimic-response@3.1.0: {} + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true - mimic-response@4.0.0: {} + /mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + dev: true - min-indent@1.0.1: {} + /mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true - minimatch@3.1.2: + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: brace-expansion: 1.1.11 + dev: true - minimatch@5.1.6: + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 + dev: true - minimatch@9.0.4: + /minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 + dev: true - minimist-options@4.1.0: - dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 - - minimist@1.2.8: {} + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true - minipass-collect@1.0.2: + /minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 + dev: true - minipass-fetch@2.1.2: + /minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: minipass: 3.3.6 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 + dev: true - minipass-fetch@3.0.5: + /minipass-fetch@3.0.5: + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 + dev: true - minipass-flush@1.0.5: + /minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 + dev: true - minipass-json-stream@1.0.1: + /minipass-json-stream@1.0.1: + resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} dependencies: jsonparse: 1.3.1 minipass: 3.3.6 + dev: true - minipass-pipeline@1.2.4: + /minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} dependencies: minipass: 3.3.6 + dev: true - minipass-sized@1.0.3: + /minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} dependencies: minipass: 3.3.6 + dev: true - minipass@3.3.6: + /minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} dependencies: yallist: 4.0.0 + dev: true - minipass@5.0.0: {} + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true - minipass@7.1.2: {} + /minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true - minizlib@2.1.2: + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} dependencies: minipass: 3.3.6 yallist: 4.0.0 + dev: true - mixme@0.5.10: {} - - mkdirp@1.0.4: {} + /mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + dev: true - mlly@1.7.0: + /mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} dependencies: - acorn: 8.11.3 + acorn: 8.12.1 pathe: 1.1.2 - pkg-types: 1.1.1 + pkg-types: 1.1.3 ufo: 1.5.3 + dev: true - mnemonist@0.39.5: + /mnemonist@0.39.5: + resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} dependencies: obliterator: 2.0.4 + dev: false - module-details-from-path@1.0.3: {} + /module-details-from-path@1.0.3: + resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + dev: false - mongodb-connection-string-url@3.0.1: + /mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 + dev: false - mongodb@6.7.0(socks@2.8.3): + /mongodb@6.8.0: + resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true dependencies: '@mongodb-js/saslprep': 1.1.7 - bson: 6.7.0 + bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - optionalDependencies: - socks: 2.8.3 + dev: false + + /moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + dev: false - moo@0.5.2: {} + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true - ms@2.1.2: {} + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: {} + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@1.0.0: {} + /mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - nanoid@3.3.7: {} + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true - nearley@2.20.1: + /nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true dependencies: commander: 2.20.3 moo: 0.5.2 railroad-diagrams: 1.0.0 randexp: 0.4.6 + dev: false - negotiator@0.6.3: {} + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: true - node-cleanup@2.1.2: {} + /node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + dev: true - node-domexception@1.0.0: {} + /node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + dev: false - node-fetch@3.3.2: + /node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 + dev: false - node-gyp@9.4.1: + /node-gyp@9.4.1: + resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} + engines: {node: ^12.13 || ^14.13 || >=16} + hasBin: true dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.1 @@ -6841,11 +4341,15 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - nodemon@3.1.1: + /nodemon@3.1.4: + resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} + engines: {node: '>=10'} + hasBin: true dependencies: chokidar: 3.6.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 pstree.remy: 1.1.8 @@ -6854,39 +4358,54 @@ snapshots: supports-color: 5.5.0 touch: 3.1.1 undefsafe: 2.0.5 + dev: true - noms@0.0.0: + /noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} dependencies: inherits: 2.0.4 readable-stream: 1.0.34 + dev: true - nopt@6.0.0: + /nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true dependencies: abbrev: 1.1.1 + dev: true - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.8 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 - - normalize-package-data@5.0.0: + /normalize-package-data@5.0.0: + resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: hosted-git-info: 6.1.1 - is-core-module: 2.13.1 + is-core-module: 2.14.0 semver: 7.6.2 validate-npm-package-license: 3.0.4 + dev: true - normalize-path@3.0.0: {} + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true - normalize-url@8.0.1: {} + /normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + dev: true - npm-bundled@3.0.1: + /npm-bundled@3.0.1: + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: npm-normalize-package-bin: 3.0.1 + dev: true - npm-check-updates@16.14.20: + /npm-check-updates@16.14.20: + resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} + engines: {node: '>=14.14'} + hasBin: true dependencies: '@types/semver-utils': 1.1.3 chalk: 5.3.0 @@ -6904,15 +4423,15 @@ snapshots: jsonlines: 0.1.1 lodash: 4.17.21 make-fetch-happen: 11.1.1 - minimatch: 9.0.4 + minimatch: 9.0.5 p-map: 4.0.0 pacote: 15.2.0 - parse-github-url: 1.0.2 + parse-github-url: 1.0.3 progress: 2.0.3 prompts-ncu: 3.0.0 rc-config-loader: 4.1.3 remote-git-tags: 3.0.0 - rimraf: 5.0.7 + rimraf: 5.0.9 semver: 7.6.2 semver-utils: 1.1.4 source-map-support: 0.5.21 @@ -6924,32 +4443,50 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - npm-install-checks@6.3.0: + /npm-install-checks@6.3.0: + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: semver: 7.6.2 + dev: true - npm-normalize-package-bin@3.0.1: {} + /npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - npm-package-arg@10.1.0: + /npm-package-arg@10.1.0: + resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: hosted-git-info: 6.1.1 proc-log: 3.0.0 semver: 7.6.2 validate-npm-package-name: 5.0.1 + dev: true - npm-packlist@7.0.4: + /npm-packlist@7.0.4: + resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: ignore-walk: 6.0.5 + dev: true - npm-pick-manifest@8.0.2: + /npm-pick-manifest@8.0.2: + resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 10.1.0 semver: 7.6.2 + dev: true - npm-registry-fetch@14.0.5: + /npm-registry-fetch@14.0.5: + resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: make-fetch-happen: 11.1.1 minipass: 5.0.0 @@ -6960,53 +4497,69 @@ snapshots: proc-log: 3.0.0 transitivePeerDependencies: - supports-color + dev: true - npmlog@6.0.2: + /npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. dependencies: are-we-there-yet: 3.0.1 console-control-strings: 1.1.0 gauge: 4.0.4 set-blocking: 2.0.0 + dev: true - object-assign@4.1.1: {} - - object-inspect@1.13.1: {} - - object-keys@1.1.1: {} - - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 + /object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false - obliterator@2.0.4: {} + /obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + dev: false - on-exit-leak-free@2.1.2: {} + /on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} - once@1.4.0: + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 + dev: true - one-time@1.0.0: + /one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} dependencies: fn.name: 1.1.0 + dev: false - onetime@5.1.2: + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} dependencies: mimic-fn: 2.1.0 + dev: true - opentelemetry-instrumentation-fetch-node@1.2.0: + /opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} + engines: {node: '>18.0.0'} + requiresBuild: true + peerDependencies: + '@opentelemetry/api': ^1.6.0 dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color + dev: false optional: true - ora@5.4.1: + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} dependencies: bl: 4.1.0 chalk: 4.1.2 @@ -7017,53 +4570,99 @@ snapshots: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 + dev: true - os-tmpdir@1.0.2: {} + /os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + dev: true - outdent@0.5.0: {} + /outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + dev: true - p-cancelable@3.0.0: {} + /p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + dev: true - p-filter@2.1.0: + /p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} dependencies: p-map: 2.1.0 + dev: true - p-limit@2.3.0: + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} dependencies: p-try: 2.2.0 + dev: true - p-limit@3.1.0: + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 + dev: true - p-limit@4.0.0: + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dependencies: - yocto-queue: 1.0.0 + yocto-queue: 1.1.1 + dev: true - p-locate@4.1.0: + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} dependencies: p-limit: 2.3.0 + dev: true - p-locate@5.0.0: + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} dependencies: p-limit: 3.1.0 + dev: true - p-map@2.1.0: {} + /p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + dev: true - p-map@4.0.0: + /p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} dependencies: aggregate-error: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true - p-try@2.2.0: {} + /package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + dev: true - package-json@8.1.1: + /package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} dependencies: got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 semver: 7.6.2 + dev: true - pacote@15.2.0: + /pacote@15.2.0: + resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true dependencies: '@npmcli/git': 4.1.0 '@npmcli/installed-package-contents': 2.1.0 @@ -7086,72 +4685,111 @@ snapshots: transitivePeerDependencies: - bluebird - supports-color + dev: true - parse-github-url@1.0.2: {} - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.24.6 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 + /parse-github-url@1.0.3: + resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} + engines: {node: '>= 0.10'} + hasBin: true + dev: true - path-exists@4.0.0: {} + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true - path-is-absolute@1.0.1: {} + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true - path-key@3.1.1: {} + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true - path-parse@1.0.7: {} + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: + /path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} dependencies: - lru-cache: 10.2.2 + lru-cache: 10.4.3 minipass: 7.1.2 + dev: true - path-type@4.0.0: {} + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true - pathe@1.1.2: {} + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true - pathval@1.1.1: {} + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true - pause-stream@0.0.11: + /pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} dependencies: through: 2.3.8 + dev: true - pg-int8@1.0.1: {} + /pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + dev: false - pg-protocol@1.6.1: {} + /pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + dev: false - pg-types@2.2.0: + /pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 postgres-bytea: 1.0.0 postgres-date: 1.0.7 postgres-interval: 1.2.0 + dev: false - pgsql-ast-parser@11.2.0: + /pgsql-ast-parser@11.2.0: + resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} dependencies: moo: 0.5.2 nearley: 2.20.1 + dev: false - pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: {} - - picocolors@1.0.1: {} + /picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - picomatch@2.3.1: {} + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true - pify@4.0.1: {} + /pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: true - pino-abstract-transport@1.2.0: + /pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} dependencies: readable-stream: 4.5.2 split2: 4.2.0 - pino-std-serializers@6.2.2: {} + /pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} - pino@8.21.0: + /pino@8.21.0: + resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} + hasBin: true dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 @@ -7165,167 +4803,253 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 - pkg-dir@4.2.0: + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} dependencies: find-up: 4.1.0 + dev: true - pkg-types@1.1.1: + /pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} dependencies: confbox: 0.1.7 - mlly: 1.7.0 + mlly: 1.7.1 pathe: 1.1.2 + dev: true - possible-typed-array-names@1.0.0: {} - - postcss@8.4.38: + /postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 + dev: true - postgres-array@2.0.0: {} + /postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + dev: false - postgres-bytea@1.0.0: {} + /postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + dev: false - postgres-date@1.0.7: {} + /postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + dev: false - postgres-interval@1.2.0: + /postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} dependencies: xtend: 4.0.2 + dev: false - preferred-pm@3.1.3: + /preferred-pm@3.1.4: + resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} + engines: {node: '>=10'} dependencies: find-up: 5.0.0 find-yarn-workspace-root2: 1.2.16 path-exists: 4.0.0 - which-pm: 2.0.0 + which-pm: 2.2.0 + dev: true - prettier@2.8.8: {} + /prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true - pretty-format@29.7.0: + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 + dev: true - proc-log@3.0.0: {} + /proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - process-nextick-args@2.0.1: {} + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true - process-warning@2.3.2: {} + /process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} - process-warning@3.0.0: {} + /process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} - process@0.11.10: {} + /process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} - progress@2.0.3: {} + /progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true - promise-inflight@1.0.1: {} + /promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + dev: true - promise-retry@2.0.1: + /promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} dependencies: err-code: 2.0.3 retry: 0.12.0 + dev: true - prompts-ncu@3.0.0: + /prompts-ncu@3.0.0: + resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} + engines: {node: '>= 14'} dependencies: kleur: 4.1.5 sisteransi: 1.0.5 + dev: true - proto-list@1.2.4: {} + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true - proxy-addr@2.0.7: + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - ps-tree@1.2.0: + /ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true dependencies: event-stream: 3.3.4 + dev: true - pseudomap@1.0.2: {} + /pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true - pstree.remy@1.1.8: {} + /pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + dev: true - punycode@2.3.1: {} + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} - pupa@3.1.0: + /pupa@3.1.0: + resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + engines: {node: '>=12.20'} dependencies: escape-goat: 4.0.0 + dev: true - queue-microtask@1.2.3: {} - - quick-format-unescaped@4.0.4: {} + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true - quick-lru@4.0.1: {} + /quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - quick-lru@5.1.1: {} + /quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + dev: true - railroad-diagrams@1.0.0: {} + /railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + dev: false - randexp@0.4.6: + /randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} dependencies: discontinuous-range: 1.0.0 ret: 0.1.15 + dev: false - rc-config-loader@4.1.3: + /rc-config-loader@4.1.3: + resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) js-yaml: 4.1.0 json5: 2.2.3 require-from-string: 2.0.2 transitivePeerDependencies: - supports-color + dev: true - rc@1.2.8: + /rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true dependencies: deep-extend: 0.6.0 ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 + dev: true - react-is@18.3.1: {} + /react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + dev: true - read-package-json-fast@3.0.2: + /read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: json-parse-even-better-errors: 3.0.2 npm-normalize-package-bin: 3.0.1 + dev: true - read-package-json@6.0.4: + /read-package-json@6.0.4: + resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. dependencies: - glob: 10.4.1 + glob: 10.4.5 json-parse-even-better-errors: 3.0.2 normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 + dev: true - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - read-yaml-file@1.1.0: + /read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 + dev: true - readable-stream@1.0.34: + /readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} dependencies: core-util-is: 1.0.3 inherits: 2.0.4 isarray: 0.0.1 string_decoder: 0.10.31 + dev: true - readable-stream@2.3.8: + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -7334,14 +5058,19 @@ snapshots: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 + dev: true - readable-stream@3.6.2: + /readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - readable-stream@4.5.2: + /readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: abort-controller: 3.0.0 buffer: 6.0.3 @@ -7349,216 +5078,284 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 - readdirp@3.6.0: + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} dependencies: picomatch: 2.3.1 + dev: true - real-require@0.2.0: {} - - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - - regenerator-runtime@0.14.1: {} + /real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: true - registry-auth-token@5.0.2: + /registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} dependencies: '@pnpm/npm-conf': 2.2.2 + dev: true - registry-url@6.0.1: + /registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} dependencies: rc: 1.2.8 + dev: true - remote-git-tags@3.0.0: {} + /remote-git-tags@3.0.0: + resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} + engines: {node: '>=8'} + dev: true - require-directory@2.1.1: {} + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true - require-from-string@2.0.2: {} + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} - require-in-the-middle@7.3.0: + /require-in-the-middle@7.3.0: + resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} + engines: {node: '>=8.6.0'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: - supports-color + dev: false - require-main-filename@2.0.0: {} - - resolve-alpn@1.2.1: {} + /resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + dev: true - resolve-from@5.0.0: {} + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true - resolve@1.22.8: + /resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true dependencies: - is-core-module: 2.13.1 + is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - responselike@3.0.0: + /responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} dependencies: lowercase-keys: 3.0.0 + dev: true - restore-cursor@3.1.0: + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} dependencies: onetime: 5.1.2 signal-exit: 3.0.7 + dev: true - ret@0.1.15: {} + /ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + dev: false - ret@0.2.2: {} + /ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} - retry@0.12.0: {} + /retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + dev: true - reusify@1.0.4: {} + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.3.1: {} + /rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rimraf@2.7.1: + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true dependencies: glob: 7.2.3 + dev: true - rimraf@3.0.2: + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true dependencies: glob: 7.2.3 + dev: true - rimraf@5.0.7: + /rimraf@5.0.9: + resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} + engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} + hasBin: true dependencies: - glob: 10.4.1 + glob: 10.4.5 + dev: true - rollup@4.18.0: + /rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.0 - '@rollup/rollup-android-arm64': 4.18.0 - '@rollup/rollup-darwin-arm64': 4.18.0 - '@rollup/rollup-darwin-x64': 4.18.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 - '@rollup/rollup-linux-arm-musleabihf': 4.18.0 - '@rollup/rollup-linux-arm64-gnu': 4.18.0 - '@rollup/rollup-linux-arm64-musl': 4.18.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 - '@rollup/rollup-linux-riscv64-gnu': 4.18.0 - '@rollup/rollup-linux-s390x-gnu': 4.18.0 - '@rollup/rollup-linux-x64-gnu': 4.18.0 - '@rollup/rollup-linux-x64-musl': 4.18.0 - '@rollup/rollup-win32-arm64-msvc': 4.18.0 - '@rollup/rollup-win32-ia32-msvc': 4.18.0 - '@rollup/rollup-win32-x64-msvc': 4.18.0 + '@rollup/rollup-android-arm-eabi': 4.18.1 + '@rollup/rollup-android-arm64': 4.18.1 + '@rollup/rollup-darwin-arm64': 4.18.1 + '@rollup/rollup-darwin-x64': 4.18.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.18.1 + '@rollup/rollup-linux-arm-musleabihf': 4.18.1 + '@rollup/rollup-linux-arm64-gnu': 4.18.1 + '@rollup/rollup-linux-arm64-musl': 4.18.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.18.1 + '@rollup/rollup-linux-riscv64-gnu': 4.18.1 + '@rollup/rollup-linux-s390x-gnu': 4.18.1 + '@rollup/rollup-linux-x64-gnu': 4.18.1 + '@rollup/rollup-linux-x64-musl': 4.18.1 + '@rollup/rollup-win32-arm64-msvc': 4.18.1 + '@rollup/rollup-win32-ia32-msvc': 4.18.1 + '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 + dev: true - rsocket-core@1.0.0-alpha.3: {} + /rsocket-core@1.0.0-alpha.3: + resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} - rsocket-websocket-client@1.0.0-alpha.3: + /rsocket-websocket-client@1.0.0-alpha.3: + resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} dependencies: rsocket-core: 1.0.0-alpha.3 + dev: true - run-async@3.0.0: {} + /run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + dev: true - run-parallel@1.2.0: + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 + dev: true - rxjs@7.8.1: - dependencies: - tslib: 2.6.2 - - safe-array-concat@1.1.2: + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 - - safe-buffer@5.1.2: {} + tslib: 2.6.3 + dev: true - safe-buffer@5.2.1: {} + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true - safe-regex-test@1.0.3: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex2@2.0.0: + /safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} dependencies: ret: 0.2.2 - safe-stable-stringify@2.4.3: {} + /safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} - safer-buffer@2.1.2: {} + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true - secure-json-parse@2.7.0: {} + /secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - semver-diff@4.0.0: + /semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} dependencies: semver: 7.6.2 + dev: true - semver-utils@1.1.4: {} - - semver@5.7.2: {} - - semver@7.6.2: {} - - set-blocking@2.0.0: {} + /semver-utils@1.1.4: + resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} + dev: true - set-cookie-parser@2.6.0: {} + /semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - shebang-command@1.2.0: + /shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} dependencies: shebang-regex: 1.0.0 + dev: true - shebang-command@2.0.0: + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + dev: true - shebang-regex@1.0.0: {} - - shebang-regex@3.0.0: {} + /shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + dev: true - shell-quote@1.8.1: {} + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true - shimmer@1.2.1: {} + /shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + dev: true - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 + /shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + dev: false - siginfo@2.0.0: {} + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true - signal-exit@3.0.7: {} + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true - signal-exit@4.1.0: {} + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true - sigstore@1.9.0: + /sigstore@1.9.0: + resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 @@ -7567,197 +5364,271 @@ snapshots: make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color + dev: true - simple-swizzle@0.2.2: + /simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: is-arrayish: 0.3.2 + dev: false - simple-update-notifier@2.0.0: + /simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} dependencies: semver: 7.6.2 + dev: true - sisteransi@1.0.5: {} - - slash@3.0.0: {} + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true - smart-buffer@4.2.0: {} + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true - smartwrap@2.0.2: - dependencies: - array.prototype.flat: 1.3.2 - breakword: 1.0.6 - grapheme-splitter: 1.0.4 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - yargs: 15.4.1 + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: true - socks-proxy-agent@7.0.0: + /socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color + dev: true - socks@2.8.3: + /socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 + dev: true - sonic-boom@3.8.1: + /sonic-boom@3.8.1: + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} dependencies: atomic-sleep: 1.0.0 - source-map-js@1.2.0: {} + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true - source-map-support@0.5.21: + /source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 + dev: true - source-map@0.6.1: {} + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: true - sparse-bitfield@3.0.3: + /sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} dependencies: memory-pager: 1.5.0 + dev: false - spawn-command@0.0.2: {} + /spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + dev: true - spawn-please@2.0.2: + /spawn-please@2.0.2: + resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} + engines: {node: '>=14'} dependencies: cross-spawn: 7.0.3 + dev: true - spawndamnit@2.0.0: + /spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} dependencies: cross-spawn: 5.1.0 signal-exit: 3.0.7 + dev: true - spdx-correct@3.2.0: + /spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.18 + dev: true - spdx-exceptions@2.5.0: {} + /spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + dev: true - spdx-expression-parse@3.0.1: + /spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 + dev: true - spdx-license-ids@3.0.18: {} + /spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + dev: true - split2@4.2.0: {} + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} - split@0.3.3: + /split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} dependencies: through: 2.3.8 + dev: true - sprintf-js@1.0.3: {} + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true - sprintf-js@1.1.3: {} + /sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + dev: true - ssri@10.0.6: + /ssri@10.0.6: + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: minipass: 7.1.2 + dev: true - ssri@9.0.1: + /ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: minipass: 3.3.6 + dev: true - stack-trace@0.0.10: {} + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + dev: false - stackback@0.0.2: {} + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true - std-env@3.7.0: {} + /std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + dev: true - stream-combiner@0.0.4: + /stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} dependencies: duplexer: 0.1.2 + dev: true - stream-transform@2.1.3: - dependencies: - mixme: 0.5.10 - - string-argv@0.3.2: {} + /string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + dev: true - string-width@4.2.3: + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 + dev: true - string-width@5.1.2: + /string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 + dev: true - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string_decoder@0.10.31: {} + /string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + dev: true - string_decoder@1.1.1: + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: safe-buffer: 5.1.2 + dev: true - string_decoder@1.3.0: + /string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 - strip-ansi@6.0.1: + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 + dev: true - strip-ansi@7.1.0: + /strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 + dev: true - strip-bom@3.0.0: {} - - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true - strip-json-comments@2.0.1: {} + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true - strip-json-comments@5.0.1: {} + /strip-json-comments@5.0.1: + resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} + engines: {node: '>=14.16'} + dev: true - strip-literal@1.3.0: + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} dependencies: - acorn: 8.11.3 + acorn: 8.12.1 + dev: true - supports-color@5.5.0: + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - supports-color@7.2.0: + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} dependencies: has-flag: 4.0.0 - supports-color@8.1.1: + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} dependencies: has-flag: 4.0.0 + dev: true - supports-preserve-symlinks-flag@1.0.0: {} + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} - tar@6.2.1: + /tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 @@ -7765,53 +5636,101 @@ snapshots: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 + dev: true - term-size@2.2.1: {} + /term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + dev: true - text-hex@1.0.0: {} + /text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + dev: false - thread-stream@2.7.0: + /thread-stream@2.7.0: + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} dependencies: real-require: 0.2.0 - through2@2.0.5: + /through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} dependencies: readable-stream: 2.3.8 xtend: 4.0.2 + dev: true - through@2.3.8: {} + /through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + dev: true - tinybench@2.8.0: {} + /tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + dev: true - tinypool@0.7.0: {} + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + dev: true - tinyspy@2.2.1: {} + /tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + dev: true - tmp@0.0.33: + /tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} dependencies: os-tmpdir: 1.0.2 + dev: true - to-regex-range@5.0.1: + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 + dev: true - toad-cache@3.7.0: {} + /toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} - touch@3.1.1: {} + /touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + dev: true - tr46@4.1.1: + /tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} dependencies: punycode: 2.3.1 + dev: false - tree-kill@1.2.2: {} - - trim-newlines@3.0.1: {} + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true - triple-beam@1.4.1: {} + /triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + dev: false - ts-codec@1.2.2: {} + /ts-codec@1.2.2: + resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} + dev: false - ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): + /ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true dependencies: chokidar: 3.6.0 dynamic-dedupe: 0.3.0 @@ -7828,8 +5747,21 @@ snapshots: - '@swc/core' - '@swc/wasm' - '@types/node' + dev: true - ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): + /ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -7837,8 +5769,8 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 18.11.11 - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 @@ -7846,136 +5778,146 @@ snapshots: typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + dev: true - tsc-watch@6.2.0(typescript@5.2.2): + /tsc-watch@6.2.0(typescript@5.2.2): + resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' dependencies: cross-spawn: 7.0.3 node-cleanup: 2.1.2 ps-tree: 1.2.0 string-argv: 0.3.2 typescript: 5.2.2 + dev: true - tsconfck@3.0.3(typescript@5.4.5): - optionalDependencies: - typescript: 5.4.5 + /tsconfck@3.1.1(typescript@5.2.2): + resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.2.2 + dev: true - tsconfig@7.0.0: + /tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} dependencies: '@types/strip-bom': 3.0.0 '@types/strip-json-comments': 0.0.30 strip-bom: 3.0.0 strip-json-comments: 2.0.1 + dev: true - tslib@2.6.2: {} - - tty-table@4.2.3: - dependencies: - chalk: 4.1.2 - csv: 5.5.3 - kleur: 4.1.5 - smartwrap: 2.0.2 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - yargs: 17.7.2 + /tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - tuf-js@1.1.7: + /tuf-js@1.1.7: + resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color + dev: true - type-detect@4.0.8: {} - - type-fest@0.13.1: {} - - type-fest@0.21.3: {} - - type-fest@0.6.0: {} - - type-fest@0.8.1: {} - - type-fest@1.4.0: {} - - type-fest@2.19.0: {} - - typed-array-buffer@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + /type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + dev: true - typed-array-length@1.0.6: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 + /type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + dev: true - typedarray-to-buffer@3.1.5: + /typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: is-typedarray: 1.0.0 + dev: true - typescript@5.2.2: {} - - typescript@5.4.5: {} - - ufo@1.5.3: {} + /typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + dev: true - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + /ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + dev: true - undefsafe@2.0.5: {} + /undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + dev: true - unique-filename@2.0.1: + /unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: unique-slug: 3.0.0 + dev: true - unique-filename@3.0.0: + /unique-filename@3.0.0: + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: unique-slug: 4.0.0 + dev: true - unique-slug@3.0.0: + /unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} dependencies: imurmurhash: 0.1.4 + dev: true - unique-slug@4.0.0: + /unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} dependencies: imurmurhash: 0.1.4 + dev: true - unique-string@3.0.0: + /unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} dependencies: crypto-random-string: 4.0.0 + dev: true - universalify@0.1.2: {} + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true - untildify@4.0.0: {} + /untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + dev: true - update-notifier@6.0.2: + /update-notifier@6.0.2: + resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} + engines: {node: '>=14.16'} dependencies: boxen: 7.1.1 chalk: 5.3.0 @@ -7991,34 +5933,53 @@ snapshots: semver: 7.6.2 semver-diff: 4.0.0 xdg-basedir: 5.1.0 + dev: true - uri-js@4.4.1: + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: punycode: 2.3.1 - util-deprecate@1.0.2: {} + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@9.0.1: {} + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false - v8-compile-cache-lib@3.0.1: {} + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true - validate-npm-package-license@3.0.4: + /validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 + dev: true - validate-npm-package-name@5.0.1: {} + /validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true - vary@1.1.2: {} + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false - vite-node@0.34.6(@types/node@18.11.11): + /vite-node@0.34.6(@types/node@18.11.11): + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@5.5.0) - mlly: 1.7.0 + debug: 4.3.5(supports-color@5.5.0) + mlly: 1.7.1 pathe: 1.1.2 picocolors: 1.0.1 - vite: 5.2.11(@types/node@18.11.11) + vite: 5.3.3(@types/node@18.11.11) transitivePeerDependencies: - '@types/node' - less @@ -8028,28 +5989,90 @@ snapshots: - sugarss - supports-color - terser + dev: true - vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@18.11.11)): + /vite-tsconfig-paths@4.3.2(typescript@5.2.2): + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) globrex: 0.1.2 - tsconfck: 3.0.3(typescript@5.4.5) - optionalDependencies: - vite: 5.2.11(@types/node@18.11.11) + tsconfck: 3.1.1(typescript@5.2.2) transitivePeerDependencies: - supports-color - typescript + dev: true - vite@5.2.11(@types/node@18.11.11): + /vite@5.3.3(@types/node@18.11.11): + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 - optionalDependencies: '@types/node': 18.11.11 + esbuild: 0.21.5 + postcss: 8.4.39 + rollup: 4.18.1 + optionalDependencies: fsevents: 2.3.3 + dev: true - vitest@0.34.6: + /vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true dependencies: '@types/chai': 4.3.16 '@types/chai-subset': 1.3.5 @@ -8059,11 +6082,11 @@ snapshots: '@vitest/snapshot': 0.34.6 '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.3 cac: 6.7.14 chai: 4.4.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) local-pkg: 0.4.3 magic-string: 0.30.10 pathe: 1.1.2 @@ -8072,9 +6095,9 @@ snapshots: strip-literal: 1.3.0 tinybench: 2.8.0 tinypool: 0.7.0 - vite: 5.2.11(@types/node@18.11.11) + vite: 5.3.3(@types/node@18.11.11) vite-node: 0.34.6(@types/node@18.11.11) - why-is-node-running: 2.2.2 + why-is-node-running: 2.3.0 transitivePeerDependencies: - less - lightningcss @@ -8083,157 +6106,204 @@ snapshots: - sugarss - supports-color - terser + dev: true - wcwidth@1.0.1: + /wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.4 + dev: true - web-streams-polyfill@3.3.3: {} + /web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + dev: false - webidl-conversions@7.0.0: {} + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: false - whatwg-url@13.0.0: + /whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 + dev: false - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - which-module@2.0.1: {} - - which-pm@2.0.0: + /which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} dependencies: load-yaml-file: 0.2.0 path-exists: 4.0.0 + dev: true - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - - which@1.3.1: + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true dependencies: isexe: 2.0.0 + dev: true - which@2.0.2: + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true dependencies: isexe: 2.0.0 + dev: true - which@3.0.1: + /which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true dependencies: isexe: 2.0.0 + dev: true - why-is-node-running@2.2.2: + /why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true dependencies: siginfo: 2.0.0 stackback: 0.0.2 + dev: true - wide-align@1.1.5: + /wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: string-width: 4.2.3 + dev: true - widest-line@4.0.1: + /widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} dependencies: string-width: 5.1.2 + dev: true - winston-transport@4.7.0: + /winston-transport@4.7.1: + resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} + engines: {node: '>= 12.0.0'} dependencies: - logform: 2.6.0 + logform: 2.6.1 readable-stream: 3.6.2 triple-beam: 1.4.1 + dev: false - winston@3.13.0: + /winston@3.13.1: + resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} + engines: {node: '>= 12.0.0'} dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 async: 3.2.5 is-stream: 2.0.1 - logform: 2.6.0 + logform: 2.6.1 one-time: 1.0.0 readable-stream: 3.6.2 safe-stable-stringify: 2.4.3 stack-trace: 0.0.10 triple-beam: 1.4.1 - winston-transport: 4.7.0 + winston-transport: 4.7.1 + dev: false - wrap-ansi@6.2.0: + /wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + dev: true - wrap-ansi@7.0.0: + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 + dev: true - wrap-ansi@8.1.0: + /wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 + dev: true - wrappy@1.0.2: {} + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true - write-file-atomic@3.0.3: + /write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} dependencies: imurmurhash: 0.1.4 is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 + dev: true - ws@8.17.0: {} - - ws@8.2.3: {} - - xdg-basedir@5.1.0: {} - - xtend@4.0.2: {} - - y18n@4.0.3: {} + /ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true - y18n@5.0.8: {} + /xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + dev: true - yallist@2.1.2: {} + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} - yallist@4.0.0: {} + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true - yaml@2.4.2: {} + /yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: true - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true - yargs-parser@20.2.9: {} + /yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + dev: false - yargs-parser@21.1.1: {} + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true - yargs@16.2.0: + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} dependencies: cliui: 7.0.4 escalade: 3.1.2 @@ -8242,8 +6312,11 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 + dev: true - yargs@17.7.2: + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -8252,11 +6325,35 @@ snapshots: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 + dev: true + + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true - yn@3.1.1: {} + /yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + dev: true - yocto-queue@0.1.0: {} + /yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + dev: true - yocto-queue@1.0.0: {} + /zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + dev: false - zod@3.23.8: {} + github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: + resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} + name: pgwire + version: 0.7.0 + engines: {node: '>=14.18.0'} + dev: false From f0e1cf17273b9cead7df5e3fd74fc2479aa395e4 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 12:31:13 +0200 Subject: [PATCH 034/298] SyncAPI tweaks based on feedback ReplicationAdapter enum fixes --- packages/service-core/src/api/SyncAPI.ts | 27 +++++++++++++------ .../replication/core/ReplicationAdapter.ts | 12 ++++----- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index c0771b9f0..ac63c8720 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -1,19 +1,16 @@ import { TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; +import * as t from 'ts-codec'; /** * Describes all the methods currently required to service the sync API endpoints. - * TODO: This interface needs to be cleaned up and made more generic. It describes the current functionality required by our API routes */ export interface SyncAPI { /** - * Performs diagnostics on the "connection" + * Checks the current connection status of the datasource. * This is usually some test query to verify the source can be reached. */ - getDiagnostics(): Promise<{ - connected: boolean; - errors?: Array<{ level: string; message: string }>; - }>; + getConnectionStatus(): Promise; /** * Generates replication table information from a given pattern of tables. @@ -49,10 +46,10 @@ export interface SyncAPI { getConnectionSchema(): Promise; /** - * Executes a SQL statement and returns the result. This is currently used in the + * Executes a query and return the result from the data source. This is currently used in the * admin API which is exposed in Collide. */ - executeSQL(sql: string, params: any[]): Promise; + executeQuery(query: string, params: any[]): Promise; //CRUD API : I don't think this is used besides maybe niche dev use cases } @@ -65,3 +62,17 @@ interface PatternResult { tables?: types.TableInfo[]; table?: types.TableInfo; } + +interface ErrorDescription { + level: string; + message: string; +} +interface StatusResponse { + connected: boolean; + errors?: ErrorDescription[]; +} + +interface QueryResults { + columns: string[]; + rows: (string | number | boolean | null)[][]; +} diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 6485926bd..83063aa62 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -69,10 +69,10 @@ export interface ReplicationAdapter { } export enum ConnectionError { - INCORRECT_CREDENTIALS, - HOST_INACCESSIBLE, - CONNECTION_REFUSED, - CONNECTION_CLOSED, - RETRIEVAL_FAILED, - GENERAL + INCORRECT_CREDENTIALS = 'INCORRECT_CREDENTIALS', + HOST_INACCESSIBLE = 'HOST_INACCESSIBLE', + CONNECTION_REFUSED = 'CONNECTION_REFUSED', + CONNECTION_CLOSED = 'CONNECTION_CLOSED', + RETRIEVAL_FAILED = 'RETRIEVAL_FAILED', + GENERAL = 'GENERAL' } From 6baa935868af3459492dae20089b3213ba38f0ed Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 13:05:28 +0200 Subject: [PATCH 035/298] Removed unused imports --- packages/service-core/src/api/SyncAPI.ts | 1 - .../src/replication/core/ReplicationEntity.ts | 10 ++++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/service-core/src/api/SyncAPI.ts b/packages/service-core/src/api/SyncAPI.ts index ac63c8720..68f9ae7d4 100644 --- a/packages/service-core/src/api/SyncAPI.ts +++ b/packages/service-core/src/api/SyncAPI.ts @@ -1,6 +1,5 @@ import { TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; -import * as t from 'ts-codec'; /** * Describes all the methods currently required to service the sync API endpoints. diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts index 01d8f7b8f..0f4d52af5 100644 --- a/packages/service-core/src/replication/core/ReplicationEntity.ts +++ b/packages/service-core/src/replication/core/ReplicationEntity.ts @@ -1,4 +1,3 @@ -import { SourceTable } from '../../storage/SourceTable.js'; import { SqliteRow } from '@powersync/service-sync-rules'; /** @@ -58,8 +57,8 @@ export abstract class ReplicationEntity { } /** - * Get the number of entries for this Entity - * @param connection + * Get the number of entries for this Entity + * @param connection */ public abstract count(connection: TConnection): Promise; @@ -70,5 +69,8 @@ export abstract class ReplicationEntity { * @param connection * @param entryConsumer */ - public abstract getSnapshot(connection: TConnection, entryConsumer: (batch: SqliteRow[]) => {}): Promise; + public abstract getSnapshot( + connection: TConnection, + entryConsumer: (batch: SqliteRow[]) => {} + ): Promise; } From 80ecdd8d5de12a199ed6aea97512881733a1c1af Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 13:48:18 +0200 Subject: [PATCH 036/298] Added interfaces for constructor parameters --- .../service-core/src/modules/AbstractModule.ts | 10 ++++++++-- .../src/replication/core/ReplicationEngine.ts | 12 ++++++++---- .../src/replication/core/ReplicationModule.ts | 15 +++++++++------ .../src/replication/core/Replicator.ts | 11 ++++++++--- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index dfad51f97..554c866bd 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -1,10 +1,16 @@ import { ServiceContext } from '../system/ServiceContext.js'; +export interface AbstractModuleOptions { + name: string; +} + export abstract class AbstractModule { public name: string; + protected options: AbstractModuleOptions; - protected constructor(name: string) { - this.name = name; + protected constructor(options: AbstractModuleOptions) { + this.options = options; + this.name = options.name; } /** diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index 56c392120..b0fea21df 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -2,12 +2,16 @@ import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; import { Replicator } from './Replicator.js'; +export interface ReplicationEngineOptions { + storage: BucketStorageFactory; +} + export class ReplicationEngine { - private readonly storage: BucketStorageFactory; + private readonly options: ReplicationEngineOptions; private readonly replicators: Map, Replicator> = new Map(); - constructor(storage: BucketStorageFactory) { - this.storage = storage; + constructor(options: ReplicationEngineOptions) { + this.options = options; } /** @@ -20,7 +24,7 @@ export class ReplicationEngine { if (this.replicators.has(adapter)) { throw new Error(`Replicator for type ${adapter.name} already registered`); } - this.replicators.set(adapter, new Replicator(this.storage, adapter)); + this.replicators.set(adapter, new Replicator(adapter, this.options.storage)); } /** diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 70b93f338..751155e9b 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -3,9 +3,13 @@ import * as t from 'ts-codec'; import { logger, schema } from '@powersync/lib-services-framework'; import { ReplicationAdapter } from './ReplicationAdapter.js'; import { SyncAPI } from '../../api/SyncAPI.js'; -import { AbstractModule } from '../../modules/AbstractModule.js'; +import { AbstractModule, AbstractModuleOptions } from '../../modules/AbstractModule.js'; import { ServiceContext } from '../../system/ServiceContext.js'; +export interface ReplicationModuleOptions extends AbstractModuleOptions { + type: string; +} + /** * A replication module describes all the functionality that PowerSync requires to * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. @@ -14,13 +18,12 @@ export abstract class ReplicationModule extends AbstractModule { protected type: string; /** - * @param name - * @param type The type of the data source * @protected + * @param options */ - protected constructor(name: string, type: string) { - super(name); - this.type = type; + protected constructor(options: ReplicationModuleOptions) { + super(options); + this.type = options.type; } /** diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts index 12b05f062..047f9b688 100644 --- a/packages/service-core/src/replication/core/Replicator.ts +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -1,6 +1,11 @@ import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +export interface ReplicatorOptions { + adapter: ReplicationAdapter; + storage: BucketStorageFactory; +} + /** * A replicator manages the mechanics for replicating data from a data source to a storage bucket. * This includes copying across the original data set and then keeping it in sync with the data source. @@ -10,9 +15,9 @@ export class Replicator { private readonly adapter: ReplicationAdapter; private storage: BucketStorageFactory; - constructor(storage: BucketStorageFactory, adapter: ReplicationAdapter) { - this.adapter = adapter; - this.storage = storage; + constructor(options: ReplicatorOptions) { + this.adapter = options.adapter; + this.storage = options.storage; } public async start(): Promise { From 6943b49d62b73390960d58389309427e0ad0eaaf Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 16 Jul 2024 13:58:19 +0200 Subject: [PATCH 037/298] Fixed Replicator arguments Updated lockfile --- .../src/replication/core/ReplicationEngine.ts | 2 +- pnpm-lock.yaml | 7258 +++++++++-------- 2 files changed, 4020 insertions(+), 3240 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index b0fea21df..abcbb346f 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -24,7 +24,7 @@ export class ReplicationEngine { if (this.replicators.has(adapter)) { throw new Error(`Replicator for type ${adapter.name} already registered`); } - this.replicators.set(adapter, new Replicator(adapter, this.options.storage)); + this.replicators.set(adapter, new Replicator({ adapter: adapter, storage: this.options.storage })); } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c250d75f..2cd881d9c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -104,7 +104,7 @@ importers: version: 3.6.0 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 packages/jsonbig: dependencies: @@ -225,7 +225,7 @@ importers: version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -371,7 +371,7 @@ importers: version: 3.3.2 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -412,38 +412,3229 @@ importers: packages: - /@babel/code-frame@7.24.7: + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.24.8': + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} + engines: {node: '>=6.9.0'} + + '@changesets/apply-release-plan@7.0.4': + resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} + + '@changesets/assemble-release-plan@6.0.3': + resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} + + '@changesets/changelog-git@0.2.0': + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + + '@changesets/cli@2.27.7': + resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} + hasBin: true + + '@changesets/config@3.0.2': + resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.1': + resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} + + '@changesets/get-release-plan@4.0.3': + resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.0': + resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + + '@changesets/logger@0.1.0': + resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + + '@changesets/parse@0.4.0': + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + + '@changesets/pre@2.0.0': + resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + + '@changesets/read@0.6.0': + resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + + '@changesets/should-skip-package@0.1.0': + resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.0.0': + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + + '@changesets/write@0.3.1': + resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@colors/colors@1.6.0': + resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@dabh/diagnostics@2.0.3': + resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@fastify/ajv-compiler@3.6.0': + resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} + + '@fastify/cors@8.4.1': + resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} + + '@fastify/error@3.4.1': + resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + + '@fastify/fast-json-stringify-compiler@4.3.0': + resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + + '@fastify/merge-json-schemas@0.1.1': + resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + + '@gar/promisify@1.1.3': + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} + + '@humanwhocodes/momoa@2.0.4': + resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} + engines: {node: '>=10.10.0'} + + '@inquirer/figures@1.0.3': + resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} + engines: {node: '>=18'} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@js-sdsl/ordered-set@4.4.2': + resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@mongodb-js/saslprep@1.1.7': + resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@npmcli/fs@2.1.2': + resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + '@npmcli/fs@3.1.1': + resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/git@4.1.0': + resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/installed-package-contents@2.1.0': + resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + '@npmcli/move-file@2.0.1': + resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This functionality has been moved to @npmcli/fs + + '@npmcli/node-gyp@3.0.0': + resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/promise-spawn@6.0.2': + resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@npmcli/run-script@6.0.2': + resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@opentelemetry/api-logs@0.51.1': + resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} + engines: {node: '>=14'} + + '@opentelemetry/api-logs@0.52.1': + resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} + engines: {node: '>=14'} + + '@opentelemetry/api@1.6.0': + resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.8.0': + resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/context-async-hooks@1.25.1': + resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@1.17.0': + resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.7.0' + + '@opentelemetry/core@1.24.1': + resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/core@1.25.1': + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-metrics-otlp-http@0.51.1': + resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.43.0': + resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.51.1': + resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-connect@0.38.0': + resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.41.0': + resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-fastify@0.38.0': + resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-graphql@0.42.0': + resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-hapi@0.40.0': + resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.52.1': + resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-ioredis@0.42.0': + resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-koa@0.42.0': + resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongodb@0.46.0': + resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mongoose@0.40.0': + resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql2@0.40.0': + resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-mysql@0.40.0': + resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-nestjs-core@0.39.0': + resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-pg@0.43.0': + resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-redis-4@0.41.0': + resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.46.0': + resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.52.1': + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.51.1': + resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/otlp-transformer@0.51.1': + resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' + + '@opentelemetry/redis-common@0.36.2': + resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} + engines: {node: '>=14'} + + '@opentelemetry/resources@1.17.0': + resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.7.0' + + '@opentelemetry/resources@1.24.1': + resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/resources@1.25.1': + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.51.1': + resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.9.0' + '@opentelemetry/api-logs': '>=0.39.1' + + '@opentelemetry/sdk-metrics@1.17.0': + resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.7.0' + + '@opentelemetry/sdk-metrics@1.24.1': + resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.9.0' + + '@opentelemetry/sdk-trace-base@1.24.1': + resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.9.0' + + '@opentelemetry/sdk-trace-base@1.25.1': + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.17.0': + resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.24.1': + resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} + engines: {node: '>=14'} + + '@opentelemetry/semantic-conventions@1.25.1': + resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} + engines: {node: '>=14'} + + '@opentelemetry/sql-common@0.40.1': + resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.1.0 + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.2.2': + resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} + engines: {node: '>=12'} + + '@prisma/instrumentation@5.16.1': + resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} + + '@rollup/rollup-android-arm-eabi@4.18.1': + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.18.1': + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.18.1': + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.18.1': + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.18.1': + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.18.1': + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.18.1': + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.18.1': + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.18.1': + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.18.1': + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.18.1': + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.18.1': + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.18.1': + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.18.1': + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + cpu: [x64] + os: [win32] + + '@sentry/core@8.17.0': + resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} + engines: {node: '>=14.18'} + + '@sentry/node@8.17.0': + resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} + engines: {node: '>=14.18'} + + '@sentry/opentelemetry@8.17.0': + resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} + engines: {node: '>=14.18'} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/core': ^1.25.1 + '@opentelemetry/instrumentation': ^0.52.1 + '@opentelemetry/sdk-trace-base': ^1.25.1 + '@opentelemetry/semantic-conventions': ^1.25.1 + + '@sentry/types@8.17.0': + resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} + engines: {node: '>=14.18'} + + '@sentry/utils@8.17.0': + resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} + engines: {node: '>=14.18'} + + '@sigstore/bundle@1.1.0': + resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/protobuf-specs@0.2.1': + resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/sign@1.0.0': + resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sigstore/tuf@1.0.3': + resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + + '@syncpoint/wkx@0.5.2': + resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} + + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@tufjs/canonical-json@1.0.0': + resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@tufjs/models@1.0.4': + resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + '@types/async@3.2.24': + resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} + + '@types/chai-subset@1.3.5': + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + + '@types/chai@4.3.16': + resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + + '@types/connect@3.4.36': + resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} + + '@types/mysql@2.15.22': + resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@13.13.52': + resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} + + '@types/node@15.14.9': + resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} + + '@types/node@18.11.11': + resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} + + '@types/pg-pool@2.0.4': + resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + + '@types/pg@8.6.1': + resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + + '@types/semver-utils@1.1.3': + resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + + '@types/strip-bom@3.0.0': + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + + '@types/strip-json-comments@0.0.30': + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + + '@types/triple-beam@1.3.5': + resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + + '@types/webidl-conversions@7.0.3': + resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} + + '@types/whatwg-url@11.0.5': + resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + + '@types/ws@8.2.3': + resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + + '@vitest/expect@0.34.6': + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + + '@vitest/runner@0.34.6': + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + + '@vitest/snapshot@0.34.6': + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + + '@vitest/spy@0.34.6': + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + + '@vitest/utils@0.34.6': + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + abstract-logging@2.0.1: + resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + + acorn-import-assertions@1.9.0: + resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} + peerDependencies: + acorn: ^8 + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} + engines: {node: '>=0.4.0'} + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + aproba@2.0.0: + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} + + are-we-there-yet@3.0.1: + resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + async-mutex@0.5.0: + resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + + async@3.2.5: + resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + + atomic-sleep@1.0.0: + resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} + engines: {node: '>=8.0.0'} + + avvio@8.3.2: + resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-ajv-errors@1.2.0: + resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} + engines: {node: '>= 12.13.0'} + peerDependencies: + ajv: 4.11.8 - 8 + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + boxen@7.1.1: + resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} + engines: {node: '>=14.16'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} + engines: {node: '>=16.20.1'} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@16.1.3: + resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + cacache@17.1.4: + resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + + camelcase@7.0.1: + resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} + engines: {node: '>=14.16'} + + chai@4.4.1: + resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} + engines: {node: '>=4'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cjs-module-lexer@1.3.1: + resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color-support@1.1.3: + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} + hasBin: true + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + + colorspace@1.1.4: + resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concurrently@8.2.2: + resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@6.0.0: + resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} + engines: {node: '>=12'} + + console-control-strings@1.1.0: + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + copyfiles@2.4.1: + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} + hasBin: true + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + delegates@1.0.0: + resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + + dot-prop@6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + dynamic-dedupe@0.3.0: + resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enabled@2.0.0: + resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + event-stream@3.3.4: + resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + exponential-backoff@3.1.1: + resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-content-type-parse@1.1.0: + resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + + fast-decode-uri-component@1.0.1: + resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stringify@5.16.1: + resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} + + fast-memoize@2.5.2: + resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + + fast-querystring@1.1.2: + resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + + fast-redact@3.5.0: + resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} + engines: {node: '>=6'} + + fast-uri@2.4.0: + resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} + + fastify-plugin@4.5.1: + resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + + fastify@4.23.2: + resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-my-way@7.7.0: + resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} + engines: {node: '>=14'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-yarn-workspace-root2@1.2.16: + resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + + fn.name@1.1.0: + resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} + engines: {node: '>=14'} + + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fp-and-or@0.1.4: + resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} + engines: {node: '>=10'} + + from@0.1.7: + resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gauge@4.0.4: + resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + global-dirs@3.0.1: + resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} + engines: {node: '>=10'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-unicode@2.0.1: + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} + + has-yarn@3.0.0: + resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hosted-git-info@5.2.1: + resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + hosted-git-info@6.1.1: + resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-in-the-middle@1.7.1: + resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} + + import-in-the-middle@1.9.0: + resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + + ini@4.1.3: + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@9.3.5: + resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} + engines: {node: '>=18'} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + ipaddr.js@2.2.0: + resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + engines: {node: '>= 10'} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-ci@3.0.1: + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} + hasBin: true + + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-installed-globally@0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-lambda@1.0.1: + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} + + is-npm@6.0.0: + resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-yarn-global@0.4.1: + resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} + engines: {node: '>=12'} + + isarray@0.0.1: + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + ix@5.0.0: + resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@3.0.2: + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + json-parse-helpfulerror@1.0.3: + resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} + + json-schema-ref-resolver@1.0.1: + resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonlines@0.1.1: + resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + kuler@2.0.0: + resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} + + latest-version@7.0.0: + resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} + engines: {node: '>=14.16'} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + light-my-request@5.13.0: + resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + + load-yaml-file@0.2.0: + resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} + engines: {node: '>=6'} + + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + logform@2.6.1: + resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} + engines: {node: '>= 12.0.0'} + + lossless-json@2.0.11: + resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + make-fetch-happen@10.2.1: + resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + make-fetch-happen@11.1.1: + resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + map-stream@0.1.0: + resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} + + memory-pager@1.5.0: + resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.7: + resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} + engines: {node: '>=8.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-fetch@2.1.2: + resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + minipass-fetch@3.0.5: + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-json-stream@1.0.1: + resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mnemonist@0.39.5: + resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} + + module-details-from-path@1.0.3: + resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} + + mongodb-connection-string-url@3.0.1: + resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + + mongodb@6.8.0: + resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} + engines: {node: '>=16.20.1'} + peerDependencies: + '@aws-sdk/credential-providers': ^3.188.0 + '@mongodb-js/zstd': ^1.1.0 + gcp-metadata: ^5.2.0 + kerberos: ^2.0.1 + mongodb-client-encryption: '>=6.0.0 <7' + snappy: ^7.2.2 + socks: ^2.7.1 + peerDependenciesMeta: + '@aws-sdk/credential-providers': + optional: true + '@mongodb-js/zstd': + optional: true + gcp-metadata: + optional: true + kerberos: + optional: true + mongodb-client-encryption: + optional: true + snappy: + optional: true + socks: + optional: true + + moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nearley@2.20.1: + resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} + hasBin: true + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-gyp@9.4.1: + resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} + engines: {node: ^12.13 || ^14.13 || >=16} + hasBin: true + + nodemon@3.1.4: + resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} + engines: {node: '>=10'} + hasBin: true + + noms@0.0.0: + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + + nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + normalize-package-data@5.0.0: + resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + + npm-bundled@3.0.1: + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-check-updates@16.14.20: + resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} + engines: {node: '>=14.14'} + hasBin: true + + npm-install-checks@6.3.0: + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-normalize-package-bin@3.0.1: + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-package-arg@10.1.0: + resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-packlist@7.0.4: + resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-pick-manifest@8.0.2: + resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npm-registry-fetch@14.0.5: + resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + npmlog@6.0.2: + resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + obliterator@2.0.4: + resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} + + on-exit-leak-free@2.1.2: + resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} + engines: {node: '>=14.0.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + one-time@1.0.0: + resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + opentelemetry-instrumentation-fetch-node@1.2.3: + resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} + engines: {node: '>18.0.0'} + peerDependencies: + '@opentelemetry/api': ^1.6.0 + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + + pacote@15.2.0: + resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + parse-github-url@1.0.3: + resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} + engines: {node: '>= 0.10'} + hasBin: true + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + pause-stream@0.0.11: + resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-protocol@1.6.1: + resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pgsql-ast-parser@11.2.0: + resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} + + pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: + resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} + name: pgwire + version: 0.7.0 + engines: {node: '>=14.18.0'} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pino-abstract-transport@1.2.0: + resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + + pino-std-serializers@6.2.2: + resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + + pino@8.21.0: + resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} + hasBin: true + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-types@1.1.3: + resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} + engines: {node: ^10 || ^12 || >=14} + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + preferred-pm@3.1.4: + resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} + engines: {node: '>=10'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + proc-log@3.0.0: + resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process-warning@2.3.2: + resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + + process-warning@3.0.0: + resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + + prompts-ncu@3.0.0: + resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} + engines: {node: '>= 14'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + ps-tree@1.2.0: + resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} + engines: {node: '>= 0.10'} + hasBin: true + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@3.1.0: + resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + engines: {node: '>=12.20'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-format-unescaped@4.0.4: + resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + + randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + + rc-config-loader@4.1.3: + resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + read-package-json-fast@3.0.2: + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + read-package-json@6.0.4: + resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readable-stream@4.5.2: + resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + real-require@0.2.0: + resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} + engines: {node: '>= 12.13.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + remote-git-tags@3.0.0: + resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} + engines: {node: '>=8'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-in-the-middle@7.3.0: + resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} + engines: {node: '>=8.6.0'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + + ret@0.2.2: + resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} + engines: {node: '>=4'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@5.0.9: + resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} + engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} + hasBin: true + + rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rsocket-core@1.0.0-alpha.3: + resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} + + rsocket-websocket-client@1.0.0-alpha.3: + resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex2@2.0.0: + resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + + safe-stable-stringify@2.4.3: + resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} + engines: {node: '>=10'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + semver-diff@4.0.0: + resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} + engines: {node: '>=12'} + + semver-utils@1.1.4: + resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} + + semver@7.6.2: + resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} + engines: {node: '>=10'} + hasBin: true + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sigstore@1.9.0: + resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + simple-update-notifier@2.0.0: + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@7.0.0: + resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} + engines: {node: '>= 10'} + + socks@2.8.3: + resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + + sonic-boom@3.8.1: + resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sparse-bitfield@3.0.3: + resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + + spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + + spawn-please@2.0.2: + resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} + engines: {node: '>=14'} + + spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.18: + resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + split@0.3.3: + resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssri@10.0.6: + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ssri@9.0.1: + resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + stream-combiner@0.0.4: + resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@0.10.31: + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@5.0.1: + resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} + engines: {node: '>=14.16'} + + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tar@6.2.1: + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + text-hex@1.0.0: + resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} + + thread-stream@2.7.0: + resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + + tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toad-cache@3.7.0: + resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} + engines: {node: '>=12'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + + tr46@4.1.1: + resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} + engines: {node: '>=14'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + triple-beam@1.4.1: + resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} + engines: {node: '>= 14.0.0'} + + ts-codec@1.2.2: + resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} + + ts-node-dev@2.0.0: + resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} + engines: {node: '>=0.8.0'} + hasBin: true + peerDependencies: + node-notifier: '*' + typescript: '*' + peerDependenciesMeta: + node-notifier: + optional: true + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsc-watch@6.2.0: + resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} + engines: {node: '>=12.12.0'} + hasBin: true + peerDependencies: + typescript: '*' + + tsconfck@3.1.1: + resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + + tuf-js@1.1.7: + resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.5.3: + resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + + unique-filename@2.0.1: + resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-filename@3.0.0: + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-slug@3.0.0: + resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + + unique-slug@4.0.0: + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + untildify@4.0.0: + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} + + update-notifier@6.0.2: + resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} + engines: {node: '>=14.16'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validate-npm-package-name@5.0.1: + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@0.34.6: + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + + vite-tsconfig-paths@4.3.2: + resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@13.0.0: + resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} + engines: {node: '>=16'} + + which-pm@2.2.0: + resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} + engines: {node: '>=8.15'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wide-align@1.1.5: + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + + widest-line@4.0.1: + resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} + engines: {node: '>=12'} + + winston-transport@4.7.1: + resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} + engines: {node: '>= 12.0.0'} + + winston@3.13.1: + resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} + engines: {node: '>= 12.0.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + zod@3.23.8: + resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + +snapshots: + + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 picocolors: 1.0.1 - dev: false - /@babel/helper-validator-identifier@7.24.7: - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} - dev: false + '@babel/helper-validator-identifier@7.24.7': {} - /@babel/highlight@7.24.7: - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.1 - dev: false - /@babel/runtime@7.24.8: - resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} - engines: {node: '>=6.9.0'} + '@babel/runtime@7.24.8': dependencies: regenerator-runtime: 0.14.1 - dev: true - /@changesets/apply-release-plan@7.0.4: - resolution: {integrity: sha512-HLFwhKWayKinWAul0Vj+76jVx1Pc2v55MGPVjZ924Y/ROeSsBMFutv9heHmCUj48lJyRfOTJG5+ar+29FUky/A==} + '@changesets/apply-release-plan@7.0.4': dependencies: '@babel/runtime': 7.24.8 '@changesets/config': 3.0.2 @@ -459,10 +3650,8 @@ packages: prettier: 2.8.8 resolve-from: 5.0.0 semver: 7.6.2 - dev: true - /@changesets/assemble-release-plan@6.0.3: - resolution: {integrity: sha512-bLNh9/Lgl1VwkjWZTq8JmRqH+hj7/Yzfz0jsQ/zJJ+FTmVqmqPj3szeKOri8O/hEM8JmHW019vh2gTO9iq5Cuw==} + '@changesets/assemble-release-plan@6.0.3': dependencies: '@babel/runtime': 7.24.8 '@changesets/errors': 0.2.0 @@ -471,17 +3660,12 @@ packages: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 semver: 7.6.2 - dev: true - /@changesets/changelog-git@0.2.0: - resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + '@changesets/changelog-git@0.2.0': dependencies: '@changesets/types': 6.0.0 - dev: true - /@changesets/cli@2.27.7: - resolution: {integrity: sha512-6lr8JltiiXPIjDeYg4iM2MeePP6VN/JkmqBsVA5XRiy01hGS3y629LtSDvKcycj/w/5Eur1rEwby/MjcYS+e2A==} - hasBin: true + '@changesets/cli@2.27.7': dependencies: '@babel/runtime': 7.24.8 '@changesets/apply-release-plan': 7.0.4 @@ -515,10 +3699,8 @@ packages: semver: 7.6.2 spawndamnit: 2.0.0 term-size: 2.2.1 - dev: true - /@changesets/config@3.0.2: - resolution: {integrity: sha512-cdEhS4t8woKCX2M8AotcV2BOWnBp09sqICxKapgLHf9m5KdENpWjyrFNMjkLqGJtUys9U+w93OxWT0czorVDfw==} + '@changesets/config@3.0.2': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.1 @@ -527,26 +3709,20 @@ packages: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.7 - dev: true - /@changesets/errors@0.2.0: - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + '@changesets/errors@0.2.0': dependencies: extendable-error: 0.1.7 - dev: true - /@changesets/get-dependents-graph@2.1.1: - resolution: {integrity: sha512-LRFjjvigBSzfnPU2n/AhFsuWR5DK++1x47aq6qZ8dzYsPtS/I5mNhIGAS68IAxh1xjO9BTtz55FwefhANZ+FCA==} + '@changesets/get-dependents-graph@2.1.1': dependencies: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 semver: 7.6.2 - dev: true - /@changesets/get-release-plan@4.0.3: - resolution: {integrity: sha512-6PLgvOIwTSdJPTtpdcr3sLtGatT+Jr22+cQwEBJBy6wP0rjB4yJ9lv583J9fVpn1bfQlBkDa8JxbS2g/n9lIyA==} + '@changesets/get-release-plan@4.0.3': dependencies: '@babel/runtime': 7.24.8 '@changesets/assemble-release-plan': 6.0.3 @@ -555,14 +3731,10 @@ packages: '@changesets/read': 0.6.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/get-version-range-type@0.4.0: - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - dev: true + '@changesets/get-version-range-type@0.4.0': {} - /@changesets/git@3.0.0: - resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + '@changesets/git@3.0.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/errors': 0.2.0 @@ -571,33 +3743,25 @@ packages: is-subdir: 1.2.0 micromatch: 4.0.7 spawndamnit: 2.0.0 - dev: true - /@changesets/logger@0.1.0: - resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + '@changesets/logger@0.1.0': dependencies: chalk: 2.4.2 - dev: true - /@changesets/parse@0.4.0: - resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + '@changesets/parse@0.4.0': dependencies: '@changesets/types': 6.0.0 js-yaml: 3.14.1 - dev: true - /@changesets/pre@2.0.0: - resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + '@changesets/pre@2.0.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/errors': 0.2.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - dev: true - /@changesets/read@0.6.0: - resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + '@changesets/read@0.6.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/git': 3.0.0 @@ -607,359 +3771,168 @@ packages: chalk: 2.4.2 fs-extra: 7.0.1 p-filter: 2.1.0 - dev: true - /@changesets/should-skip-package@0.1.0: - resolution: {integrity: sha512-FxG6Mhjw7yFStlSM7Z0Gmg3RiyQ98d/9VpQAZ3Fzr59dCOM9G6ZdYbjiSAt0XtFr9JR5U2tBaJWPjrkGGc618g==} + '@changesets/should-skip-package@0.1.0': dependencies: '@babel/runtime': 7.24.8 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/types@4.1.0: - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - dev: true + '@changesets/types@4.1.0': {} - /@changesets/types@6.0.0: - resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - dev: true + '@changesets/types@6.0.0': {} - /@changesets/write@0.3.1: - resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + '@changesets/write@0.3.1': dependencies: '@babel/runtime': 7.24.8 '@changesets/types': 6.0.0 fs-extra: 7.0.1 human-id: 1.0.2 prettier: 2.8.8 - dev: true - /@colors/colors@1.5.0: - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - requiresBuild: true - dev: true + '@colors/colors@1.5.0': optional: true - /@colors/colors@1.6.0: - resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} - engines: {node: '>=0.1.90'} - dev: false + '@colors/colors@1.6.0': {} - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} + '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - dev: true - /@dabh/diagnostics@2.0.3: - resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@dabh/diagnostics@2.0.3': dependencies: colorspace: 1.1.4 enabled: 2.0.0 kuler: 2.0.0 - dev: false - /@esbuild/aix-ppc64@0.21.5: - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - requiresBuild: true - dev: true + '@esbuild/aix-ppc64@0.21.5': optional: true - /@esbuild/android-arm64@0.21.5: - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm64@0.21.5': optional: true - /@esbuild/android-arm@0.21.5: - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-arm@0.21.5': optional: true - /@esbuild/android-x64@0.21.5: - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - requiresBuild: true - dev: true + '@esbuild/android-x64@0.21.5': optional: true - /@esbuild/darwin-arm64@0.21.5: - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-arm64@0.21.5': optional: true - /@esbuild/darwin-x64@0.21.5: - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@esbuild/darwin-x64@0.21.5': optional: true - /@esbuild/freebsd-arm64@0.21.5: - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-arm64@0.21.5': optional: true - /@esbuild/freebsd-x64@0.21.5: - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true + '@esbuild/freebsd-x64@0.21.5': optional: true - /@esbuild/linux-arm64@0.21.5: - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm64@0.21.5': optional: true - /@esbuild/linux-arm@0.21.5: - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-arm@0.21.5': optional: true - /@esbuild/linux-ia32@0.21.5: - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ia32@0.21.5': optional: true - /@esbuild/linux-loong64@0.21.5: - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-loong64@0.21.5': optional: true - /@esbuild/linux-mips64el@0.21.5: - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-mips64el@0.21.5': optional: true - /@esbuild/linux-ppc64@0.21.5: - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-ppc64@0.21.5': optional: true - /@esbuild/linux-riscv64@0.21.5: - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-riscv64@0.21.5': optional: true - /@esbuild/linux-s390x@0.21.5: - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-s390x@0.21.5': optional: true - /@esbuild/linux-x64@0.21.5: - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@esbuild/linux-x64@0.21.5': optional: true - /@esbuild/netbsd-x64@0.21.5: - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - requiresBuild: true - dev: true + '@esbuild/netbsd-x64@0.21.5': optional: true - /@esbuild/openbsd-x64@0.21.5: - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - requiresBuild: true - dev: true + '@esbuild/openbsd-x64@0.21.5': optional: true - /@esbuild/sunos-x64@0.21.5: - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - requiresBuild: true - dev: true + '@esbuild/sunos-x64@0.21.5': optional: true - /@esbuild/win32-arm64@0.21.5: - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-arm64@0.21.5': optional: true - /@esbuild/win32-ia32@0.21.5: - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-ia32@0.21.5': optional: true - /@esbuild/win32-x64@0.21.5: - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@esbuild/win32-x64@0.21.5': optional: true - /@fastify/ajv-compiler@3.6.0: - resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} + '@fastify/ajv-compiler@3.6.0': dependencies: ajv: 8.16.0 ajv-formats: 2.1.1(ajv@8.16.0) fast-uri: 2.4.0 - /@fastify/cors@8.4.1: - resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} + '@fastify/cors@8.4.1': dependencies: fastify-plugin: 4.5.1 mnemonist: 0.39.5 - dev: false - /@fastify/error@3.4.1: - resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} + '@fastify/error@3.4.1': {} - /@fastify/fast-json-stringify-compiler@4.3.0: - resolution: {integrity: sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==} + '@fastify/fast-json-stringify-compiler@4.3.0': dependencies: fast-json-stringify: 5.16.1 - /@fastify/merge-json-schemas@0.1.1: - resolution: {integrity: sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==} + '@fastify/merge-json-schemas@0.1.1': dependencies: fast-deep-equal: 3.1.3 - /@gar/promisify@1.1.3: - resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - dev: true + '@gar/promisify@1.1.3': {} - /@humanwhocodes/momoa@2.0.4: - resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} - engines: {node: '>=10.10.0'} - dev: false + '@humanwhocodes/momoa@2.0.4': {} - /@inquirer/figures@1.0.3: - resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} - engines: {node: '>=18'} - dev: true + '@inquirer/figures@1.0.3': {} - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 + string-width-cjs: string-width@4.2.3 strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 + strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true + wrap-ansi-cjs: wrap-ansi@7.0.0 - /@jest/schemas@29.6.3: - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - dev: true - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - dev: true + '@jridgewell/resolve-uri@3.1.2': {} - /@jridgewell/sourcemap-codec@1.5.0: - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - dev: true + '@jridgewell/sourcemap-codec@1.5.0': {} - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /@js-sdsl/ordered-set@4.4.2: - resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} - dev: false + '@js-sdsl/ordered-set@4.4.2': {} - /@manypkg/find-root@1.1.0: - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.24.8 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 - dev: true - /@manypkg/get-packages@1.1.3: - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@manypkg/get-packages@1.1.3': dependencies: '@babel/runtime': 7.24.8 '@changesets/types': 4.1.0 @@ -967,53 +3940,33 @@ packages: fs-extra: 8.1.0 globby: 11.1.0 read-yaml-file: 1.1.0 - dev: true - /@mongodb-js/saslprep@1.1.7: - resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==} + '@mongodb-js/saslprep@1.1.7': dependencies: sparse-bitfield: 3.0.3 - dev: false - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - dev: true + '@nodelib/fs.stat@2.0.5': {} - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - dev: true - /@npmcli/fs@2.1.2: - resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + '@npmcli/fs@2.1.2': dependencies: '@gar/promisify': 1.1.3 semver: 7.6.2 - dev: true - /@npmcli/fs@3.1.1: - resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/fs@3.1.1': dependencies: semver: 7.6.2 - dev: true - /@npmcli/git@4.1.0: - resolution: {integrity: sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/git@4.1.0': dependencies: '@npmcli/promise-spawn': 6.0.2 lru-cache: 7.18.3 @@ -1025,41 +3978,24 @@ packages: which: 3.0.1 transitivePeerDependencies: - bluebird - dev: true - /@npmcli/installed-package-contents@2.1.0: - resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + '@npmcli/installed-package-contents@2.1.0': dependencies: npm-bundled: 3.0.1 npm-normalize-package-bin: 3.0.1 - dev: true - /@npmcli/move-file@2.0.1: - resolution: {integrity: sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This functionality has been moved to @npmcli/fs + '@npmcli/move-file@2.0.1': dependencies: mkdirp: 1.0.4 rimraf: 3.0.2 - dev: true - /@npmcli/node-gyp@3.0.0: - resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@npmcli/node-gyp@3.0.0': {} - /@npmcli/promise-spawn@6.0.2: - resolution: {integrity: sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/promise-spawn@6.0.2': dependencies: which: 3.0.1 - dev: true - /@npmcli/run-script@6.0.2: - resolution: {integrity: sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/run-script@6.0.2': dependencies: '@npmcli/node-gyp': 3.0.0 '@npmcli/promise-spawn': 6.0.2 @@ -1069,121 +4005,61 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /@opentelemetry/api-logs@0.51.1: - resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} - engines: {node: '>=14'} + '@opentelemetry/api-logs@0.51.1': dependencies: '@opentelemetry/api': 1.8.0 - dev: false - /@opentelemetry/api-logs@0.52.1: - resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} - engines: {node: '>=14'} + '@opentelemetry/api-logs@0.52.1': dependencies: '@opentelemetry/api': 1.6.0 - dev: false - /@opentelemetry/api@1.6.0: - resolution: {integrity: sha512-OWlrQAnWn9577PhVgqjUvMr1pg57Bc4jv0iL4w0PRuOSRvq67rvHW9Ie/dZVMvCzhSCB+UxhcY/PmCmFj33Q+g==} - engines: {node: '>=8.0.0'} - dev: false + '@opentelemetry/api@1.6.0': {} - /@opentelemetry/api@1.8.0: - resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} - engines: {node: '>=8.0.0'} - dev: false + '@opentelemetry/api@1.8.0': {} - /@opentelemetry/api@1.9.0: - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - dev: false + '@opentelemetry/api@1.9.0': {} - /@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - dev: false - /@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.7.0' + '@opentelemetry/core@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.17.0 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/core@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-oFXvif9iksHUxrzG3P8ohMLt7xSrl+oDMqxD/3XXndU761RFAKSbRDpfrQs25U5D+A2aMV3qk+4kfUWdJhZ77g==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) @@ -1191,37 +4067,22 @@ packages: '@opentelemetry/otlp-transformer': 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-tJeZVmzzeG98BMPssrnUYZ7AdMtZEYqgOL44z/bF4YWqGePQoelmxuTn8Do0tIyBURqr0Whi/7P5/XxWMK1zTw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-prometheus@0.43.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/sdk-metrics': 1.17.0(@opentelemetry/api@1.6.0) - dev: false - /@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-c8TrTlLm9JJRIHW6MtFv6ESoZRgXBXD/YrTRYylWiyYBOVbYHo1c5Qaw/j/thXDhkmYOYAn4LAhJZpLl5gBFEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-prometheus@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1230,13 +4091,8 @@ packages: '@types/connect': 3.4.36 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1244,13 +4100,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1258,25 +4109,15 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1284,13 +4125,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1299,13 +4135,8 @@ packages: semver: 7.6.2 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1313,13 +4144,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1327,13 +4153,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1341,13 +4162,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -1355,13 +4171,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1369,13 +4180,8 @@ packages: '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1383,26 +4189,16 @@ packages: '@types/mysql': 2.15.22 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1412,13 +4208,8 @@ packages: '@types/pg-pool': 2.0.4 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -1426,14 +4217,8 @@ packages: '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} - engines: {node: '>=14'} - requiresBuild: true - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@types/shimmer': 1.2.0 @@ -1443,14 +4228,9 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false optional: true - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/api-logs': 0.52.1 @@ -1461,13 +4241,8 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.52.1 @@ -1478,13 +4253,8 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.52.1 @@ -1495,23 +4265,13 @@ packages: shimmer: 1.2.1 transitivePeerDependencies: - supports-color - dev: false - /@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.0.0 + '@opentelemetry/otlp-exporter-base@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/otlp-transformer@0.51.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 @@ -1520,389 +4280,195 @@ packages: '@opentelemetry/sdk-logs': 0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/redis-common@0.36.2: - resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/redis-common@0.36.2': {} - /@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.7.0' + '@opentelemetry/resources@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.17.0 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.4.0 <1.9.0' - '@opentelemetry/api-logs': '>=0.39.1' + '@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/api-logs': 0.51.1 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) - dev: false - /@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-HlWM27yGmYuwCoVRe3yg2PqKnIsq0kEF0HQgvkeDWz2NYkq9fFaSspR6kvjxUTbghAlZrabiqbgyKoYpYaXS3w==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.7.0' + '@opentelemetry/sdk-metrics@1.17.0(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.17.0(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.6.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.9.0' + '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.9.0) lodash.merge: 4.6.2 - dev: false - /@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.9.0' + '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - dev: false - /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0): - resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0)': dependencies: '@opentelemetry/api': 1.6.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.6.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0): - resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/semantic-conventions@1.17.0: - resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.17.0': {} - /@opentelemetry/semantic-conventions@1.24.1: - resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.24.1': {} - /@opentelemetry/semantic-conventions@1.25.1: - resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} - engines: {node: '>=14'} - dev: false + '@opentelemetry/semantic-conventions@1.25.1': {} - /@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.1.0 + '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - dev: false - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: true + '@pkgjs/parseargs@0.11.0': optional: true - /@pnpm/config.env-replace@1.1.0: - resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} - engines: {node: '>=12.22.0'} - dev: true + '@pnpm/config.env-replace@1.1.0': {} - /@pnpm/network.ca-file@1.0.2: - resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} - engines: {node: '>=12.22.0'} + '@pnpm/network.ca-file@1.0.2': dependencies: graceful-fs: 4.2.10 - dev: true - - /@pnpm/npm-conf@2.2.2: - resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} - engines: {node: '>=12'} + + '@pnpm/npm-conf@2.2.2': dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - dev: true - /@prisma/instrumentation@5.16.1: - resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} + '@prisma/instrumentation@5.16.1': dependencies: '@opentelemetry/api': 1.8.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.8.0) transitivePeerDependencies: - supports-color - dev: false - /@rollup/rollup-android-arm-eabi@4.18.1: - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} - cpu: [arm] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true - /@rollup/rollup-android-arm64@4.18.1: - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} - cpu: [arm64] - os: [android] - requiresBuild: true - dev: true + '@rollup/rollup-android-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-arm64@4.18.1: - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} - cpu: [arm64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-arm64@4.18.1': optional: true - /@rollup/rollup-darwin-x64@4.18.1: - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} - cpu: [x64] - os: [darwin] - requiresBuild: true - dev: true + '@rollup/rollup-darwin-x64@4.18.1': optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.18.1: - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm-musleabihf@4.18.1: - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm-musleabihf@4.18.1': optional: true - /@rollup/rollup-linux-arm64-gnu@4.18.1: - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-arm64-musl@4.18.1: - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} - cpu: [arm64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-arm64-musl@4.18.1': optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.18.1: - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': optional: true - /@rollup/rollup-linux-riscv64-gnu@4.18.1: - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} - cpu: [riscv64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-riscv64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-s390x-gnu@4.18.1: - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} - cpu: [s390x] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-s390x-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-gnu@4.18.1: - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-gnu@4.18.1': optional: true - /@rollup/rollup-linux-x64-musl@4.18.1: - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} - cpu: [x64] - os: [linux] - requiresBuild: true - dev: true + '@rollup/rollup-linux-x64-musl@4.18.1': optional: true - /@rollup/rollup-win32-arm64-msvc@4.18.1: - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} - cpu: [arm64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-arm64-msvc@4.18.1': optional: true - /@rollup/rollup-win32-ia32-msvc@4.18.1: - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-ia32-msvc@4.18.1': optional: true - /@rollup/rollup-win32-x64-msvc@4.18.1: - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} - cpu: [x64] - os: [win32] - requiresBuild: true - dev: true + '@rollup/rollup-win32-x64-msvc@4.18.1': optional: true - /@sentry/core@8.17.0: - resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} - engines: {node: '>=14.18'} + '@sentry/core@8.17.0': dependencies: '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 - dev: false - /@sentry/node@8.17.0: - resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} - engines: {node: '>=14.18'} + '@sentry/node@8.17.0': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -1935,17 +4501,8 @@ packages: opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - dev: false - /@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1): - resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} - engines: {node: '>=14.18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/core': ^1.25.1 - '@opentelemetry/instrumentation': ^0.52.1 - '@opentelemetry/sdk-trace-base': ^1.25.1 - '@opentelemetry/semantic-conventions': ^1.25.1 + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.25.1)(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) @@ -1955,477 +4512,276 @@ packages: '@sentry/core': 8.17.0 '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 - dev: false - /@sentry/types@8.17.0: - resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} - engines: {node: '>=14.18'} + '@sentry/types@8.17.0': {} - /@sentry/utils@8.17.0: - resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} - engines: {node: '>=14.18'} + '@sentry/utils@8.17.0': dependencies: '@sentry/types': 8.17.0 - dev: false - /@sigstore/bundle@1.1.0: - resolution: {integrity: sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/bundle@1.1.0': dependencies: '@sigstore/protobuf-specs': 0.2.1 - dev: true - /@sigstore/protobuf-specs@0.2.1: - resolution: {integrity: sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@sigstore/protobuf-specs@0.2.1': {} - /@sigstore/sign@1.0.0: - resolution: {integrity: sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/sign@1.0.0': dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /@sigstore/tuf@1.0.3: - resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sigstore/tuf@1.0.3': dependencies: '@sigstore/protobuf-specs': 0.2.1 tuf-js: 1.1.7 transitivePeerDependencies: - supports-color - dev: true - /@sinclair/typebox@0.27.8: - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - dev: true + '@sinclair/typebox@0.27.8': {} - /@sindresorhus/is@5.6.0: - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} - dev: true + '@sindresorhus/is@5.6.0': {} - /@syncpoint/wkx@0.5.2: - resolution: {integrity: sha512-o3gaGp38Gg31Pl2jULfLmOpSEyg2gcgrccCZ5kdEjKwhjV+lC38s0p2fLpDijpoL6JsCYSmgNSJ6JJBuN67YDQ==} + '@syncpoint/wkx@0.5.2': dependencies: '@types/node': 15.14.9 - dev: false - /@szmarczak/http-timer@5.0.1: - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} + '@szmarczak/http-timer@5.0.1': dependencies: defer-to-connect: 2.0.1 - dev: true - /@tootallnate/once@2.0.0: - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - dev: true + '@tootallnate/once@2.0.0': {} - /@tsconfig/node10@1.0.11: - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - dev: true + '@tsconfig/node10@1.0.11': {} - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true + '@tsconfig/node12@1.0.11': {} - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true + '@tsconfig/node14@1.0.3': {} - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true + '@tsconfig/node16@1.0.4': {} - /@tufjs/canonical-json@1.0.0: - resolution: {integrity: sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + '@tufjs/canonical-json@1.0.0': {} - /@tufjs/models@1.0.4: - resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@tufjs/models@1.0.4': dependencies: '@tufjs/canonical-json': 1.0.0 minimatch: 9.0.5 - dev: true - /@types/async@3.2.24: - resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - dev: true + '@types/async@3.2.24': {} - /@types/chai-subset@1.3.5: - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + '@types/chai-subset@1.3.5': dependencies: '@types/chai': 4.3.16 - dev: true - /@types/chai@4.3.16: - resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} - dev: true + '@types/chai@4.3.16': {} - /@types/connect@3.4.36: - resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} + '@types/connect@3.4.36': dependencies: '@types/node': 18.11.11 - dev: false - /@types/estree@1.0.5: - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: true + '@types/estree@1.0.5': {} - /@types/http-cache-semantics@4.0.4: - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - dev: true + '@types/http-cache-semantics@4.0.4': {} - /@types/lodash@4.17.6: - resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} - dev: true + '@types/lodash@4.17.6': {} - /@types/mysql@2.15.22: - resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + '@types/mysql@2.15.22': dependencies: '@types/node': 18.11.11 - dev: false - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: true + '@types/node@12.20.55': {} - /@types/node@13.13.52: - resolution: {integrity: sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==} - dev: false + '@types/node@13.13.52': {} - /@types/node@15.14.9: - resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - dev: false + '@types/node@15.14.9': {} - /@types/node@18.11.11: - resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} + '@types/node@18.11.11': {} - /@types/pg-pool@2.0.4: - resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + '@types/pg-pool@2.0.4': dependencies: '@types/pg': 8.6.1 - dev: false - /@types/pg@8.6.1: - resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} + '@types/pg@8.6.1': dependencies: '@types/node': 18.11.11 pg-protocol: 1.6.1 pg-types: 2.2.0 - dev: false - /@types/semver-utils@1.1.3: - resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} - dev: true + '@types/semver-utils@1.1.3': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true + '@types/semver@7.5.8': {} - /@types/shimmer@1.2.0: - resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} - dev: false + '@types/shimmer@1.2.0': {} - /@types/strip-bom@3.0.0: - resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} - dev: true + '@types/strip-bom@3.0.0': {} - /@types/strip-json-comments@0.0.30: - resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} - dev: true + '@types/strip-json-comments@0.0.30': {} - /@types/triple-beam@1.3.5: - resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} - dev: false + '@types/triple-beam@1.3.5': {} - /@types/uuid@9.0.8: - resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - dev: true + '@types/uuid@9.0.8': {} - /@types/webidl-conversions@7.0.3: - resolution: {integrity: sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==} - dev: false + '@types/webidl-conversions@7.0.3': {} - /@types/whatwg-url@11.0.5: - resolution: {integrity: sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==} + '@types/whatwg-url@11.0.5': dependencies: '@types/webidl-conversions': 7.0.3 - dev: false - /@types/ws@8.2.3: - resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + '@types/ws@8.2.3': dependencies: '@types/node': 18.11.11 - dev: true - /@vitest/expect@0.34.6: - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + '@vitest/expect@0.34.6': dependencies: '@vitest/spy': 0.34.6 '@vitest/utils': 0.34.6 chai: 4.4.1 - dev: true - /@vitest/runner@0.34.6: - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/runner@0.34.6': dependencies: '@vitest/utils': 0.34.6 p-limit: 4.0.0 pathe: 1.1.2 - dev: true - /@vitest/snapshot@0.34.6: - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/snapshot@0.34.6': dependencies: magic-string: 0.30.10 pathe: 1.1.2 pretty-format: 29.7.0 - dev: true - /@vitest/spy@0.34.6: - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/spy@0.34.6': dependencies: tinyspy: 2.2.1 - dev: true - /@vitest/utils@0.34.6: - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/utils@0.34.6': dependencies: diff-sequences: 29.6.3 loupe: 2.3.7 pretty-format: 29.7.0 - dev: true - /abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - dev: true + abbrev@1.1.1: {} - /abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 - /abstract-logging@2.0.1: - resolution: {integrity: sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==} + abstract-logging@2.0.1: {} - /acorn-import-assertions@1.9.0(acorn@8.12.1): - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} - requiresBuild: true - peerDependencies: - acorn: ^8 + acorn-import-assertions@1.9.0(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: false optional: true - /acorn-import-attributes@1.9.5(acorn@8.12.1): - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: acorn: 8.12.1 - dev: false - /acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} - engines: {node: '>=0.4.0'} + acorn-walk@8.3.3: dependencies: acorn: 8.12.1 - dev: true - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true + acorn@8.12.1: {} - /agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} + agent-base@6.0.2: dependencies: debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /agentkeepalive@4.5.0: - resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} - engines: {node: '>= 8.0.0'} + agentkeepalive@4.5.0: dependencies: humanize-ms: 1.2.1 - dev: true - /aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - dev: true - /ajv-formats@2.1.1(ajv@8.16.0): - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true + ajv-formats@2.1.1(ajv@8.16.0): dependencies: ajv: 8.16.0 - /ajv-formats@3.0.1(ajv@8.16.0): - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true + ajv-formats@3.0.1(ajv@8.16.0): dependencies: ajv: 8.16.0 - /ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + ajv@8.16.0: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 uri-js: 4.4.1 - /ansi-align@3.0.1: - resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + ansi-align@3.0.1: dependencies: string-width: 4.2.3 - dev: true - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: true + ansi-colors@4.1.3: {} - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - dev: true - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: true + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - dev: true + ansi-styles@5.2.0: {} - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true + ansi-styles@6.2.1: {} - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true - /aproba@2.0.0: - resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} - dev: true + aproba@2.0.0: {} - /are-we-there-yet@3.0.1: - resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + are-we-there-yet@3.0.1: dependencies: delegates: 1.0.0 readable-stream: 3.6.2 - dev: true - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true + arg@4.1.3: {} - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true + argparse@2.0.1: {} - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - dev: true + array-union@2.1.0: {} - /assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - dev: true + assertion-error@1.1.0: {} - /async-mutex@0.5.0: - resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async-mutex@0.5.0: dependencies: tslib: 2.6.3 - dev: false - /async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + async@3.2.5: {} - /atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} + atomic-sleep@1.0.0: {} - /avvio@8.3.2: - resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + avvio@8.3.2: dependencies: '@fastify/error': 3.4.1 fastq: 1.17.1 - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: true + balanced-match@1.0.2: {} - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + base64-js@1.5.1: {} - /better-ajv-errors@1.2.0(ajv@8.16.0): - resolution: {integrity: sha512-UW+IsFycygIo7bclP9h5ugkNH8EjCSgqyFB/yQ4Hqqa1OEYDtb0uFIkYE0b6+CjkgJYVM5UKI/pJPxjYe9EZlA==} - engines: {node: '>= 12.13.0'} - peerDependencies: - ajv: 4.11.8 - 8 + better-ajv-errors@1.2.0(ajv@8.16.0): dependencies: '@babel/code-frame': 7.24.7 '@humanwhocodes/momoa': 2.0.4 @@ -2433,31 +4789,20 @@ packages: chalk: 4.1.2 jsonpointer: 5.0.1 leven: 3.1.0 - dev: false - /better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 - dev: true - /binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - dev: true + binary-extensions@2.3.0: {} - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /boxen@7.1.1: - resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} - engines: {node: '>=14.16'} + boxen@7.1.1: dependencies: ansi-align: 3.0.1 camelcase: 7.0.1 @@ -2467,57 +4812,37 @@ packages: type-fest: 2.19.0 widest-line: 4.0.1 wrap-ansi: 8.1.0 - dev: true - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - dev: true - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - dev: true - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - dev: true - /bson@6.8.0: - resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} - engines: {node: '>=16.20.1'} + bson@6.8.0: {} - /buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - dev: true + buffer-from@1.1.2: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - /cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - dev: true + cac@6.7.14: {} - /cacache@16.1.3: - resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + cacache@16.1.3: dependencies: '@npmcli/fs': 2.1.2 '@npmcli/move-file': 2.0.1 @@ -2539,11 +4864,8 @@ packages: unique-filename: 2.0.1 transitivePeerDependencies: - bluebird - dev: true - /cacache@17.1.4: - resolution: {integrity: sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + cacache@17.1.4: dependencies: '@npmcli/fs': 3.1.1 fs-minipass: 3.0.3 @@ -2557,16 +4879,10 @@ packages: ssri: 10.0.6 tar: 6.2.1 unique-filename: 3.0.0 - dev: true - /cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - dev: true + cacheable-lookup@7.0.0: {} - /cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} + cacheable-request@10.2.14: dependencies: '@types/http-cache-semantics': 4.0.4 get-stream: 6.0.1 @@ -2575,16 +4891,10 @@ packages: mimic-response: 4.0.0 normalize-url: 8.0.1 responselike: 3.0.0 - dev: true - /camelcase@7.0.1: - resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} - engines: {node: '>=14.16'} - dev: true + camelcase@7.0.1: {} - /chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} + chai@4.4.1: dependencies: assertion-error: 1.1.0 check-error: 1.0.3 @@ -2593,41 +4903,27 @@ packages: loupe: 2.3.7 pathval: 1.1.1 type-detect: 4.0.8 - dev: true - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - dev: true + chalk@5.3.0: {} - /chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: true + chardet@0.7.0: {} - /check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@1.0.3: dependencies: get-func-name: 2.0.2 - dev: true - /chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -2638,145 +4934,83 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - dev: true + chownr@2.0.0: {} - /ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - dev: true + ci-info@3.9.0: {} - /cjs-module-lexer@1.3.1: - resolution: {integrity: sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==} - dev: false + cjs-module-lexer@1.3.1: {} - /clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - dev: true + clean-stack@2.2.0: {} - /cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - dev: true + cli-boxes@3.0.0: {} - /cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 - dev: true - /cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - dev: true + cli-spinners@2.9.2: {} - /cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} + cli-table3@0.6.5: dependencies: string-width: 4.2.3 optionalDependencies: '@colors/colors': 1.5.0 - dev: true - /cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - dev: true + cli-width@4.1.0: {} - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@7.0.4: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - dev: true + clone@1.0.4: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - dev: false - /color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - dev: true + color-support@1.1.3: {} - /color@3.2.1: - resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + color@3.2.1: dependencies: color-convert: 1.9.3 color-string: 1.9.1 - dev: false - /colorspace@1.1.4: - resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==} + colorspace@1.1.4: dependencies: color: 3.2.1 text-hex: 1.0.0 - dev: false - /commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - dev: true + commander@10.0.1: {} - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - dev: false + commander@12.1.0: {} - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - dev: false + commander@2.20.3: {} - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: true + concat-map@0.0.1: {} - /concurrently@8.2.2: - resolution: {integrity: sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==} - engines: {node: ^14.13.0 || >=16.0.0} - hasBin: true + concurrently@8.2.2: dependencies: chalk: 4.1.2 date-fns: 2.30.0 @@ -2787,41 +5021,27 @@ packages: supports-color: 8.1.1 tree-kill: 1.2.2 yargs: 17.7.2 - dev: true - /confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - dev: true + confbox@0.1.7: {} - /config-chain@1.1.13: - resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + config-chain@1.1.13: dependencies: ini: 1.3.8 proto-list: 1.2.4 - dev: true - /configstore@6.0.0: - resolution: {integrity: sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==} - engines: {node: '>=12'} + configstore@6.0.0: dependencies: dot-prop: 6.0.1 graceful-fs: 4.2.11 unique-string: 3.0.0 write-file-atomic: 3.0.3 xdg-basedir: 5.1.0 - dev: true - /console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - dev: true + console-control-strings@1.1.0: {} - /cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} + cookie@0.6.0: {} - /copyfiles@2.4.1: - resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} - hasBin: true + copyfiles@2.4.1: dependencies: glob: 7.2.3 minimatch: 3.1.2 @@ -2830,204 +5050,110 @@ packages: through2: 2.0.5 untildify: 4.0.0 yargs: 16.2.0 - dev: true - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true + core-util-is@1.0.3: {} - /cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} + cors@2.8.5: dependencies: object-assign: 4.1.1 vary: 1.1.2 - dev: false - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true + create-require@1.1.1: {} - /cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + cross-spawn@5.1.0: dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 - dev: true - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true - /crypto-random-string@4.0.0: - resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} - engines: {node: '>=12'} + crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 - dev: true - /data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - dev: false + data-uri-to-buffer@4.0.1: {} - /date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} + date-fns@2.30.0: dependencies: '@babel/runtime': 7.24.8 - dev: true - /date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - dev: false + date-fns@3.6.0: {} - /debug@4.3.5(supports-color@5.5.0): - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.5(supports-color@5.5.0): dependencies: ms: 2.1.2 supports-color: 5.5.0 - /decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 - dev: true - /deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} + deep-eql@4.1.4: dependencies: type-detect: 4.0.8 - dev: true - /deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - dev: true + deep-extend@0.6.0: {} - /defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defaults@1.0.4: dependencies: clone: 1.0.4 - dev: true - /defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - dev: true + defer-to-connect@2.0.1: {} - /delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - dev: true + delegates@1.0.0: {} - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true + detect-indent@6.1.0: {} - /diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dev: true + diff-sequences@29.6.3: {} - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true + diff@4.0.2: {} - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - dev: true - /discontinuous-range@1.0.0: - resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} - dev: false + discontinuous-range@1.0.0: {} - /dot-prop@6.0.1: - resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} - engines: {node: '>=10'} + dot-prop@6.0.1: dependencies: is-obj: 2.0.0 - dev: true - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - dev: false + dotenv@16.4.5: {} - /duplexer@0.1.2: - resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} - dev: true + duplexer@0.1.2: {} - /dynamic-dedupe@0.3.0: - resolution: {integrity: sha512-ssuANeD+z97meYOqd50e04Ze5qp4bPqo8cCkI4TRjZkzAUgIDTrXV1R8QCdINpiI+hw14+rYazvTRdQrz0/rFQ==} + dynamic-dedupe@0.3.0: dependencies: xtend: 4.0.2 - dev: true - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true + emoji-regex@9.2.2: {} - /enabled@2.0.0: - resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} - dev: false + enabled@2.0.0: {} - /encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - requiresBuild: true + encoding@0.1.13: dependencies: iconv-lite: 0.6.3 - dev: true optional: true - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - dev: true - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: true + env-paths@2.2.1: {} - /err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - dev: true + err-code@2.0.3: {} - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - requiresBuild: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 '@esbuild/android-arm': 0.21.5 @@ -3052,30 +5178,16 @@ packages: '@esbuild/win32-arm64': 0.21.5 '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - dev: true - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - dev: true + escalade@3.1.2: {} - /escape-goat@4.0.0: - resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} - engines: {node: '>=12'} - dev: true + escape-goat@4.0.0: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true + esprima@4.0.1: {} - /event-stream@3.3.4: - resolution: {integrity: sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==} + event-stream@3.3.4: dependencies: duplexer: 0.1.2 from: 0.1.7 @@ -3084,55 +5196,36 @@ packages: split: 0.3.3 stream-combiner: 0.0.4 through: 2.3.8 - dev: true - /event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} + event-target-shim@5.0.1: {} - /events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} + events@3.3.0: {} - /exponential-backoff@3.1.1: - resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} - dev: true + exponential-backoff@3.1.1: {} - /extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - dev: true + extendable-error@0.1.7: {} - /external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} + external-editor@3.1.0: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 - dev: true - /fast-content-type-parse@1.1.0: - resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} + fast-content-type-parse@1.1.0: {} - /fast-decode-uri-component@1.0.1: - resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} + fast-decode-uri-component@1.0.1: {} - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.7 - dev: true - /fast-json-stringify@5.16.1: - resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} + fast-json-stringify@5.16.1: dependencies: '@fastify/merge-json-schemas': 0.1.1 ajv: 8.16.0 @@ -3142,27 +5235,19 @@ packages: json-schema-ref-resolver: 1.0.1 rfdc: 1.4.1 - /fast-memoize@2.5.2: - resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} - dev: true + fast-memoize@2.5.2: {} - /fast-querystring@1.1.2: - resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fast-querystring@1.1.2: dependencies: fast-decode-uri-component: 1.0.1 - /fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} + fast-redact@3.5.0: {} - /fast-uri@2.4.0: - resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} + fast-uri@2.4.0: {} - /fastify-plugin@4.5.1: - resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} + fastify-plugin@4.5.1: {} - /fastify@4.23.2: - resolution: {integrity: sha512-WFSxsHES115svC7NrerNqZwwM0UOxbC/P6toT9LRHgAAFvG7o2AN5W+H4ihCtOGuYXjZf4z+2jXC89rVEoPWOA==} + fastify@4.23.2: dependencies: '@fastify/ajv-compiler': 3.6.0 '@fastify/error': 3.4.1 @@ -3181,149 +5266,89 @@ packages: semver: 7.6.2 toad-cache: 3.7.0 - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - /fecha@4.2.3: - resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - dev: false + fecha@4.2.3: {} - /fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 - dev: false - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - dev: true - /find-my-way@7.7.0: - resolution: {integrity: sha512-+SrHpvQ52Q6W9f3wJoJBbAQULJuNEEQwBvlvYwACDhBTLOTMiQ0HYWh4+vC3OivGP2ENcTI1oKlFA2OepJNjhQ==} - engines: {node: '>=14'} + find-my-way@7.7.0: dependencies: fast-deep-equal: 3.1.3 fast-querystring: 1.1.2 safe-regex2: 2.0.0 - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /find-yarn-workspace-root2@1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} + find-yarn-workspace-root2@1.2.16: dependencies: micromatch: 4.0.7 pkg-dir: 4.2.0 - dev: true - /fn.name@1.1.0: - resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - dev: false + fn.name@1.1.0: {} - /foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - dev: true + form-data-encoder@2.1.4: {} - /formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} + formdata-polyfill@4.0.10: dependencies: fetch-blob: 3.2.0 - dev: false - /forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} + forwarded@0.2.0: {} - /fp-and-or@0.1.4: - resolution: {integrity: sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==} - engines: {node: '>=10'} - dev: true + fp-and-or@0.1.4: {} - /from@0.1.7: - resolution: {integrity: sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==} - dev: true + from@0.1.7: {} - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 - dev: true - /fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fs-minipass@3.0.3: dependencies: minipass: 7.1.2 - dev: true - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: true + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /gauge@4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + gauge@4.0.4: dependencies: aproba: 2.0.0 color-support: 1.1.3 @@ -3333,37 +5358,20 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 wide-align: 1.1.5 - dev: true - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + get-caller-file@2.0.5: {} - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: true + get-func-name@2.0.2: {} - /get-stdin@8.0.0: - resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} - engines: {node: '>=10'} - dev: true + get-stdin@8.0.0: {} - /get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - dev: true + get-stream@6.0.1: {} - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - dev: true - /glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true + glob@10.4.5: dependencies: foreground-child: 3.2.1 jackspeak: 3.4.3 @@ -3371,11 +5379,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 1.11.1 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -3383,30 +5388,20 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 - dev: true - /glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported + glob@8.1.0: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 minimatch: 5.1.6 once: 1.4.0 - dev: true - /global-dirs@3.0.1: - resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} - engines: {node: '>=10'} + global-dirs@3.0.1: dependencies: ini: 2.0.0 - dev: true - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -3414,15 +5409,10 @@ packages: ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - dev: true - /globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - dev: true + globrex@0.1.2: {} - /got@12.6.1: - resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} - engines: {node: '>=14.16'} + got@12.6.1: dependencies: '@sindresorhus/is': 5.6.0 '@szmarczak/http-timer': 5.0.1 @@ -3435,198 +5425,115 @@ packages: lowercase-keys: 3.0.0 p-cancelable: 3.0.0 responselike: 3.0.0 - dev: true - /graceful-fs@4.2.10: - resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} - dev: true + graceful-fs@4.2.10: {} - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - dev: true + graceful-fs@4.2.11: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - dev: true + has-unicode@2.0.1: {} - /has-yarn@3.0.0: - resolution: {integrity: sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + has-yarn@3.0.0: {} - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /hosted-git-info@5.2.1: - resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hosted-git-info@5.2.1: dependencies: lru-cache: 7.18.3 - dev: true - /hosted-git-info@6.1.1: - resolution: {integrity: sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hosted-git-info@6.1.1: dependencies: lru-cache: 7.18.3 - dev: true - /http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: true + http-cache-semantics@4.1.1: {} - /http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} + http2-wrapper@2.2.1: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - dev: true - /https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color - dev: true - /human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - dev: true + human-id@1.0.2: {} - /humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + humanize-ms@1.2.1: dependencies: ms: 2.1.3 - dev: true - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: true - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - requiresBuild: true + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - dev: true optional: true - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ieee754@1.2.1: {} - /ignore-by-default@1.0.1: - resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} - dev: true + ignore-by-default@1.0.1: {} - /ignore-walk@6.0.5: - resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ignore-walk@6.0.5: dependencies: minimatch: 9.0.5 - dev: true - /ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - dev: true + ignore@5.3.1: {} - /import-in-the-middle@1.7.1: - resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} - requiresBuild: true + import-in-the-middle@1.7.1: dependencies: acorn: 8.12.1 acorn-import-assertions: 1.9.0(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 - dev: false optional: true - /import-in-the-middle@1.9.0: - resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} + import-in-the-middle@1.9.0: dependencies: acorn: 8.12.1 acorn-import-attributes: 1.9.5(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 - dev: false - /import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - dev: true + import-lazy@4.0.0: {} - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - dev: true + imurmurhash@0.1.4: {} - /indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - dev: true + indent-string@4.0.0: {} - /infer-owner@1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - dev: true + infer-owner@1.0.4: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - dev: true - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true + ini@1.3.8: {} - /ini@2.0.0: - resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} - engines: {node: '>=10'} - dev: true + ini@2.0.0: {} - /ini@4.1.3: - resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + ini@4.1.3: {} - /inquirer@9.3.5: - resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} - engines: {node: '>=18'} + inquirer@9.3.5: dependencies: '@inquirer/figures': 1.0.3 ansi-escapes: 4.3.2 @@ -3640,326 +5547,178 @@ packages: strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.2 - dev: true - /ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} + ip-address@9.0.5: dependencies: jsbn: 1.1.0 sprintf-js: 1.1.3 - dev: true - /ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} + ipaddr.js@1.9.1: {} - /ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} - engines: {node: '>= 10'} - dev: false + ipaddr.js@2.2.0: {} - /is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - dev: false + is-arrayish@0.3.2: {} - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - dev: true - /is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true + is-ci@3.0.1: dependencies: ci-info: 3.9.0 - dev: true - /is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} - engines: {node: '>= 0.4'} + is-core-module@2.14.0: dependencies: hasown: 2.0.2 - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: true + is-extglob@2.1.1: {} - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true + is-fullwidth-code-point@3.0.0: {} - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - dev: true - - /is-installed-globally@0.4.0: - resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} - engines: {node: '>=10'} + + is-installed-globally@0.4.0: dependencies: global-dirs: 3.0.1 is-path-inside: 3.0.3 - dev: true - /is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - dev: true + is-interactive@1.0.0: {} - /is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - dev: true + is-lambda@1.0.1: {} - /is-npm@6.0.0: - resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-npm@6.0.0: {} - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: true + is-number@7.0.0: {} - /is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - dev: true + is-obj@2.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - dev: true + is-path-inside@3.0.3: {} - /is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - dev: false + is-stream@2.0.1: {} - /is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 - dev: true - /is-typedarray@1.0.0: - resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} - dev: true + is-typedarray@1.0.0: {} - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true + is-unicode-supported@0.1.0: {} - /is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - dev: true + is-windows@1.0.2: {} - /is-yarn-global@0.4.1: - resolution: {integrity: sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==} - engines: {node: '>=12'} - dev: true + is-yarn-global@0.4.1: {} - /isarray@0.0.1: - resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} - dev: true + isarray@0.0.1: {} - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true + isarray@1.0.0: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true + isexe@2.0.0: {} - /ix@5.0.0: - resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} + ix@5.0.0: dependencies: '@types/node': 13.13.52 tslib: 2.6.3 - dev: false - /jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - dev: true + jju@1.4.0: {} - /jose@4.15.9: - resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} - dev: false + jose@4.15.9: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: false + js-tokens@4.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - dev: true - /jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - dev: true + jsbn@1.1.0: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: true + json-buffer@3.0.1: {} - /json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + json-parse-even-better-errors@3.0.2: {} - /json-parse-helpfulerror@1.0.3: - resolution: {integrity: sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==} + json-parse-helpfulerror@1.0.3: dependencies: jju: 1.4.0 - dev: true - /json-schema-ref-resolver@1.0.1: - resolution: {integrity: sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==} + json-schema-ref-resolver@1.0.1: dependencies: fast-deep-equal: 3.1.3 - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true + json5@2.2.3: {} - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - dev: true - /jsonlines@0.1.1: - resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==} - dev: true + jsonlines@0.1.1: {} - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: true + jsonparse@1.3.1: {} - /jsonpointer@5.0.1: - resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} - engines: {node: '>=0.10.0'} - dev: false + jsonpointer@5.0.1: {} - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - dev: true - /kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - dev: true + kleur@4.1.5: {} - /kuler@2.0.0: - resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - dev: false + kuler@2.0.0: {} - /latest-version@7.0.0: - resolution: {integrity: sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==} - engines: {node: '>=14.16'} + latest-version@7.0.0: dependencies: package-json: 8.1.1 - dev: true - /leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - dev: false + leven@3.1.0: {} - /light-my-request@5.13.0: - resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + light-my-request@5.13.0: dependencies: cookie: 0.6.0 process-warning: 3.0.0 set-cookie-parser: 2.6.0 - /load-yaml-file@0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} + load-yaml-file@0.2.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - dev: true + local-pkg@0.4.3: {} - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - dev: false + lodash.merge@4.6.2: {} - /lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - dev: true + lodash.startcase@4.4.0: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.21: {} - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - /logform@2.6.1: - resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} - engines: {node: '>= 12.0.0'} + logform@2.6.1: dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -3967,51 +5726,31 @@ packages: ms: 2.1.3 safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 - dev: false - /lossless-json@2.0.11: - resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} - dev: false + lossless-json@2.0.11: {} - /loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@2.3.7: dependencies: get-func-name: 2.0.2 - dev: true - /lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + lowercase-keys@3.0.0: {} - /lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@10.4.3: {} - /lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: true - /lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - dev: true + lru-cache@7.18.3: {} - /magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - dev: true - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true + make-error@1.3.6: {} - /make-fetch-happen@10.2.1: - resolution: {integrity: sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + make-fetch-happen@10.2.1: dependencies: agentkeepalive: 4.5.0 cacache: 16.1.3 @@ -4032,11 +5771,8 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /make-fetch-happen@11.1.1: - resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + make-fetch-happen@11.1.1: dependencies: agentkeepalive: 4.5.0 cacache: 17.1.4 @@ -4055,277 +5791,146 @@ packages: ssri: 10.0.6 transitivePeerDependencies: - supports-color - dev: true - /map-stream@0.1.0: - resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} - dev: true + map-stream@0.1.0: {} - /memory-pager@1.5.0: - resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - dev: false + memory-pager@1.5.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - dev: true + merge2@1.4.1: {} - /micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} + micromatch@4.0.7: dependencies: braces: 3.0.3 picomatch: 2.3.1 - dev: true - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true + mimic-fn@2.1.0: {} - /mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - dev: true + mimic-response@3.1.0: {} - /mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + mimic-response@4.0.0: {} - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - dev: true - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - dev: true + minimist@1.2.8: {} - /minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} + minipass-collect@1.0.2: dependencies: minipass: 3.3.6 - dev: true - /minipass-fetch@2.1.2: - resolution: {integrity: sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + minipass-fetch@2.1.2: dependencies: minipass: 3.3.6 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 - dev: true - /minipass-fetch@3.0.5: - resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + minipass-fetch@3.0.5: dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 minizlib: 2.1.2 optionalDependencies: encoding: 0.1.13 - dev: true - /minipass-flush@1.0.5: - resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} - engines: {node: '>= 8'} + minipass-flush@1.0.5: dependencies: minipass: 3.3.6 - dev: true - /minipass-json-stream@1.0.1: - resolution: {integrity: sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==} + minipass-json-stream@1.0.1: dependencies: jsonparse: 1.3.1 minipass: 3.3.6 - dev: true - /minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} + minipass-pipeline@1.2.4: dependencies: minipass: 3.3.6 - dev: true - /minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} + minipass-sized@1.0.3: dependencies: minipass: 3.3.6 - dev: true - /minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} + minipass@3.3.6: dependencies: yallist: 4.0.0 - dev: true - /minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - dev: true + minipass@5.0.0: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + minizlib@2.1.2: dependencies: minipass: 3.3.6 yallist: 4.0.0 - dev: true - /mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - dev: true + mkdirp@1.0.4: {} - /mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.1: dependencies: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.3 ufo: 1.5.3 - dev: true - /mnemonist@0.39.5: - resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} + mnemonist@0.39.5: dependencies: obliterator: 2.0.4 - dev: false - /module-details-from-path@1.0.3: - resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} - dev: false + module-details-from-path@1.0.3: {} - /mongodb-connection-string-url@3.0.1: - resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} + mongodb-connection-string-url@3.0.1: dependencies: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 - dev: false - /mongodb@6.8.0: - resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} - engines: {node: '>=16.20.1'} - peerDependencies: - '@aws-sdk/credential-providers': ^3.188.0 - '@mongodb-js/zstd': ^1.1.0 - gcp-metadata: ^5.2.0 - kerberos: ^2.0.1 - mongodb-client-encryption: '>=6.0.0 <7' - snappy: ^7.2.2 - socks: ^2.7.1 - peerDependenciesMeta: - '@aws-sdk/credential-providers': - optional: true - '@mongodb-js/zstd': - optional: true - gcp-metadata: - optional: true - kerberos: - optional: true - mongodb-client-encryption: - optional: true - snappy: - optional: true - socks: - optional: true + mongodb@6.8.0: dependencies: '@mongodb-js/saslprep': 1.1.7 bson: 6.8.0 mongodb-connection-string-url: 3.0.1 - dev: false - /moo@0.5.2: - resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} - dev: false + moo@0.5.2: {} - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true + mri@1.2.0: {} - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.2: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + mute-stream@1.0.0: {} - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true + nanoid@3.3.7: {} - /nearley@2.20.1: - resolution: {integrity: sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==} - hasBin: true + nearley@2.20.1: dependencies: commander: 2.20.3 moo: 0.5.2 railroad-diagrams: 1.0.0 randexp: 0.4.6 - dev: false - /negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - dev: true + negotiator@0.6.3: {} - /node-cleanup@2.1.2: - resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} - dev: true + node-cleanup@2.1.2: {} - /node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - dev: false + node-domexception@1.0.0: {} - /node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - dev: false - /node-gyp@9.4.1: - resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==} - engines: {node: ^12.13 || ^14.13 || >=16} - hasBin: true + node-gyp@9.4.1: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.1 @@ -4341,12 +5946,8 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /nodemon@3.1.4: - resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} - engines: {node: '>=10'} - hasBin: true + nodemon@3.1.4: dependencies: chokidar: 3.6.0 debug: 4.3.5(supports-color@5.5.0) @@ -4358,54 +5959,32 @@ packages: supports-color: 5.5.0 touch: 3.1.1 undefsafe: 2.0.5 - dev: true - /noms@0.0.0: - resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} + noms@0.0.0: dependencies: inherits: 2.0.4 readable-stream: 1.0.34 - dev: true - /nopt@6.0.0: - resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - hasBin: true + nopt@6.0.0: dependencies: abbrev: 1.1.1 - dev: true - /normalize-package-data@5.0.0: - resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + normalize-package-data@5.0.0: dependencies: hosted-git-info: 6.1.1 is-core-module: 2.14.0 semver: 7.6.2 validate-npm-package-license: 3.0.4 - dev: true - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true + normalize-path@3.0.0: {} - /normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} - engines: {node: '>=14.16'} - dev: true + normalize-url@8.0.1: {} - /npm-bundled@3.0.1: - resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-bundled@3.0.1: dependencies: npm-normalize-package-bin: 3.0.1 - dev: true - /npm-check-updates@16.14.20: - resolution: {integrity: sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==} - engines: {node: '>=14.14'} - hasBin: true + npm-check-updates@16.14.20: dependencies: '@types/semver-utils': 1.1.3 chalk: 5.3.0 @@ -4443,50 +6022,32 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /npm-install-checks@6.3.0: - resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-install-checks@6.3.0: dependencies: semver: 7.6.2 - dev: true - /npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + npm-normalize-package-bin@3.0.1: {} - /npm-package-arg@10.1.0: - resolution: {integrity: sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-package-arg@10.1.0: dependencies: hosted-git-info: 6.1.1 proc-log: 3.0.0 semver: 7.6.2 validate-npm-package-name: 5.0.1 - dev: true - /npm-packlist@7.0.4: - resolution: {integrity: sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-packlist@7.0.4: dependencies: ignore-walk: 6.0.5 - dev: true - /npm-pick-manifest@8.0.2: - resolution: {integrity: sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-pick-manifest@8.0.2: dependencies: npm-install-checks: 6.3.0 npm-normalize-package-bin: 3.0.1 npm-package-arg: 10.1.0 semver: 7.6.2 - dev: true - /npm-registry-fetch@14.0.5: - resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-registry-fetch@14.0.5: dependencies: make-fetch-happen: 11.1.1 minipass: 5.0.0 @@ -4497,69 +6058,42 @@ packages: proc-log: 3.0.0 transitivePeerDependencies: - supports-color - dev: true - /npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. + npmlog@6.0.2: dependencies: are-we-there-yet: 3.0.1 console-control-strings: 1.1.0 gauge: 4.0.4 set-blocking: 2.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - dev: false + object-assign@4.1.1: {} - /obliterator@2.0.4: - resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} - dev: false + obliterator@2.0.4: {} - /on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} + on-exit-leak-free@2.1.2: {} - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - dev: true - /one-time@1.0.0: - resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} + one-time@1.0.0: dependencies: fn.name: 1.1.0 - dev: false - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - dev: true - /opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} - engines: {node: '>18.0.0'} - requiresBuild: true - peerDependencies: - '@opentelemetry/api': ^1.6.0 + opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - dev: false optional: true - /ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} + ora@5.4.1: dependencies: bl: 4.1.0 chalk: 4.1.2 @@ -4570,99 +6104,55 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - dev: true - /os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: true + os-tmpdir@1.0.2: {} - /outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - dev: true + outdent@0.5.0: {} - /p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - dev: true + p-cancelable@3.0.0: {} - /p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} + p-filter@2.1.0: dependencies: p-map: 2.1.0 - dev: true - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + p-limit@2.3.0: dependencies: p-try: 2.2.0 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - dev: true - /p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-limit@4.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + p-locate@4.1.0: dependencies: p-limit: 2.3.0 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true + p-map@2.1.0: {} - /p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} + p-map@4.0.0: dependencies: aggregate-error: 3.1.0 - dev: true - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true + p-try@2.2.0: {} - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /package-json@8.1.1: - resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} - engines: {node: '>=14.16'} + package-json@8.1.1: dependencies: got: 12.6.1 registry-auth-token: 5.0.2 registry-url: 6.0.1 semver: 7.6.2 - dev: true - /pacote@15.2.0: - resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + pacote@15.2.0: dependencies: '@npmcli/git': 4.1.0 '@npmcli/installed-package-contents': 2.1.0 @@ -4685,111 +6175,65 @@ packages: transitivePeerDependencies: - bluebird - supports-color - dev: true - /parse-github-url@1.0.3: - resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} - engines: {node: '>= 0.10'} - hasBin: true - dev: true + parse-github-url@1.0.3: {} - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: true + path-exists@4.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: true + path-is-absolute@1.0.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - dev: true + path-key@3.1.1: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-parse@1.0.7: {} - /path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} + path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 minipass: 7.1.2 - dev: true - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - dev: true + path-type@4.0.0: {} - /pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - dev: true + pathe@1.1.2: {} - /pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - dev: true + pathval@1.1.1: {} - /pause-stream@0.0.11: - resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pause-stream@0.0.11: dependencies: through: 2.3.8 - dev: true - /pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - dev: false + pg-int8@1.0.1: {} - /pg-protocol@1.6.1: - resolution: {integrity: sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==} - dev: false + pg-protocol@1.6.1: {} - /pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} + pg-types@2.2.0: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 postgres-bytea: 1.0.0 postgres-date: 1.0.7 postgres-interval: 1.2.0 - dev: false - /pgsql-ast-parser@11.2.0: - resolution: {integrity: sha512-/8KCcQjePoQDOtfZQuoV/4Y3WpmQVp7E+RFayAdjJpdBdu2dBnKnuQe9XU4g5Td5qC0G+i/fFK/DlNjvWwg+FA==} + pgsql-ast-parser@11.2.0: dependencies: moo: 0.5.2 nearley: 2.20.1 - dev: false - /picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + pgwire@https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: true + picocolors@1.0.1: {} - /pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - dev: true + picomatch@2.3.1: {} - /pino-abstract-transport@1.2.0: - resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + pify@4.0.1: {} + + pino-abstract-transport@1.2.0: dependencies: readable-stream: 4.5.2 split2: 4.2.0 - /pino-std-serializers@6.2.2: - resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino-std-serializers@6.2.2: {} - /pino@8.21.0: - resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} - hasBin: true + pino@8.21.0: dependencies: atomic-sleep: 1.0.0 fast-redact: 3.5.0 @@ -4803,190 +6247,106 @@ packages: sonic-boom: 3.8.1 thread-stream: 2.7.0 - /pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - dev: true - /pkg-types@1.1.3: - resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pkg-types@1.1.3: dependencies: confbox: 0.1.7 mlly: 1.7.1 pathe: 1.1.2 - dev: true - /postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 source-map-js: 1.2.0 - dev: true - /postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - dev: false + postgres-array@2.0.0: {} - /postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - dev: false + postgres-bytea@1.0.0: {} - /postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - dev: false + postgres-date@1.0.7: {} - /postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} + postgres-interval@1.2.0: dependencies: xtend: 4.0.2 - dev: false - /preferred-pm@3.1.4: - resolution: {integrity: sha512-lEHd+yEm22jXdCphDrkvIJQU66EuLojPPtvZkpKIkiD+l0DMThF/niqZKJSoU8Vl7iuvtmzyMhir9LdVy5WMnA==} - engines: {node: '>=10'} + preferred-pm@3.1.4: dependencies: find-up: 5.0.0 find-yarn-workspace-root2: 1.2.16 path-exists: 4.0.0 which-pm: 2.2.0 - dev: true - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true + prettier@2.8.8: {} - /pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.3.1 - dev: true - /proc-log@3.0.0: - resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + proc-log@3.0.0: {} - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true + process-nextick-args@2.0.1: {} - /process-warning@2.3.2: - resolution: {integrity: sha512-n9wh8tvBe5sFmsqlg+XQhaQLumwpqoAUruLwjCopgTmUBjJ/fjtBsJzKleCaIGBOMXYEhp1YfKl4d7rJ5ZKJGA==} + process-warning@2.3.2: {} - /process-warning@3.0.0: - resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} + process-warning@3.0.0: {} - /process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} + process@0.11.10: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true + progress@2.0.3: {} - /promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - dev: true + promise-inflight@1.0.1: {} - /promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} + promise-retry@2.0.1: dependencies: err-code: 2.0.3 retry: 0.12.0 - dev: true - /prompts-ncu@3.0.0: - resolution: {integrity: sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==} - engines: {node: '>= 14'} + prompts-ncu@3.0.0: dependencies: kleur: 4.1.5 sisteransi: 1.0.5 - dev: true - /proto-list@1.2.4: - resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - dev: true + proto-list@1.2.4: {} - /proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} + proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 - /ps-tree@1.2.0: - resolution: {integrity: sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA==} - engines: {node: '>= 0.10'} - hasBin: true + ps-tree@1.2.0: dependencies: event-stream: 3.3.4 - dev: true - /pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: true + pseudomap@1.0.2: {} - /pstree.remy@1.1.8: - resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - dev: true + pstree.remy@1.1.8: {} - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /pupa@3.1.0: - resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} - engines: {node: '>=12.20'} + pupa@3.1.0: dependencies: escape-goat: 4.0.0 - dev: true - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true + queue-microtask@1.2.3: {} - /quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + quick-format-unescaped@4.0.4: {} - /quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - dev: true + quick-lru@5.1.1: {} - /railroad-diagrams@1.0.0: - resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} - dev: false + railroad-diagrams@1.0.0: {} - /randexp@0.4.6: - resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} - engines: {node: '>=0.12'} + randexp@0.4.6: dependencies: discontinuous-range: 1.0.0 ret: 0.1.15 - dev: false - /rc-config-loader@4.1.3: - resolution: {integrity: sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==} + rc-config-loader@4.1.3: dependencies: debug: 4.3.5(supports-color@5.5.0) js-yaml: 4.1.0 @@ -4994,62 +6354,43 @@ packages: require-from-string: 2.0.2 transitivePeerDependencies: - supports-color - dev: true - /rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true + rc@1.2.8: dependencies: deep-extend: 0.6.0 ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 - dev: true - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: true + react-is@18.3.1: {} - /read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 npm-normalize-package-bin: 3.0.1 - dev: true - /read-package-json@6.0.4: - resolution: {integrity: sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - deprecated: This package is no longer supported. Please use @npmcli/package-json instead. + read-package-json@6.0.4: dependencies: glob: 10.4.5 json-parse-even-better-errors: 3.0.2 normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 - dev: true - /read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + readable-stream@1.0.34: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 isarray: 0.0.1 string_decoder: 0.10.31 - dev: true - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -5058,19 +6399,14 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + readable-stream@4.5.2: dependencies: abort-controller: 3.0.0 buffer: 6.0.3 @@ -5078,141 +6414,78 @@ packages: process: 0.11.10 string_decoder: 1.3.0 - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - dev: true - /real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} + real-require@0.2.0: {} - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - dev: true + regenerator-runtime@0.14.1: {} - /registry-auth-token@5.0.2: - resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} - engines: {node: '>=14'} + registry-auth-token@5.0.2: dependencies: '@pnpm/npm-conf': 2.2.2 - dev: true - /registry-url@6.0.1: - resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} - engines: {node: '>=12'} + registry-url@6.0.1: dependencies: rc: 1.2.8 - dev: true - /remote-git-tags@3.0.0: - resolution: {integrity: sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==} - engines: {node: '>=8'} - dev: true + remote-git-tags@3.0.0: {} - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /require-in-the-middle@7.3.0: - resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} - engines: {node: '>=8.6.0'} + require-in-the-middle@7.3.0: dependencies: debug: 4.3.5(supports-color@5.5.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: false - /resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - dev: true + resolve-alpn@1.2.1: {} - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true + resolve-from@5.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} + responselike@3.0.0: dependencies: lowercase-keys: 3.0.0 - dev: true - /restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: true - /ret@0.1.15: - resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} - engines: {node: '>=0.12'} - dev: false + ret@0.1.15: {} - /ret@0.2.2: - resolution: {integrity: sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==} - engines: {node: '>=4'} + ret@0.2.2: {} - /retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - dev: true + retry@0.12.0: {} - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + reusify@1.0.4: {} - /rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rfdc@1.4.1: {} - /rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@2.7.1: dependencies: glob: 7.2.3 - dev: true - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - dev: true - /rimraf@5.0.9: - resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} - engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} - hasBin: true + rimraf@5.0.9: dependencies: glob: 10.4.5 - dev: true - /rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: @@ -5233,129 +6506,72 @@ packages: '@rollup/rollup-win32-ia32-msvc': 4.18.1 '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 - dev: true - /rsocket-core@1.0.0-alpha.3: - resolution: {integrity: sha512-BzIe2w8dFJlUS5N9fGUNRkxL19kd64bxbXsT11wj7isLfKkPZeNXisB2p/LWvSjFzWStnpOiScZ0g3/8ROE0pw==} + rsocket-core@1.0.0-alpha.3: {} - /rsocket-websocket-client@1.0.0-alpha.3: - resolution: {integrity: sha512-CwTwTNMGa8BKvrWde/kM3q8IHuzO8RCIfzuj25BsVe9y8eehDQHt4fXk0g1i/wpsxTm+RY6DxE6Vr5snozKVOg==} + rsocket-websocket-client@1.0.0-alpha.3: dependencies: rsocket-core: 1.0.0-alpha.3 - dev: true - /run-async@3.0.0: - resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} - engines: {node: '>=0.12.0'} - dev: true + run-async@3.0.0: {} - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - dev: true - /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.1: dependencies: tslib: 2.6.3 - dev: true - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-buffer@5.2.1: {} - /safe-regex2@2.0.0: - resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} + safe-regex2@2.0.0: dependencies: ret: 0.2.2 - /safe-stable-stringify@2.4.3: - resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==} - engines: {node: '>=10'} + safe-stable-stringify@2.4.3: {} - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - dev: true + safer-buffer@2.1.2: {} - /secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + secure-json-parse@2.7.0: {} - /semver-diff@4.0.0: - resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} - engines: {node: '>=12'} + semver-diff@4.0.0: dependencies: semver: 7.6.2 - dev: true - /semver-utils@1.1.4: - resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} - dev: true + semver-utils@1.1.4: {} - /semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.2: {} - /set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - dev: true + set-blocking@2.0.0: {} - /set-cookie-parser@2.6.0: - resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + set-cookie-parser@2.6.0: {} - /shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 - dev: true - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - dev: true - /shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - dev: true + shebang-regex@1.0.0: {} - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - dev: true + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - dev: false + shimmer@1.2.1: {} - /siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - dev: true + siginfo@2.0.0: {} - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - dev: true + signal-exit@4.1.0: {} - /sigstore@1.9.0: - resolution: {integrity: sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + sigstore@1.9.0: dependencies: '@sigstore/bundle': 1.1.0 '@sigstore/protobuf-specs': 0.2.1 @@ -5364,271 +6580,161 @@ packages: make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + simple-swizzle@0.2.2: dependencies: is-arrayish: 0.3.2 - dev: false - /simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} + simple-update-notifier@2.0.0: dependencies: semver: 7.6.2 - dev: true - /sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - dev: true + sisteransi@1.0.5: {} - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - dev: true + slash@3.0.0: {} - /smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - dev: true + smart-buffer@4.2.0: {} - /socks-proxy-agent@7.0.0: - resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} - engines: {node: '>= 10'} + socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 debug: 4.3.5(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color - dev: true - /socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + socks@2.8.3: dependencies: ip-address: 9.0.5 smart-buffer: 4.2.0 - dev: true - /sonic-boom@3.8.1: - resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} + sonic-boom@3.8.1: dependencies: atomic-sleep: 1.0.0 - /source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - dev: true + source-map-js@1.2.0: {} - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - dev: true - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: true + source-map@0.6.1: {} - /sparse-bitfield@3.0.3: - resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} + sparse-bitfield@3.0.3: dependencies: memory-pager: 1.5.0 - dev: false - /spawn-command@0.0.2: - resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} - dev: true + spawn-command@0.0.2: {} - /spawn-please@2.0.2: - resolution: {integrity: sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==} - engines: {node: '>=14'} + spawn-please@2.0.2: dependencies: cross-spawn: 7.0.3 - dev: true - /spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + spawndamnit@2.0.0: dependencies: cross-spawn: 5.1.0 signal-exit: 3.0.7 - dev: true - /spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.18 - dev: true - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - dev: true + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.18 - dev: true - /spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - dev: true + spdx-license-ids@3.0.18: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} + split2@4.2.0: {} - /split@0.3.3: - resolution: {integrity: sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==} + split@0.3.3: dependencies: through: 2.3.8 - dev: true - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true + sprintf-js@1.0.3: {} - /sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - dev: true + sprintf-js@1.1.3: {} - /ssri@10.0.6: - resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ssri@10.0.6: dependencies: minipass: 7.1.2 - dev: true - /ssri@9.0.1: - resolution: {integrity: sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + ssri@9.0.1: dependencies: minipass: 3.3.6 - dev: true - /stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - dev: false + stack-trace@0.0.10: {} - /stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - dev: true + stackback@0.0.2: {} - /std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - dev: true + std-env@3.7.0: {} - /stream-combiner@0.0.4: - resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-combiner@0.0.4: dependencies: duplexer: 0.1.2 - dev: true - /string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - dev: true + string-argv@0.3.2: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - /string_decoder@0.10.31: - resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} - dev: true + string_decoder@0.10.31: {} - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - dev: true - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - dev: true - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - dev: true + strip-bom@3.0.0: {} - /strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - dev: true + strip-json-comments@2.0.1: {} - /strip-json-comments@5.0.1: - resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} - engines: {node: '>=14.16'} - dev: true + strip-json-comments@5.0.1: {} - /strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + strip-literal@1.3.0: dependencies: acorn: 8.12.1 - dev: true - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} + tar@6.2.1: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 @@ -5636,101 +6742,51 @@ packages: minizlib: 2.1.2 mkdirp: 1.0.4 yallist: 4.0.0 - dev: true - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: true + term-size@2.2.1: {} - /text-hex@1.0.0: - resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - dev: false + text-hex@1.0.0: {} - /thread-stream@2.7.0: - resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} + thread-stream@2.7.0: dependencies: real-require: 0.2.0 - /through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + through2@2.0.5: dependencies: readable-stream: 2.3.8 xtend: 4.0.2 - dev: true - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true + through@2.3.8: {} - /tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - dev: true + tinybench@2.8.0: {} - /tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} - dev: true + tinypool@0.7.0: {} - /tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - dev: true + tinyspy@2.2.1: {} - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - dev: true - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - dev: true - /toad-cache@3.7.0: - resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} - engines: {node: '>=12'} + toad-cache@3.7.0: {} - /touch@3.1.1: - resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} - hasBin: true - dev: true + touch@3.1.1: {} - /tr46@4.1.1: - resolution: {integrity: sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==} - engines: {node: '>=14'} + tr46@4.1.1: dependencies: punycode: 2.3.1 - dev: false - /tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - dev: true + tree-kill@1.2.2: {} - /triple-beam@1.4.1: - resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} - engines: {node: '>= 14.0.0'} - dev: false + triple-beam@1.4.1: {} - /ts-codec@1.2.2: - resolution: {integrity: sha512-dlAsfmeEa3AA+6rvgKLlLb6rqTMthQNH9w+B2FGM5sRu7sTd0GSbmLuW0HAmRlIeyUEZzMXk0yRmP0afribqmg==} - dev: false + ts-codec@1.2.2: {} - /ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): - resolution: {integrity: sha512-ywMrhCfH6M75yftYvrvNarLEY+SUXtUvU8/0Z6llrHQVBx12GiFk5sStF8UdfE/yfzk9IAq7O5EEbTQsxlBI8w==} - engines: {node: '>=0.8.0'} - hasBin: true - peerDependencies: - node-notifier: '*' - typescript: '*' - peerDependenciesMeta: - node-notifier: - optional: true + ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): dependencies: chokidar: 3.6.0 dynamic-dedupe: 0.3.0 @@ -5747,21 +6803,8 @@ packages: - '@swc/core' - '@swc/wasm' - '@types/node' - dev: true - /ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true + ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -5778,146 +6821,79 @@ packages: typescript: 5.2.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - dev: true - /tsc-watch@6.2.0(typescript@5.2.2): - resolution: {integrity: sha512-2LBhf9kjKXnz7KQ/puLHlozMzzUNHAdYBNMkg3eksQJ9GBAgMg8czznM83T5PmsoUvDnXzfIeQn2lNcIYDr8LA==} - engines: {node: '>=12.12.0'} - hasBin: true - peerDependencies: - typescript: '*' + tsc-watch@6.2.0(typescript@5.2.2): dependencies: cross-spawn: 7.0.3 node-cleanup: 2.1.2 ps-tree: 1.2.0 string-argv: 0.3.2 typescript: 5.2.2 - dev: true - /tsconfck@3.1.1(typescript@5.2.2): - resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} - engines: {node: ^18 || >=20} - hasBin: true - peerDependencies: - typescript: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true + tsconfck@3.1.1(typescript@5.2.2): dependencies: typescript: 5.2.2 - dev: true - /tsconfig@7.0.0: - resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + tsconfig@7.0.0: dependencies: '@types/strip-bom': 3.0.0 '@types/strip-json-comments': 0.0.30 strip-bom: 3.0.0 strip-json-comments: 2.0.1 - dev: true - /tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.6.3: {} - /tuf-js@1.1.7: - resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 debug: 4.3.5(supports-color@5.5.0) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color - dev: true - /type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - dev: true + type-detect@4.0.8: {} - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true + type-fest@0.21.3: {} - /type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} - dev: true + type-fest@1.4.0: {} - /type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - dev: true + type-fest@2.19.0: {} - /typedarray-to-buffer@3.1.5: - resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + typedarray-to-buffer@3.1.5: dependencies: is-typedarray: 1.0.0 - dev: true - /typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} - engines: {node: '>=14.17'} - hasBin: true - dev: true + typescript@5.2.2: {} - /ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - dev: true + ufo@1.5.3: {} - /undefsafe@2.0.5: - resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - dev: true + undefsafe@2.0.5: {} - /unique-filename@2.0.1: - resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-filename@2.0.1: dependencies: unique-slug: 3.0.0 - dev: true - /unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 - dev: true - /unique-slug@3.0.0: - resolution: {integrity: sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + unique-slug@3.0.0: dependencies: imurmurhash: 0.1.4 - dev: true - /unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-slug@4.0.0: dependencies: imurmurhash: 0.1.4 - dev: true - /unique-string@3.0.0: - resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} - engines: {node: '>=12'} + unique-string@3.0.0: dependencies: crypto-random-string: 4.0.0 - dev: true - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true + universalify@0.1.2: {} - /untildify@4.0.0: - resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} - engines: {node: '>=8'} - dev: true + untildify@4.0.0: {} - /update-notifier@6.0.2: - resolution: {integrity: sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==} - engines: {node: '>=14.16'} + update-notifier@6.0.2: dependencies: boxen: 7.1.1 chalk: 5.3.0 @@ -5933,46 +6909,27 @@ packages: semver: 7.6.2 semver-diff: 4.0.0 xdg-basedir: 5.1.0 - dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - dev: false + uuid@9.0.1: {} - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true + v8-compile-cache-lib@3.0.1: {} - /validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - dev: true - /validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + validate-npm-package-name@5.0.1: {} - /vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - dev: false + vary@1.1.2: {} - /vite-node@0.34.6(@types/node@18.11.11): - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true + vite-node@0.34.6(@types/node@18.11.11): dependencies: cac: 6.7.14 debug: 4.3.5(supports-color@5.5.0) @@ -5989,15 +6946,8 @@ packages: - sugarss - supports-color - terser - dev: true - /vite-tsconfig-paths@4.3.2(typescript@5.2.2): - resolution: {integrity: sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==} - peerDependencies: - vite: '*' - peerDependenciesMeta: - vite: - optional: true + vite-tsconfig-paths@4.3.2(typescript@5.2.2): dependencies: debug: 4.3.5(supports-color@5.5.0) globrex: 0.1.2 @@ -6005,35 +6955,8 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: true - /vite@5.3.3(@types/node@18.11.11): - resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.3.3(@types/node@18.11.11): dependencies: '@types/node': 18.11.11 esbuild: 0.21.5 @@ -6041,38 +6964,8 @@ packages: rollup: 4.18.1 optionalDependencies: fsevents: 2.3.3 - dev: true - /vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true + vitest@0.34.6: dependencies: '@types/chai': 4.3.16 '@types/chai-subset': 1.3.5 @@ -6106,97 +6999,57 @@ packages: - sugarss - supports-color - terser - dev: true - /wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 - dev: true - /web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - dev: false + web-streams-polyfill@3.3.3: {} - /webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - dev: false + webidl-conversions@7.0.0: {} - /whatwg-url@13.0.0: - resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} - engines: {node: '>=16'} + whatwg-url@13.0.0: dependencies: tr46: 4.1.1 webidl-conversions: 7.0.0 - dev: false - /which-pm@2.2.0: - resolution: {integrity: sha512-MOiaDbA5ZZgUjkeMWM5EkJp4loW5ZRoa5bc3/aeMox/PJelMhE6t7S/mLuiY43DBupyxH+S0U1bTui9kWUlmsw==} - engines: {node: '>=8.15'} + which-pm@2.2.0: dependencies: load-yaml-file: 0.2.0 path-exists: 4.0.0 - dev: true - /which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + which@1.3.1: dependencies: isexe: 2.0.0 - dev: true - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - dev: true - /which@3.0.1: - resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - hasBin: true + which@3.0.1: dependencies: isexe: 2.0.0 - dev: true - /why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - dev: true - /wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + wide-align@1.1.5: dependencies: string-width: 4.2.3 - dev: true - /widest-line@4.0.1: - resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} - engines: {node: '>=12'} + widest-line@4.0.1: dependencies: string-width: 5.1.2 - dev: true - /winston-transport@4.7.1: - resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} - engines: {node: '>= 12.0.0'} + winston-transport@4.7.1: dependencies: logform: 2.6.1 readable-stream: 3.6.2 triple-beam: 1.4.1 - dev: false - /winston@3.13.1: - resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} - engines: {node: '>= 12.0.0'} + winston@3.13.1: dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 @@ -6209,101 +7062,53 @@ packages: stack-trace: 0.0.10 triple-beam: 1.4.1 winston-transport: 4.7.1 - dev: false - /wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true + wrappy@1.0.2: {} - /write-file-atomic@3.0.3: - resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + write-file-atomic@3.0.3: dependencies: imurmurhash: 0.1.4 is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - dev: true - /ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@8.18.0: {} - /xdg-basedir@5.1.0: - resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} - engines: {node: '>=12'} - dev: true + xdg-basedir@5.1.0: {} - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: true + yallist@2.1.2: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - dev: true + yallist@4.0.0: {} - /yaml@2.4.5: - resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} - engines: {node: '>= 14'} - hasBin: true - dev: false + yaml@2.4.5: {} - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - dev: true + yargs-parser@20.2.9: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true + yargs-parser@21.1.1: {} - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} + yargs@16.2.0: dependencies: cliui: 7.0.4 escalade: 3.1.2 @@ -6312,11 +7117,8 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 - dev: true - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.1.2 @@ -6325,35 +7127,13 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: true + yn@3.1.1: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@0.1.0: {} - /yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - dev: true + yocto-queue@1.1.1: {} - /zod@3.23.8: - resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} - dev: false + yoctocolors-cjs@2.1.2: {} - github.com/kagis/pgwire/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87: - resolution: {tarball: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87} - name: pgwire - version: 0.7.0 - engines: {node: '>=14.18.0'} - dev: false + zod@3.23.8: {} From bf2524916cac3f7eaef09c369016e1788eb97f29 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 23 Jul 2024 09:08:53 +0200 Subject: [PATCH 038/298] Removed connection from Adapter interfaces --- .../replication/core/ReplicationAdapter.ts | 84 ++++++++----------- .../src/replication/core/ReplicationEntity.ts | 32 ++----- .../src/replication/core/ReplicationModule.ts | 2 +- 3 files changed, 45 insertions(+), 73 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 83063aa62..3c685cda8 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -1,78 +1,68 @@ -import { TablePattern } from '@powersync/service-sync-rules'; +import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; import { ReplicationEntity } from './ReplicationEntity.js'; import { SaveOptions } from '../../storage/BucketStorage.js'; -/** - * Manage the creation and termination of connections for a datasource. - * For some data sources there will be no difference between snapshot and replication connections - */ -interface ConnectionManager { - /** - * Create a connection to use for replication. - */ - createReplicationConnection(): Promise; - - /** - * Create a connection to use for the initial snapshot replication. - * This connection should not be shared - */ - createConnection(): Promise; - - /** - * Map a datasource specific connection error to a general category of powersync errors - * @param error - */ - mapError(error: Error): ConnectionError; -} - /** * The ReplicationAdapter describes all the methods that are required by the * Replicator to replicate data from a datasource into the PowerSync Bucket storage */ -export interface ReplicationAdapter { +export interface ReplicationAdapter { /** * Unique name to identify this adapter in the PowerSync system - * Suggestion: datasource type + datasource name + * Suggestion: datasource type + datasource name ie. postgres-prod1 */ name(): string; /** - * Return a manager that can create connections to the DataSource + * Check that the configuration required for replication on the datasource is in place. + * If any configuration is missing or incorrect, an error should be thrown with the details. */ - createConnectionManager(): ConnectionManager; + checkPrerequisites(): Promise; /** - * Validate any configuration on the datasource that needs to be in place for - * replication. If any configuration is missing or incorrect, an error should be thrown with the details. + * Get all the fully qualified entities that match the provided pattern + * @param pattern // TODO: Need something more generic than TablePattern */ - validateConfiguration(connection: TConnection): void; + toReplicationEntities(pattern: TablePattern): Promise; /** - * Get all the fully qualified entities that match the provided pattern - * @param connection - * @param pattern // TODO: Need something more generic than TablePattern + * Get the number of entries for this Entity + * @param entity */ - toReplicationEntities(connection: TConnection, pattern: TablePattern): Promise[]>; + count(entity: ReplicationEntity): Promise; + + /** + * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. + * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. + * This LSN will be used as the starting point for the replication stream. + * @param options + */ + initializeData(options: InitializeDataOptions): Promise; /** * Start replicating data, assumes that initializeData has already finished running * Stream any changes back on the provided changeListener - * @param connection - * @param changeListener + * @param options */ - startReplication(connection: TConnection, changeListener: (change: SaveOptions) => {}): Promise; + startReplication(options: StartReplicationOptions): Promise; /** - * Immediately interrupt and stop any currently running replication. + * Clean up any configuration or state for the replication with the given identifier on the datasource. + * This assumes that the replication is not currently active. + * @param syncRuleId The id of the SyncRule that was used to configure the replication */ - terminateReplication(): Promise; + cleanupReplication(syncRuleId: number): Promise; +} + +export interface InitializeDataOptions { + entity: ReplicationEntity; + entry_consumer: (batch: SqliteRow[]) => {}; + abort_signal: AbortSignal; } -export enum ConnectionError { - INCORRECT_CREDENTIALS = 'INCORRECT_CREDENTIALS', - HOST_INACCESSIBLE = 'HOST_INACCESSIBLE', - CONNECTION_REFUSED = 'CONNECTION_REFUSED', - CONNECTION_CLOSED = 'CONNECTION_CLOSED', - RETRIEVAL_FAILED = 'RETRIEVAL_FAILED', - GENERAL = 'GENERAL' +export interface StartReplicationOptions { + entities: ReplicationEntity[]; + from_lsn: string; + change_listener: (change: SaveOptions) => {}; + abort_signal: AbortSignal; } diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts index 0f4d52af5..699ea96a5 100644 --- a/packages/service-core/src/replication/core/ReplicationEntity.ts +++ b/packages/service-core/src/replication/core/ReplicationEntity.ts @@ -2,7 +2,6 @@ import { SqliteRow } from '@powersync/service-sync-rules'; /** * Describes a replication entity, which is a logical representation of a table or collection in a data source. - * Extend this interface to add additional properties specific entities in the data source. */ export interface ReplicationEntityDescriptor { name: string; @@ -24,11 +23,12 @@ export interface ReplicationEntityDescriptor { * The field(s) that uniquely identify an entry/row in this replication entity */ primaryIdentifierFields: string[]; -} -export abstract class ReplicationEntity { - public descriptor: T; + // Add any additional properties specific to the entity here + additionalProperties: Record; +} +export class ReplicationEntity { /** * Defaults to true for tests. */ @@ -44,9 +44,9 @@ export abstract class ReplicationEntity { */ public snapshotComplete: boolean = false; - constructor(descriptor: T) { - this.descriptor = descriptor; - } + public snapshotLSN: string | null = null; + + constructor(public descriptor: ReplicationEntityDescriptor) {} public hasPrimaryIdentifierFields(): boolean { return this.descriptor.primaryIdentifierFields.length > 0; @@ -55,22 +55,4 @@ export abstract class ReplicationEntity { public syncAny() { return this.syncData || this.syncParameters; } - - /** - * Get the number of entries for this Entity - * @param connection - */ - public abstract count(connection: TConnection): Promise; - - /** - * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. - * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. - * This LSN will be used as the starting point for the replication stream. - * @param connection - * @param entryConsumer - */ - public abstract getSnapshot( - connection: TConnection, - entryConsumer: (batch: SqliteRow[]) => {} - ): Promise; } diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 751155e9b..d8ec2019e 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -35,7 +35,7 @@ export abstract class ReplicationModule extends AbstractModule { /** * Create the ReplicationAdapter to be used by PowerSync replicator. */ - protected abstract createReplicationAdapter(): ReplicationAdapter; + protected abstract createReplicationAdapter(): ReplicationAdapter; /** * Return the TS codec schema describing the required configuration values for this module. From 13e505519beb6924c3f6855aaef7184cb0fc29b0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 23 Jul 2024 14:22:25 +0200 Subject: [PATCH 039/298] Keeping SourceTable in replication adapter interface for now --- .../src/replication/core/ReplicationAdapter.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 3c685cda8..7bdff26e6 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -1,6 +1,5 @@ import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; -import { ReplicationEntity } from './ReplicationEntity.js'; -import { SaveOptions } from '../../storage/BucketStorage.js'; +import * as storage from '../../storage/storage-index.js'; /** * The ReplicationAdapter describes all the methods that are required by the @@ -23,13 +22,13 @@ export interface ReplicationAdapter { * Get all the fully qualified entities that match the provided pattern * @param pattern // TODO: Need something more generic than TablePattern */ - toReplicationEntities(pattern: TablePattern): Promise; + resolveReplicationEntities(pattern: TablePattern): Promise; /** * Get the number of entries for this Entity * @param entity */ - count(entity: ReplicationEntity): Promise; + count(entity: storage.SourceTable): Promise; /** * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. @@ -55,14 +54,14 @@ export interface ReplicationAdapter { } export interface InitializeDataOptions { - entity: ReplicationEntity; + entity: storage.SourceTable; entry_consumer: (batch: SqliteRow[]) => {}; abort_signal: AbortSignal; } export interface StartReplicationOptions { - entities: ReplicationEntity[]; + entities: storage.SourceTable[]; from_lsn: string; - change_listener: (change: SaveOptions) => {}; + change_listener: (change: storage.SaveOptions) => {}; abort_signal: AbortSignal; } From 7998d33a90b996d981e8692b9ce79ffddb43b0b0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 30 Jul 2024 10:07:19 +0200 Subject: [PATCH 040/298] Expanded startReplication method in ReplicationAdapter --- .../replication/core/ReplicationAdapter.ts | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 7bdff26e6..dfb84e3ff 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -1,5 +1,6 @@ import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; import * as storage from '../../storage/storage-index.js'; +import { PgRelation } from '../PgRelation.js'; /** * The ReplicationAdapter describes all the methods that are required by the @@ -20,7 +21,7 @@ export interface ReplicationAdapter { /** * Get all the fully qualified entities that match the provided pattern - * @param pattern // TODO: Need something more generic than TablePattern + * @param pattern // TODO: Need something more generic then SourceTable */ resolveReplicationEntities(pattern: TablePattern): Promise; @@ -31,12 +32,12 @@ export interface ReplicationAdapter { count(entity: storage.SourceTable): Promise; /** - * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided recordConsumer in batches. + * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided entryConsumer in batches. * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. * This LSN will be used as the starting point for the replication stream. * @param options */ - initializeData(options: InitializeDataOptions): Promise; + initializeData(options: InitializeDataOptions): Promise; /** * Start replicating data, assumes that initializeData has already finished running @@ -53,15 +54,51 @@ export interface ReplicationAdapter { cleanupReplication(syncRuleId: number): Promise; } -export interface InitializeDataOptions { +export interface InitializeDataBatch { + entries: SqliteRow[]; entity: storage.SourceTable; - entry_consumer: (batch: SqliteRow[]) => {}; - abort_signal: AbortSignal; + isLast: boolean; + fromLSN: string; +} + +export interface InitializeDataOptions { + entities: storage.SourceTable[]; + entryConsumer: (batch: InitializeDataBatch) => {}; + abortSignal: AbortSignal; } export interface StartReplicationOptions { entities: storage.SourceTable[]; - from_lsn: string; - change_listener: (change: storage.SaveOptions) => {}; - abort_signal: AbortSignal; + changeListener: (change: ReplicationUpdate) => {}; + abortSignal: AbortSignal; } + +export enum UpdateType { + INSERT = 'INSERT', + UPDATE = 'UPDATE', + DELETE = 'DELETE', + TRUNCATE = 'TRUNCATE', + SCHEMA_CHANGE = 'SCHEMA_CHANGE', + COMMIT = 'COMMIT' +} + +type ReplicationUpdate = { + type: UpdateType; + /** + * Descriptor of the data source entity that was updated + */ + entity: storage.SourceTable; + /** + * Present when the update is an insert, update or delete. Contains the changed values for adding to the bucket storage + */ + entry?: storage.SaveOptions; + /** + * Present when the update is a schema change. Describes the new data source entity + * TODO: Redefine PgRelation + */ + entityDescriptor?: PgRelation; + /** + * Present when the update is a commit. Contains the LSN of the commit + */ + lsn?: string; +}; From 038a9b1d3bfa263e6e81532e21faa0525a2752a2 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 10:15:55 +0200 Subject: [PATCH 041/298] add implementation for routeAPI --- .../src/auth/SupabaseKeyCollector.ts | 4 +- .../src/replication/PostgresSyncAPIAdapter.ts | 71 ++++++++++++++----- packages/service-core/src/api/RouteAPI.ts | 12 +--- .../src/routes/endpoints/admin.ts | 22 ------ 4 files changed, 57 insertions(+), 52 deletions(-) diff --git a/modules/module-postgres/src/auth/SupabaseKeyCollector.ts b/modules/module-postgres/src/auth/SupabaseKeyCollector.ts index cb59bc401..1cd66f215 100644 --- a/modules/module-postgres/src/auth/SupabaseKeyCollector.ts +++ b/modules/module-postgres/src/auth/SupabaseKeyCollector.ts @@ -1,6 +1,6 @@ -import * as jose from 'jose'; -import * as pgwire from '@powersync/service-jpgwire'; import { auth } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; +import * as jose from 'jose'; import { ResolvedConnectionConfig } from '../types/types.js'; import * as pgwire_utils from '../utils/pgwire_utils.js'; diff --git a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts index 957494225..54639905a 100644 --- a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts @@ -1,7 +1,7 @@ -import * as pgwire from '@powersync/service-jpgwire'; import { api, replication, storage } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; -import { DEFAULT_TAG, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; +import { DEFAULT_TAG, isJsonValue, SqlSyncRules, TablePattern, toSyncRulesValue } from '@powersync/service-sync-rules'; import { configFile, ConnectionStatusV2, @@ -11,11 +11,11 @@ import { TableInfo } from '@powersync/service-types'; -import * as pg_utils from '../utils/pgwire_utils.js'; import * as replication_utils from '../replication/replication-utils.js'; import { baseUri, ResolvedConnectionConfig } from '../types/types.js'; +import * as pg_utils from '../utils/pgwire_utils.js'; -export class PostgresSyncAPIAdapter implements api.SyncAPI { +export class PostgresSyncAPIAdapter implements api.RouteAPI { protected pool: pgwire.PgClient; // TODO this should probably be configurable one day @@ -69,16 +69,52 @@ export class PostgresSyncAPIAdapter implements api.SyncAPI { }; } - executeQuery(query: string, params: any[]): Promise { - throw new Error('Method not implemented.'); - } + async executeQuery(query: string, params: any[]): Promise { + if (!this.config.debug_api) { + return internal_routes.ExecuteSqlResponse.encode({ + results: { + columns: [], + rows: [] + }, + success: false, + error: 'SQL querying is not enabled' + }); + } - getDemoCredentials(): Promise { - throw new Error('Method not implemented.'); - } + try { + const result = await this.pool.query({ + statement: query, + params: params.map(pg_utils.autoParameter) + }); - getDiagnostics(): Promise<{ connected: boolean; errors?: Array<{ level: string; message: string }> }> { - throw new Error('Method not implemented.'); + return internal_routes.ExecuteSqlResponse.encode({ + success: true, + results: { + columns: result.columns.map((c) => c.name), + rows: result.rows.map((row) => { + return row.map((value) => { + const sqlValue = toSyncRulesValue(value); + if (typeof sqlValue == 'bigint') { + return Number(value); + } else if (isJsonValue(sqlValue)) { + return sqlValue; + } else { + return null; + } + }); + }) + } + }); + } catch (e) { + return internal_routes.ExecuteSqlResponse.encode({ + results: { + columns: [], + rows: [] + }, + success: false, + error: e.message + }); + } } async getDebugTablesInfo( @@ -248,8 +284,11 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, throw new Error(`Could not determine replication lag for slot ${slotName}`); } - getCheckpoint(): Promise { - throw new Error('Method not implemented.'); + async getReplicationHead(): Promise { + const [{ lsn }] = pgwire.pgwireRows( + await pg_utils.retriedQuery(this.pool, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) + ); + return String(lsn); } async getConnectionSchema(): Promise { @@ -328,8 +367,4 @@ GROUP BY schemaname, tablename, quoted_name` return Object.values(schemas); } - - executeSQL(sql: string, params: any[]): Promise { - throw new Error('Method not implemented.'); - } } diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index 63dccac56..6d37f48d2 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -36,9 +36,9 @@ export interface RouteAPI { getReplicationLag(slotName: string): Promise; /** - * Get the current LSN or equivalent replication position identifier + * Get the current LSN or equivalent replication HEAD position identifier */ - getCheckpoint(): Promise; + getReplicationHead(): Promise; /** * @returns The schema for tables inside the connected database. This is typically @@ -55,14 +55,6 @@ export interface RouteAPI { */ executeQuery(query: string, params: any[]): Promise; - /** - * The management service and SDK expose a demo credentials endpoint. - * Not sure if this is actually used. - */ - getDemoCredentials(): Promise; - - //CRUD API : I don't think this is used besides maybe niche dev use cases - /** * Close any resources that need graceful termination. */ diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index ed9aa1221..eff30186c 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -8,28 +8,6 @@ import { PersistedSyncRulesContent } from '../../storage/BucketStorage.js'; import { authApi } from '../auth.js'; import { routeDefinition } from '../router.js'; -const demoCredentials = routeDefinition({ - path: '/api/admin/v1/demo-credentials', - method: router.HTTPMethod.POST, - authorize: authApi, - validator: schema.createTsCodecValidator(internal_routes.DemoCredentialsRequest, { - allowAdditional: true - }), - handler: async (payload) => { - // TODO is this used? - // const connection = payload.context.system.config.connection; - // if (connection == null || !connection.demo_database) { - // return internal_routes.DemoCredentialsResponse.encode({}); - // } - // const uri = util.buildDemoPgUri(connection); - // return internal_routes.DemoCredentialsResponse.encode({ - // credentials: { - // postgres_uri: uri - // } - // }); - } -}); - export const executeSql = routeDefinition({ path: '/api/admin/v1/execute-sql', method: router.HTTPMethod.POST, From 988885fcd4a604bd03856e37556979ea51452fc3 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 10:21:40 +0200 Subject: [PATCH 042/298] cleanup routes --- packages/service-core/src/routes/endpoints/admin.ts | 2 +- packages/service-core/src/routes/endpoints/checkpointing.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index eff30186c..fa0fa9702 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -203,4 +203,4 @@ function mapColumnValue(value: SqliteValue) { } } -export const ADMIN_ROUTES = [demoCredentials, executeSql, diagnostics, getSchema, reprocess, validate]; +export const ADMIN_ROUTES = [executeSql, diagnostics, getSchema, reprocess, validate]; diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index 9f2a50df3..36cbb7d5b 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -24,7 +24,7 @@ export const writeCheckpoint = routeDefinition({ // Since we don't use LSNs anymore, the only way to get that is to wait. const start = Date.now(); - const head = String(await api.getCheckpoint()); + const head = await api.getReplicationHead(); const timeout = 50_000; @@ -59,10 +59,10 @@ export const writeCheckpoint2 = routeDefinition({ } // Might want to call this something link replicationHead or something else - const currentCheckpoint = await api.getCheckpoint(); + const currentCheckpoint = await api.getReplicationHead(); const { storage } = service_context; - const id = await storage.createWriteCheckpoint(user_id!, { '1': String(currentCheckpoint) }); + const id = await storage.createWriteCheckpoint(user_id!, { '1': currentCheckpoint }); logger.info(`Write checkpoint 2: ${JSON.stringify({ currentCheckpoint, id: String(id) })}`); return { From 7877c643b6605165d7a023b6f5402c8136950873 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 11:50:47 +0200 Subject: [PATCH 043/298] neaten lifecycle of modulemanager and servicd context --- modules/module-postgres/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index 8e11bc774..f48de53a0 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -3,7 +3,7 @@ "repository": "https://github.com/powersync-ja/powersync-service", "types": "dist/index.d.ts", "publishConfig": { - "access": "public" + "access": "restricted" }, "version": "0.0.1", "main": "dist/index.js", From 895e4a88366609c35b2a87f6ab66c3f43d7143a9 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 11:51:09 +0200 Subject: [PATCH 044/298] neaten lifecycles --- packages/service-core/src/entry/cli-entry.ts | 10 +- .../src/entry/commands/compact-action.ts | 12 +- .../src/entry/commands/migrate-action.ts | 13 +- .../src/entry/commands/start-action.ts | 9 +- .../db/migrations/1684951997326-init.ts | 13 +- .../1702295701188-sync-rule-state.ts | 13 +- .../1711543888062-write-checkpoint-index.ts | 12 +- .../service-core/src/migrations/migrations.ts | 10 +- .../src/modules/AbstractModule.ts | 7 + .../service-core/src/modules/ModuleManager.ts | 17 ++- .../service-core/src/routes/RouterEngine.ts | 66 ++++----- .../service-core/src/system/ServiceContext.ts | 39 +++--- .../service-core/src/util/config/types.ts | 2 +- service/src/entry.ts | 9 +- service/src/routes/router-config.ts | 128 ------------------ service/src/runners/server.ts | 59 +++++--- service/src/runners/stream-worker.ts | 2 +- 17 files changed, 167 insertions(+), 254 deletions(-) delete mode 100644 service/src/routes/router-config.ts diff --git a/packages/service-core/src/entry/cli-entry.ts b/packages/service-core/src/entry/cli-entry.ts index 0c92e44fb..aebf4eb5d 100644 --- a/packages/service-core/src/entry/cli-entry.ts +++ b/packages/service-core/src/entry/cli-entry.ts @@ -1,7 +1,7 @@ import { Command } from 'commander'; import { logger } from '@powersync/lib-services-framework'; -import * as system from '../system/system-index.js'; +import * as modules from '../modules/modules-index.js'; import * as utils from '../util/util-index.js'; import { registerMigrationAction } from './commands/migrate-action.js'; import { registerTearDownAction } from './commands/teardown-action.js'; @@ -14,18 +14,18 @@ import { registerCompactAction, registerStartAction } from './entry-index.js'; * Optionally registers the start command handlers. */ export function generateEntryProgram( - serviceContext: system.ServiceContext, + moduleManager: modules.ModuleManager, startHandlers?: Record ) { const entryProgram = new Command(); entryProgram.name('powersync-runner').description('CLI to initiate a PowerSync service runner'); registerTearDownAction(entryProgram); - registerMigrationAction(entryProgram); - registerCompactAction(entryProgram); + registerMigrationAction(entryProgram, moduleManager); + registerCompactAction(entryProgram, moduleManager); if (startHandlers) { - registerStartAction(entryProgram, serviceContext, startHandlers); + registerStartAction(entryProgram, moduleManager, startHandlers); } return { diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index 2fff227b3..74f80fcec 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -2,8 +2,8 @@ import { Command } from 'commander'; import { logger } from '@powersync/lib-services-framework'; import * as v8 from 'v8'; +import * as modules from '../../modules/modules-index.js'; import { createPowerSyncMongo, MongoBucketStorage } from '../../storage/storage-index.js'; -import { loadConfig } from '../../util/config.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'compact'; @@ -20,7 +20,7 @@ const HEAP_LIMIT = v8.getHeapStatistics().heap_size_limit; */ const COMPACT_MEMORY_LIMIT_MB = Math.min(HEAP_LIMIT / 1024 / 1024 - 128, 1024); -export function registerCompactAction(program: Command) { +export function registerCompactAction(program: Command, moduleManager: modules.ModuleManager) { const compactCommand = program.command(COMMAND_NAME); wrapConfigCommand(compactCommand); @@ -28,13 +28,15 @@ export function registerCompactAction(program: Command) { return compactCommand.description('Compact storage').action(async (options) => { const runnerConfig = extractRunnerOptions(options); - const config = await loadConfig(runnerConfig); - const { storage } = config; + await moduleManager.initialize(runnerConfig); + + const { configuration } = moduleManager.serviceContext; + const { storage } = configuration; const psdb = createPowerSyncMongo(storage); const client = psdb.client; await client.connect(); try { - const bucketStorage = new MongoBucketStorage(psdb, { slot_name_prefix: config.slot_name_prefix }); + const bucketStorage = new MongoBucketStorage(psdb, { slot_name_prefix: configuration.slot_name_prefix }); const active = await bucketStorage.getActiveSyncRules(); if (active == null) { logger.info('No active instance to compact'); diff --git a/packages/service-core/src/entry/commands/migrate-action.ts b/packages/service-core/src/entry/commands/migrate-action.ts index 2347e63d3..92dc89df7 100644 --- a/packages/service-core/src/entry/commands/migrate-action.ts +++ b/packages/service-core/src/entry/commands/migrate-action.ts @@ -1,13 +1,14 @@ +import { logger } from '@powersync/lib-services-framework'; import { Command } from 'commander'; -import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; -import { migrate } from '../../migrations/migrations.js'; import { Direction } from '../../migrations/definitions.js'; -import { logger } from '@powersync/lib-services-framework'; +import { migrate } from '../../migrations/migrations.js'; +import * as modules from '../../modules/modules-index.js'; +import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'migrate'; -export function registerMigrationAction(program: Command) { +export function registerMigrationAction(program: Command, moduleManager: modules.ModuleManager) { const migrationCommand = program.command(COMMAND_NAME); wrapConfigCommand(migrationCommand); @@ -16,12 +17,12 @@ export function registerMigrationAction(program: Command) { .description('Run migrations') .argument('', 'Migration direction. `up` or `down`') .action(async (direction: Direction, options) => { - const runnerConfig = extractRunnerOptions(options); + await moduleManager.initialize(extractRunnerOptions(options)); try { await migrate({ direction, - runner_config: runnerConfig + service_context: moduleManager.serviceContext }); process.exit(0); diff --git a/packages/service-core/src/entry/commands/start-action.ts b/packages/service-core/src/entry/commands/start-action.ts index 8963b45d9..db40c55b6 100644 --- a/packages/service-core/src/entry/commands/start-action.ts +++ b/packages/service-core/src/entry/commands/start-action.ts @@ -1,6 +1,6 @@ import { Command } from 'commander'; -import * as system from '../../system/system-index.js'; +import * as modules from '../../modules/modules-index.js'; import * as utils from '../../util/util-index.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; @@ -8,7 +8,7 @@ const COMMAND_NAME = 'start'; export function registerStartAction( program: Command, - serviceContext: system.ServiceContext, + moduleManager: modules.ModuleManager, handlers: Record ) { const startCommand = program.command(COMMAND_NAME); @@ -23,10 +23,9 @@ export function registerStartAction( utils.env.PS_RUNNER_TYPE ) .action(async (options) => { - // load the config - await serviceContext.initialize(extractRunnerOptions(options)); + await moduleManager.initialize(extractRunnerOptions(options)); const runner = handlers[options.runnerType as utils.ServiceRunner]; - await runner(serviceContext); + await runner(moduleManager.serviceContext); }); } diff --git a/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts b/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts index e41adf260..4f1fff58a 100644 --- a/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts +++ b/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts @@ -2,9 +2,10 @@ import * as mongo from '../../../db/mongo.js'; import * as storage from '../../../storage/storage-index.js'; import * as utils from '../../../util/util-index.js'; -export const up = async (context?: utils.MigrationContext) => { - const config = await utils.loadConfig(context?.runner_config); - const database = storage.createPowerSyncMongo(config.storage); +export const up = async (context: utils.MigrationContext) => { + const { service_context } = context; + + const database = storage.createPowerSyncMongo(service_context.configuration.storage); await mongo.waitForAuth(database.db); try { await database.bucket_parameters.createIndex( @@ -20,9 +21,9 @@ export const up = async (context?: utils.MigrationContext) => { } }; -export const down = async (context?: utils.MigrationContext) => { - const config = await utils.loadConfig(context?.runner_config); - const database = storage.createPowerSyncMongo(config.storage); +export const down = async (context: utils.MigrationContext) => { + const { service_context } = context; + const database = storage.createPowerSyncMongo(service_context.configuration.storage); try { if (await database.bucket_parameters.indexExists('lookup')) { await database.bucket_parameters.dropIndex('lookup1'); diff --git a/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts b/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts index 4a802e7de..a23b47e68 100644 --- a/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +++ b/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts @@ -23,9 +23,10 @@ interface LegacySyncRulesDocument extends storage.SyncRuleDocument { auto_activate?: boolean; } -export const up = async (context?: utils.MigrationContext) => { - const config = await utils.loadConfig(context?.runner_config); - const db = storage.createPowerSyncMongo(config.storage); +export const up = async (context: utils.MigrationContext) => { + const { service_context } = context; + const db = storage.createPowerSyncMongo(service_context.configuration.storage); + await mongo.waitForAuth(db.db); try { // We keep the old flags for existing deployments still shutting down. @@ -68,10 +69,10 @@ export const up = async (context?: utils.MigrationContext) => { } }; -export const down = async (context?: utils.MigrationContext) => { - const config = await utils.loadConfig(context?.runner_config); +export const down = async (context: utils.MigrationContext) => { + const { service_context } = context; - const db = storage.createPowerSyncMongo(config.storage); + const db = storage.createPowerSyncMongo(service_context.configuration.storage); try { await db.sync_rules.updateMany( { diff --git a/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts b/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts index a73f0c7d7..30ecbbcc3 100644 --- a/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +++ b/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts @@ -1,9 +1,9 @@ import * as storage from '../../../storage/storage-index.js'; import * as utils from '../../../util/util-index.js'; -export const up = async (context?: utils.MigrationContext) => { - const config = await utils.loadConfig(context?.runner_config); - const db = storage.createPowerSyncMongo(config.storage); +export const up = async (context: utils.MigrationContext) => { + const { service_context } = context; + const db = storage.createPowerSyncMongo(service_context.configuration.storage); try { await db.write_checkpoints.createIndex( @@ -17,10 +17,10 @@ export const up = async (context?: utils.MigrationContext) => { } }; -export const down = async (context?: utils.MigrationContext) => { - const config = await utils.loadConfig(context?.runner_config); +export const down = async (context: utils.MigrationContext) => { + const { service_context } = context; - const db = storage.createPowerSyncMongo(config.storage); + const db = storage.createPowerSyncMongo(service_context.configuration.storage); try { if (await db.write_checkpoints.indexExists('user_id')) { diff --git a/packages/service-core/src/migrations/migrations.ts b/packages/service-core/src/migrations/migrations.ts index dd4917fb0..7dc45939a 100644 --- a/packages/service-core/src/migrations/migrations.ts +++ b/packages/service-core/src/migrations/migrations.ts @@ -21,7 +21,7 @@ const MIGRATIONS_DIR = path.join(__dirname, '/db/migrations'); export type MigrationOptions = { direction: Direction; - serviceContext: system.ServiceContext; + service_context: system.ServiceContext; }; /** @@ -35,7 +35,7 @@ const loadMigrations = async (dir: string, serviceContext: system.ServiceContext }); const context: util.MigrationContext = { - serviceContext + service_context: serviceContext }; return await Promise.all( @@ -54,14 +54,14 @@ const loadMigrations = async (dir: string, serviceContext: system.ServiceContext * Runs migration scripts exclusively using Mongo locks */ export const migrate = async (options: MigrationOptions) => { - const { direction, serviceContext } = options; + const { direction, service_context } = options; /** * Try and get Mongo from config file. * But this might not be available in Journey Micro as we use the standard Mongo. */ - const { configuration } = serviceContext; + const { configuration } = service_context; const { storage } = configuration; const client = db.mongo.createMongoClient(storage); @@ -97,7 +97,7 @@ export const migrate = async (options: MigrationOptions) => { try { logger.info('Loading migrations'); - const migrations = await loadMigrations(MIGRATIONS_DIR, serviceContext); + const migrations = await loadMigrations(MIGRATIONS_DIR, service_context); // Use the provided config to connect to Mongo const store = createMongoMigrationStore(clientDB); diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index 554c866bd..060e0229b 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -13,6 +13,13 @@ export abstract class AbstractModule { this.name = options.name; } + /** + * Register any functionality on the {@link ServiceContext}. + * Note this will be executed before the ServiceContext has been initialized. + * This can be used to register storage providers which will be created during initialization. + */ + public abstract register(context: ServiceContext): Promise; + /** * Initialize the module using any required services from the ServiceContext */ diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index 72a0c4921..7b1563afd 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -1,13 +1,20 @@ import { logger } from '@powersync/lib-services-framework'; -import { AbstractModule } from './AbstractModule.js'; import { ServiceContext } from '../system/ServiceContext.js'; +import { RunnerConfig } from '../util/util-index.js'; +import { AbstractModule } from './AbstractModule.js'; /** * The module manager is responsible for managing the lifecycle of all modules in the system. */ export class ModuleManager { + readonly serviceContext: ServiceContext; + private readonly modules: Map = new Map(); + constructor(serviceContext?: ServiceContext) { + this.serviceContext = serviceContext ?? new ServiceContext(); + } + public register(modules: AbstractModule[]) { for (const module of modules) { if (this.modules.has(module.name)) { @@ -15,12 +22,16 @@ export class ModuleManager { } else { this.modules.set(module.name, module); } + // Let the module register functionality on the service context + module.register(this.serviceContext); } } - async initialize(context: ServiceContext) { + async initialize(entryConfig: RunnerConfig) { + await this.serviceContext.initialize(entryConfig); + for (const module of this.modules.values()) { - await module.initialize(context); + await module.initialize(this.serviceContext); } } diff --git a/packages/service-core/src/routes/RouterEngine.ts b/packages/service-core/src/routes/RouterEngine.ts index 9749d4382..affa39dea 100644 --- a/packages/service-core/src/routes/RouterEngine.ts +++ b/packages/service-core/src/routes/RouterEngine.ts @@ -1,4 +1,4 @@ -import { container, logger } from '@powersync/lib-services-framework'; +import { logger } from '@powersync/lib-services-framework'; import * as api from '../api/api-index.js'; @@ -13,9 +13,9 @@ export type RouterSetupResponse = { }; export type RouterEngineRoutes = { - apiRoutes: RouteDefinition[]; - streamRoutes: RouteDefinition[]; - socketRoutes: RouteDefinition[]; + api_routes: RouteDefinition[]; + stream_routes: RouteDefinition[]; + socket_routes: RouteDefinition[]; }; export type RouterSetup = (routes: RouterEngineRoutes) => Promise; @@ -26,45 +26,31 @@ export type RouterSetup = (routes: RouterEngineRoutes) => Promise void>; /** - * The reference itself is readonly, but users should eventually - * be able to add their own route definitions. + * A final cleanup handler to be executed after all stopHandlers */ - readonly routes: RouterEngineRoutes; + protected cleanupHandler: (() => Promise) | null; - protected stopHandlers: Set<() => void>; private api: api.RouteAPI | null; constructor() { this.api = null; this.stopHandlers = new Set(); + this.cleanupHandler = null; this.closed = false; // Default routes this.routes = { - apiRoutes: [...ADMIN_ROUTES, ...CHECKPOINT_ROUTES, ...SYNC_RULES_ROUTES], - streamRoutes: [...SYNC_STREAM_ROUTES], - socketRoutes: [ + api_routes: [...ADMIN_ROUTES, ...CHECKPOINT_ROUTES, ...SYNC_RULES_ROUTES], + stream_routes: [...SYNC_STREAM_ROUTES], + socket_routes: [ // TODO ] }; - - /** - * This adds a termination handler to the begining of the queue - * A server termination handler should be added to run after this one with - * `handleTerminationSignalLast` - */ - container.terminationHandler.handleTerminationSignal(async () => { - // Close open streams, so that they don't block the server from closing. - // Note: This does not work well when streaming requests are queued. In that case, the server still doesn't - // close in the 30-second timeout. - this.closed = true; - logger.info(`Closing ${this.stopHandlers.size} streams`); - for (let handler of this.stopHandlers) { - handler(); - } - }); } public registerAPI(api: api.RouteAPI) { @@ -84,13 +70,31 @@ export class RouterEngine { */ async start(setup: RouterSetup) { const { onShutdown } = await setup(this.routes); + this.cleanupHandler = onShutdown; + } + + /** + * Runs all stop handlers then final cleanup. + */ + async shutdown() { + // Close open streams, so that they don't block the server from closing. + // Note: This does not work well when streaming requests are queued. In that case, the server still doesn't + // close in the 30-second timeout. + this.closed = true; + + logger.info(`Closing ${this.stopHandlers.size} streams`); + for (let handler of this.stopHandlers) { + handler(); + } - // This will cause the router shutdown to occur after the stop handlers have completed - container.terminationHandler.handleTerminationSignalLast(async () => { - await onShutdown(); - }); + logger.info(`Running close cleanup`); + await this.cleanupHandler?.(); } + /** + * Add a stop handler callback to be executed when the router engine is being + * shutdown. + */ addStopHandler(handler: () => void): () => void { if (this.closed) { handler(); diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index bdee334e8..adebecf34 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -17,23 +17,10 @@ export class ServiceContext extends LifeCycledSystem { private _configuration: ResolvedPowerSyncConfig | null; protected storageProviders: Map; - + routerEngine: RouterEngine; + configCollector: CompoundConfigCollector; // TODO metrics - constructor() { - super(); - - this._storage = null; - this._configuration = null; - - this.storageProviders = new Map(); - container.register(RouterEngine, new RouterEngine()); - container.register(CompoundConfigCollector, new CompoundConfigCollector()); - - // Mongo storage is available as an option by default - this.registerStorageProvider(new MongoStorageProvider()); - } - get configuration(): ResolvedPowerSyncConfig { if (!this._configuration) { throw new Error(`Attempt to use configuration before it has been collected`); @@ -49,15 +36,27 @@ export class ServiceContext extends LifeCycledSystem { } get replicationEngine(): ReplicationEngine { + // TODO clean this up return container.getImplementation(ReplicationEngine); } - get routerEngine(): RouterEngine { - return container.getImplementation(RouterEngine); - } + constructor() { + super(); + + // These will only be set once `initialize` has been called + this._storage = null; + this._configuration = null; - get configCollector(): CompoundConfigCollector { - return container.getImplementation(CompoundConfigCollector); + this.storageProviders = new Map(); + // Mongo storage is available as an option by default + this.registerStorageProvider(new MongoStorageProvider()); + + this.configCollector = new CompoundConfigCollector(); + + this.routerEngine = new RouterEngine(); + this.withLifecycle(this.routerEngine, { + stop: (routerEngine) => routerEngine.shutdown() + }); } /** diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index 951ffa2e2..b98709b76 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -16,7 +16,7 @@ export type RunnerConfig = { }; export type MigrationContext = { - serviceContext: ServiceContext; + service_context: ServiceContext; }; export type Runner = (serviceContext: ServiceContext) => Promise; diff --git a/service/src/entry.ts b/service/src/entry.ts index 1485810ea..7a6f810b6 100644 --- a/service/src/entry.ts +++ b/service/src/entry.ts @@ -1,5 +1,5 @@ import { container, ContainerImplementation } from '@powersync/lib-services-framework'; -import { entry, modules, system, utils } from '@powersync/service-core'; +import { entry, modules, utils } from '@powersync/service-core'; import PostgresModule from '@powersync/service-module-postgres'; import { startServer } from './runners/server.js'; import { startStreamWorker } from './runners/stream-worker.js'; @@ -9,16 +9,11 @@ import { createSentryReporter } from './util/alerting.js'; container.registerDefaults(); container.register(ContainerImplementation.REPORTER, createSentryReporter()); -const serviceContext = new system.ServiceContext(); const moduleManager = new modules.ModuleManager(); - moduleManager.register([PostgresModule]); -// TODO this can't happen here. The service context needs to be initialized first -await moduleManager.initialize(serviceContext); - // Generate Commander CLI entry point program -const { execute } = entry.generateEntryProgram(serviceContext, { +const { execute } = entry.generateEntryProgram(moduleManager, { [utils.ServiceRunner.API]: startServer, [utils.ServiceRunner.SYNC]: startStreamWorker, [utils.ServiceRunner.UNIFIED]: async (serviceContext) => { diff --git a/service/src/routes/router-config.ts b/service/src/routes/router-config.ts deleted file mode 100644 index fa709a03e..000000000 --- a/service/src/routes/router-config.ts +++ /dev/null @@ -1,128 +0,0 @@ -import cors from '@fastify/cors'; -import { container, errors, logger } from '@powersync/lib-services-framework'; -import * as core from '@powersync/service-core'; -import { RSocketRequestMeta } from '@powersync/service-rsocket-router'; -import { deserialize } from 'bson'; -import fastify from 'fastify'; -import { SocketRouter } from './router.js'; - -export const routerSetup: core.routes.RouterSetup = async (routes) => { - const serviceContext = container.getImplementation(core.system.ServiceContext); - - const server = fastify.fastify(); - - /** - * Fastify creates an encapsulated context for each `.register` call. - * Creating a separate context here to separate the concurrency limits for Admin APIs - * and Sync Streaming routes. - * https://github.com/fastify/fastify/blob/main/docs/Reference/Encapsulation.md - */ - server.register(async function (childContext) { - core.routes.registerFastifyRoutes( - childContext, - async () => { - return { - user_id: undefined, - // Put here for convenience - service_context: serviceContext - }; - }, - [...routes.apiRoutes] - ); - // Limit the active concurrent requests - childContext.addHook( - 'onRequest', - core.routes.hooks.createRequestQueueHook({ - max_queue_depth: 20, - concurrency: 10 - }) - ); - }); - - // Create a separate context for concurrency queueing - server.register(async function (childContext) { - core.routes.registerFastifyRoutes( - childContext, - async () => { - return { - user_id: undefined, - // Put here for convenience - service_context: container.getImplementation(core.system.ServiceContext) - }; - }, - [...routes.streamRoutes] - ); - // Limit the active concurrent requests - childContext.addHook( - 'onRequest', - core.routes.hooks.createRequestQueueHook({ - max_queue_depth: 0, - concurrency: 200 - }) - ); - }); - - server.register(cors, { - origin: '*', - allowedHeaders: ['Content-Type', 'Authorization'], - exposedHeaders: ['Content-Type'], - // Cache time for preflight response - maxAge: 3600 - }); - - SocketRouter.applyWebSocketEndpoints(server.server, { - contextProvider: async (data: Buffer) => { - const { token } = core.routes.RSocketContextMeta.decode(deserialize(data) as any); - - if (!token) { - throw new errors.ValidationError('No token provided in context'); - } - - const serviceContext = container.getImplementation(core.system.ServiceContext); - - try { - const extracted_token = core.routes.auth.getTokenFromHeader(token); - if (extracted_token != null) { - const { context, errors } = await core.routes.auth.generateContext(extracted_token); - return { - token, - ...context, - token_errors: errors, - // Put here for convenience - service_context: serviceContext - }; - } - } catch (ex) { - logger.error(ex); - } - - return { - token, - service_context: serviceContext - }; - }, - // TODO - endpoints: [core.routes.endpoints.syncStreamReactive(SocketRouter)], - metaDecoder: async (meta: Buffer) => { - return RSocketRequestMeta.decode(deserialize(meta) as any); - }, - payloadDecoder: async (rawData?: Buffer) => rawData && deserialize(rawData) - }); - - const { port } = serviceContext.configuration; - - await server.listen({ - host: '0.0.0.0', - port - }); - - logger.info(`Running on port ${port}`); - - return { - onShutdown: async () => { - logger.info('Shutting down HTTP server...'); - await server.close(); - logger.info('HTTP server stopped'); - } - }; -}; diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 17f67aec1..10ddc5231 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -1,11 +1,11 @@ -import * as core from '@powersync/service-core'; -import { routerSetup } from '../routes/router-config.js'; - import cors from '@fastify/cors'; -import { container, logger } from '@powersync/lib-services-framework'; import fastify from 'fastify'; +import { container, logger } from '@powersync/lib-services-framework'; +import * as core from '@powersync/service-core'; + import { SocketRouter } from '../routes/router.js'; + /** * Starts an API server */ @@ -26,27 +26,48 @@ export async function startServer(serviceContext: core.system.ServiceContext) { } }); - const server = fastify.fastify(); - - server.register(cors, { - origin: '*', - allowedHeaders: ['Content-Type', 'Authorization'], - exposedHeaders: ['Content-Type'], - // Cache time for preflight response - maxAge: 3600 - }); - - core.routes.configureFastifyServer(server, { service_context: serviceContext }); - core.routes.configureRSocket(SocketRouter, { server: server.server, service_context: serviceContext }); - logger.info('Starting service'); + // TODO cleanup the initialization of metrics await serviceContext.start(); core.Metrics.getInstance().configureApiMetrics(); - // Start the router - await serviceContext.routerEngine.start(routerSetup); + await serviceContext.routerEngine.start(async (routes) => { + const server = fastify.fastify(); + + server.register(cors, { + origin: '*', + allowedHeaders: ['Content-Type', 'Authorization'], + exposedHeaders: ['Content-Type'], + // Cache time for preflight response + maxAge: 3600 + }); + + core.routes.configureFastifyServer(server, { + service_context: serviceContext, + routes: { api: { routes: routes.api_routes }, sync_stream: { routes: routes.stream_routes } } + }); + + core.routes.configureRSocket(SocketRouter, { server: server.server, service_context: serviceContext }); + + const { port } = serviceContext.configuration; + + await server.listen({ + host: '0.0.0.0', + port + }); + + logger.info(`Running on port ${port}`); + + return { + onShutdown: async () => { + logger.info('Shutting down HTTP server...'); + await server.close(); + logger.info('HTTP server stopped'); + } + }; + }); logger.info('service started'); diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index 3887bb6ff..1f9e4b77f 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -8,7 +8,7 @@ export async function startStreamWorker(serviceContext: system.ServiceContext) { if (!serviceContext.configuration.migrations?.disable_auto_migration) { await migrations.migrate({ direction: migrations.Direction.Up, - serviceContext + service_context: serviceContext }); } From 405deacd09e5439c642696f3cbedf25a8b98f936 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 13:11:36 +0200 Subject: [PATCH 045/298] comment --- packages/service-core/src/system/ServiceContext.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index adebecf34..25843c980 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -104,6 +104,7 @@ export class ServiceContext extends LifeCycledSystem { stop: () => disposer() }); + // TODO neaten this container.register( ReplicationEngine, new ReplicationEngine({ From 1ac8856dd794307289d2917b8a52d6da7644f5ae Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 13:24:39 +0200 Subject: [PATCH 046/298] move wal files out of core --- .../src/{replication => api}/PostgresSyncAPIAdapter.ts | 0 .../module-postgres}/src/replication/WalConnection.ts | 0 .../module-postgres}/src/replication/WalStream.ts | 0 .../module-postgres}/src/replication/WalStreamManager.ts | 0 .../module-postgres}/src/replication/WalStreamRunner.ts | 0 .../module-postgres/src/replication/replication-index.ts | 6 ++++++ .../service-core/src/replication/replication-index.ts | 8 +------- 7 files changed, 7 insertions(+), 7 deletions(-) rename modules/module-postgres/src/{replication => api}/PostgresSyncAPIAdapter.ts (100%) rename {packages/service-core => modules/module-postgres}/src/replication/WalConnection.ts (100%) rename {packages/service-core => modules/module-postgres}/src/replication/WalStream.ts (100%) rename {packages/service-core => modules/module-postgres}/src/replication/WalStreamManager.ts (100%) rename {packages/service-core => modules/module-postgres}/src/replication/WalStreamRunner.ts (100%) create mode 100644 modules/module-postgres/src/replication/replication-index.ts diff --git a/modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts similarity index 100% rename from modules/module-postgres/src/replication/PostgresSyncAPIAdapter.ts rename to modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts diff --git a/packages/service-core/src/replication/WalConnection.ts b/modules/module-postgres/src/replication/WalConnection.ts similarity index 100% rename from packages/service-core/src/replication/WalConnection.ts rename to modules/module-postgres/src/replication/WalConnection.ts diff --git a/packages/service-core/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts similarity index 100% rename from packages/service-core/src/replication/WalStream.ts rename to modules/module-postgres/src/replication/WalStream.ts diff --git a/packages/service-core/src/replication/WalStreamManager.ts b/modules/module-postgres/src/replication/WalStreamManager.ts similarity index 100% rename from packages/service-core/src/replication/WalStreamManager.ts rename to modules/module-postgres/src/replication/WalStreamManager.ts diff --git a/packages/service-core/src/replication/WalStreamRunner.ts b/modules/module-postgres/src/replication/WalStreamRunner.ts similarity index 100% rename from packages/service-core/src/replication/WalStreamRunner.ts rename to modules/module-postgres/src/replication/WalStreamRunner.ts diff --git a/modules/module-postgres/src/replication/replication-index.ts b/modules/module-postgres/src/replication/replication-index.ts new file mode 100644 index 000000000..150898d72 --- /dev/null +++ b/modules/module-postgres/src/replication/replication-index.ts @@ -0,0 +1,6 @@ +export * from './PgRelation.js'; +export * from './replication-utils.js'; +export * from './WalConnection.js'; +export * from './WalStream.js'; +export * from './WalStreamManager.js'; +export * from './WalStreamRunner.js'; diff --git a/packages/service-core/src/replication/replication-index.ts b/packages/service-core/src/replication/replication-index.ts index 603327fc2..7f4cf0983 100644 --- a/packages/service-core/src/replication/replication-index.ts +++ b/packages/service-core/src/replication/replication-index.ts @@ -1,13 +1,7 @@ export * from './ErrorRateLimiter.js'; -export * from './PgRelation.js'; -export * from './util.js'; -export * from './WalConnection.js'; -export * from './WalStream.js'; -export * from './WalStreamManager.js'; -export * from './WalStreamRunner.js'; export * from './core/ReplicationAdapter.js'; export * from './core/ReplicationEngine.js'; export * from './core/ReplicationEntity.js'; -export * from './core/Replicator.js'; export * from './core/ReplicationModule.js'; +export * from './core/Replicator.js'; From c78cdfdf7897017341de381c740ff080749ab230 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 13:50:37 +0200 Subject: [PATCH 047/298] cleanup --- .../src/api/PostgresSyncAPIAdapter.ts | 11 +- .../src/module/PostgresModule.ts | 7 +- .../replication/PostgresReplicationAdapter.ts | 21 +- .../src/replication/WalConnection.ts | 357 +++-- .../src/replication/WalStream.ts | 1248 ++++++++--------- .../src/replication/WalStreamManager.ts | 426 +++--- .../src/replication/WalStreamRunner.ts | 360 ++--- modules/module-postgres/src/types/types.ts | 2 +- .../module-postgres/src/utils/pgwire_utils.ts | 4 +- modules/module-postgres/src/utils/utils.ts | 55 - packages/service-core/src/api/RouteAPI.ts | 21 +- packages/service-core/src/api/diagnostics.ts | 7 +- packages/service-core/src/api/schema.ts | 11 +- packages/service-core/src/entry/cli-entry.ts | 3 +- .../src/entry/commands/compact-action.ts | 8 +- .../src/entry/commands/migrate-action.ts | 7 +- .../service-core/src/modules/ModuleManager.ts | 1 + .../src/routes/endpoints/admin.ts | 29 +- .../src/routes/endpoints/sync-rules.ts | 4 +- .../src/routes/endpoints/sync-stream.ts | 3 +- .../src/storage/mongo/MongoBucketBatch.ts | 4 +- packages/service-core/src/sync/sync.ts | 3 +- packages/service-core/src/sync/util.ts | 1 - 23 files changed, 1255 insertions(+), 1338 deletions(-) delete mode 100644 modules/module-postgres/src/utils/utils.ts diff --git a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts index 54639905a..1f04d7d90 100644 --- a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts @@ -1,4 +1,4 @@ -import { api, replication, storage } from '@powersync/service-core'; +import { api, storage } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import { DEFAULT_TAG, isJsonValue, SqlSyncRules, TablePattern, toSyncRulesValue } from '@powersync/service-sync-rules'; @@ -117,16 +117,13 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { } } - async getDebugTablesInfo( - tablePatterns: TablePattern[], - sqlSyncRules: SqlSyncRules - ): Promise { - let result: replication.PatternResult[] = []; + async getDebugTablesInfo(tablePatterns: TablePattern[], sqlSyncRules: SqlSyncRules): Promise { + let result: api.PatternResult[] = []; for (let tablePattern of tablePatterns) { const schema = tablePattern.schema; - let patternResult: replication.PatternResult = { + let patternResult: api.PatternResult = { schema: schema, pattern: tablePattern.tablePattern, wildcard: tablePattern.isWildcard diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 5021c079e..f1414b8e0 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -2,8 +2,9 @@ import * as t from 'ts-codec'; import { api, replication } from '@powersync/service-core'; +import { ServiceContext } from '@powersync/service-core/src/system/ServiceContext.js'; +import { PostgresSyncAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; -import { PostgresSyncAPIAdapter } from '../replication/PostgresSyncAPIAdapter.js'; import { normalizeConnectionConfig, PostgresConnectionConfig, ResolvedConnectionConfig } from '../types/types.js'; export class PostgresModule extends replication.ReplicationModule { @@ -14,12 +15,14 @@ export class PostgresModule extends replication.ReplicationModule { }); } + async register(context: ServiceContext): Promise {} + protected configSchema(): t.AnyCodec { // Intersection types have some limitations in codec typing return PostgresConnectionConfig; } - protected createSyncAPIAdapter(config: PostgresConnectionConfig): api.SyncAPI { + protected createSyncAPIAdapter(config: PostgresConnectionConfig): api.RouteAPI { throw new PostgresSyncAPIAdapter(this.resolveConfig(config)); } diff --git a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts index 91b3f1d1e..8e0e0f01b 100644 --- a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts +++ b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts @@ -1,6 +1,6 @@ -import { replication } from '@powersync/service-core'; -import * as sync_rules from '@powersync/service-sync-rules'; +import { replication, SourceTable } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; +import * as sync_rules from '@powersync/service-sync-rules'; import { ResolvedConnectionConfig } from '../types/types.js'; @@ -8,6 +8,15 @@ export type PostgresConnection = pgwire.PgClient; export class PostgresReplicationAdapter implements replication.ReplicationAdapter { constructor(protected config: ResolvedConnectionConfig) {} + resolveReplicationEntities(pattern: sync_rules.TablePattern): Promise { + throw new Error('Method not implemented.'); + } + count(entity: SourceTable): Promise { + throw new Error('Method not implemented.'); + } + initializeData(options: replication.InitializeDataOptions): Promise { + throw new Error('Method not implemented.'); + } name(): string { return 'postgres'; @@ -25,14 +34,6 @@ export class PostgresReplicationAdapter implements replication.ReplicationAdapte throw new Error('Method not implemented.'); } - count(entity: replication.ReplicationEntity): Promise { - throw new Error('Method not implemented.'); - } - - initializeData(options: replication.InitializeDataOptions): Promise { - throw new Error('Method not implemented.'); - } - startReplication(options: replication.StartReplicationOptions): Promise { throw new Error('Method not implemented.'); } diff --git a/modules/module-postgres/src/replication/WalConnection.ts b/modules/module-postgres/src/replication/WalConnection.ts index 559baad22..02e099d0f 100644 --- a/modules/module-postgres/src/replication/WalConnection.ts +++ b/modules/module-postgres/src/replication/WalConnection.ts @@ -1,184 +1,173 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import { pgwireRows } from '@powersync/service-jpgwire'; -import { DEFAULT_TAG, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; -import { ReplicationError, TableInfo } from '@powersync/service-types'; - -import * as storage from '../storage/storage-index.js'; -import * as util from '../util/util-index.js'; - -import { ReplicaIdentityResult, getReplicationIdentityColumns } from './util.js'; -/** - * Connection that _manages_ WAL, but does not do streaming. - */ -export class WalConnection { - db: pgwire.PgClient; - connectionTag = DEFAULT_TAG; - publication_name = 'powersync'; - - sync_rules: SqlSyncRules; - - /** - * db can be a PgConnection or PgPool. - * - * No transactions are used here, but it is up to the client to ensure - * nothing here is called in the middle of another transaction if - * PgConnection is used. - */ - constructor(options: { db: pgwire.PgClient; sync_rules: SqlSyncRules }) { - this.db = options.db; - this.sync_rules = options.sync_rules; - } - - async checkSourceConfiguration() { - await checkSourceConfiguration(this.db); - } - - async getDebugTableInfo(tablePattern: TablePattern, name: string, relationId: number | null): Promise { - const schema = tablePattern.schema; - let id_columns_result: ReplicaIdentityResult | undefined = undefined; - let id_columns_error = null; - - if (relationId != null) { - try { - id_columns_result = await getReplicationIdentityColumns(this.db, relationId); - } catch (e) { - id_columns_error = { level: 'fatal', message: e.message }; - } - } - - const id_columns = id_columns_result?.columns ?? []; - - const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); - - const syncData = this.sync_rules.tableSyncsData(sourceTable); - const syncParameters = this.sync_rules.tableSyncsParameters(sourceTable); - - if (relationId == null) { - return { - schema: schema, - name: name, - pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - replication_id: [], - data_queries: syncData, - parameter_queries: syncParameters, - // Also - errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] - }; - } - if (id_columns.length == 0 && id_columns_error == null) { - let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; - if (id_columns_result?.replicationIdentity == 'default') { - message += ' Configure a primary key on the table.'; - } - id_columns_error = { level: 'fatal', message }; - } - - let selectError = null; - try { - await util.retriedQuery(this.db, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); - } catch (e) { - selectError = { level: 'fatal', message: e.message }; - } - - let replicateError = null; - - const publications = await util.retriedQuery(this.db, { - statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, - params: [ - { type: 'varchar', value: this.publication_name }, - { type: 'varchar', value: tablePattern.schema }, - { type: 'varchar', value: name } - ] - }); - if (publications.rows.length == 0) { - replicateError = { - level: 'fatal', - message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` - }; - } - - return { - schema: schema, - name: name, - pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - replication_id: id_columns.map((c) => c.name), - data_queries: syncData, - parameter_queries: syncParameters, - errors: [id_columns_error, selectError, replicateError].filter((error) => error != null) as ReplicationError[] - }; - } - - async getDebugTablesInfo(tablePatterns: TablePattern[]): Promise { - let result: PatternResult[] = []; - - for (let tablePattern of tablePatterns) { - const schema = tablePattern.schema; - - let patternResult: PatternResult = { - schema: schema, - pattern: tablePattern.tablePattern, - wildcard: tablePattern.isWildcard - }; - result.push(patternResult); - - if (tablePattern.isWildcard) { - patternResult.tables = []; - const prefix = tablePattern.tablePrefix; - const results = await util.retriedQuery(this.db, { - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname LIKE $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - - for (let row of pgwireRows(results)) { - const name = row.table_name as string; - const relationId = row.relid as number; - if (!name.startsWith(prefix)) { - continue; - } - const details = await this.getDebugTableInfo(tablePattern, name, relationId); - patternResult.tables.push(details); - } - } else { - const results = await util.retriedQuery(this.db, { - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname = $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - if (results.rows.length == 0) { - // Table not found - const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); - patternResult.table = details; - } else { - const row = pgwireRows(results)[0]; - const name = row.table_name as string; - const relationId = row.relid as number; - patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId); - } - } - } - return result; - } -} - -export interface PatternResult { - schema: string; - pattern: string; - wildcard: boolean; - tables?: TableInfo[]; - table?: TableInfo; -} +// import * as pgwire from '@powersync/service-jpgwire'; +// import { DEFAULT_TAG, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; +// import { ReplicationError, TableInfo } from '@powersync/service-types'; +// import {} from '@powersync/service-core'; + +// // import { ReplicaIdentityResult, getReplicationIdentityColumns } from './util.js'; +// /** +// * Connection that _manages_ WAL, but does not do streaming. +// */ +// export class WalConnection { +// db: pgwire.PgClient; +// connectionTag = DEFAULT_TAG; +// publication_name = 'powersync'; + +// sync_rules: SqlSyncRules; + +// /** +// * db can be a PgConnection or PgPool. +// * +// * No transactions are used here, but it is up to the client to ensure +// * nothing here is called in the middle of another transaction if +// * PgConnection is used. +// */ +// constructor(options: { db: pgwire.PgClient; sync_rules: SqlSyncRules }) { +// this.db = options.db; +// this.sync_rules = options.sync_rules; +// } + +// async checkSourceConfiguration() { +// await checkSourceConfiguration(this.db); +// } + +// async getDebugTableInfo(tablePattern: TablePattern, name: string, relationId: number | null): Promise { +// const schema = tablePattern.schema; +// let id_columns_result: ReplicaIdentityResult | undefined = undefined; +// let id_columns_error = null; + +// if (relationId != null) { +// try { +// id_columns_result = await getReplicationIdentityColumns(this.db, relationId); +// } catch (e) { +// id_columns_error = { level: 'fatal', message: e.message }; +// } +// } + +// const id_columns = id_columns_result?.columns ?? []; + +// const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); + +// const syncData = this.sync_rules.tableSyncsData(sourceTable); +// const syncParameters = this.sync_rules.tableSyncsParameters(sourceTable); + +// if (relationId == null) { +// return { +// schema: schema, +// name: name, +// pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, +// replication_id: [], +// data_queries: syncData, +// parameter_queries: syncParameters, +// // Also +// errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] +// }; +// } +// if (id_columns.length == 0 && id_columns_error == null) { +// let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; +// if (id_columns_result?.replicationIdentity == 'default') { +// message += ' Configure a primary key on the table.'; +// } +// id_columns_error = { level: 'fatal', message }; +// } + +// let selectError = null; +// try { +// await util.retriedQuery(this.db, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); +// } catch (e) { +// selectError = { level: 'fatal', message: e.message }; +// } + +// let replicateError = null; + +// const publications = await util.retriedQuery(this.db, { +// statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, +// params: [ +// { type: 'varchar', value: this.publication_name }, +// { type: 'varchar', value: tablePattern.schema }, +// { type: 'varchar', value: name } +// ] +// }); +// if (publications.rows.length == 0) { +// replicateError = { +// level: 'fatal', +// message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` +// }; +// } + +// return { +// schema: schema, +// name: name, +// pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, +// replication_id: id_columns.map((c) => c.name), +// data_queries: syncData, +// parameter_queries: syncParameters, +// errors: [id_columns_error, selectError, replicateError].filter((error) => error != null) as ReplicationError[] +// }; +// } + +// async getDebugTablesInfo(tablePatterns: TablePattern[]): Promise { +// let result: PatternResult[] = []; + +// for (let tablePattern of tablePatterns) { +// const schema = tablePattern.schema; + +// let patternResult: PatternResult = { +// schema: schema, +// pattern: tablePattern.tablePattern, +// wildcard: tablePattern.isWildcard +// }; +// result.push(patternResult); + +// if (tablePattern.isWildcard) { +// patternResult.tables = []; +// const prefix = tablePattern.tablePrefix; +// const results = await util.retriedQuery(this.db, { +// statement: `SELECT c.oid AS relid, c.relname AS table_name +// FROM pg_class c +// JOIN pg_namespace n ON n.oid = c.relnamespace +// WHERE n.nspname = $1 +// AND c.relkind = 'r' +// AND c.relname LIKE $2`, +// params: [ +// { type: 'varchar', value: schema }, +// { type: 'varchar', value: tablePattern.tablePattern } +// ] +// }); + +// for (let row of pgwireRows(results)) { +// const name = row.table_name as string; +// const relationId = row.relid as number; +// if (!name.startsWith(prefix)) { +// continue; +// } +// const details = await this.getDebugTableInfo(tablePattern, name, relationId); +// patternResult.tables.push(details); +// } +// } else { +// const results = await util.retriedQuery(this.db, { +// statement: `SELECT c.oid AS relid, c.relname AS table_name +// FROM pg_class c +// JOIN pg_namespace n ON n.oid = c.relnamespace +// WHERE n.nspname = $1 +// AND c.relkind = 'r' +// AND c.relname = $2`, +// params: [ +// { type: 'varchar', value: schema }, +// { type: 'varchar', value: tablePattern.tablePattern } +// ] +// }); +// if (results.rows.length == 0) { +// // Table not found +// const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); +// patternResult.table = details; +// } else { +// const row = pgwireRows(results)[0]; +// const name = row.table_name as string; +// const relationId = row.relid as number; +// patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId); +// } +// } +// } +// return result; +// } +// } diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index c672b133b..130790bc8 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -1,624 +1,624 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import { container, errors, logger } from '@powersync/lib-services-framework'; -import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; - -import * as storage from '../storage/storage-index.js'; -import * as util from '../util/util-index.js'; - -import { getPgOutputRelation, getRelId, PgRelation } from './PgRelation.js'; -import { getReplicationIdentityColumns } from './util.js'; -import { WalConnection } from './WalConnection.js'; -import { Metrics } from '../metrics/Metrics.js'; - -export const ZERO_LSN = '00000000/00000000'; - -export interface WalStreamOptions { - connections: util.PgManager; - factory: storage.BucketStorageFactory; - storage: storage.SyncRulesBucketStorage; - abort_signal: AbortSignal; -} - -interface InitResult { - needsInitialSync: boolean; -} - -export class MissingReplicationSlotError extends Error { - constructor(message: string) { - super(message); - } -} - -export class WalStream { - sync_rules: SqlSyncRules; - group_id: number; - - wal_connection: WalConnection; - connection_id = 1; - - private readonly storage: storage.SyncRulesBucketStorage; - - private slot_name: string; - - private connections: util.PgManager; - - private abort_signal: AbortSignal; - - private relation_cache = new Map(); - - private startedStreaming = false; - - constructor(options: WalStreamOptions) { - this.storage = options.storage; - this.sync_rules = options.storage.sync_rules; - this.group_id = options.storage.group_id; - this.slot_name = options.storage.slot_name; - this.connections = options.connections; - - this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); - this.abort_signal = options.abort_signal; - this.abort_signal.addEventListener( - 'abort', - () => { - if (this.startedStreaming) { - // Ping to speed up cancellation of streaming replication - // We're not using pg_snapshot here, since it could be in the middle of - // an initial replication transaction. - const promise = util.retriedQuery( - this.connections.pool, - `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` - ); - promise.catch((e) => { - // Failures here are okay - this only speeds up stopping the process. - logger.warn('Failed to ping connection', e); - }); - } else { - // If we haven't started streaming yet, it could be due to something like - // and invalid password. In that case, don't attempt to ping. - } - }, - { once: true } - ); - } - - get publication_name() { - return this.wal_connection.publication_name; - } - - get connectionTag() { - return this.wal_connection.connectionTag; - } - - get stopped() { - return this.abort_signal.aborted; - } - - async getQualifiedTableNames( - batch: storage.BucketStorageBatch, - db: pgwire.PgConnection, - tablePattern: TablePattern - ): Promise { - const schema = tablePattern.schema; - if (tablePattern.connectionTag != this.connectionTag) { - return []; - } - - let tableRows: any[]; - const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; - if (tablePattern.isWildcard) { - const result = await db.query({ - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname LIKE $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - tableRows = pgwire.pgwireRows(result); - } else { - const result = await db.query({ - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname = $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - - tableRows = pgwire.pgwireRows(result); - } - let result: storage.SourceTable[] = []; - - for (let row of tableRows) { - const name = row.table_name as string; - if (typeof row.relid != 'bigint') { - throw new Error(`missing relid for ${name}`); - } - const relid = Number(row.relid as bigint); - - if (prefix && !name.startsWith(prefix)) { - continue; - } - - const rs = await db.query({ - statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, - params: [ - { type: 'varchar', value: this.publication_name }, - { type: 'varchar', value: tablePattern.schema }, - { type: 'varchar', value: name } - ] - }); - if (rs.rows.length == 0) { - logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); - continue; - } - - const cresult = await getReplicationIdentityColumns(db, relid); - - const table = await this.handleRelation( - batch, - { - name, - schema, - relationId: relid, - replicaIdentity: cresult.replicationIdentity, - replicationColumns: cresult.columns - }, - false - ); - - result.push(table); - } - return result; - } - - async initSlot(): Promise { - await this.wal_connection.checkSourceConfiguration(); - - const slotName = this.slot_name; - - const status = await this.storage.getStatus(); - if (status.snapshot_done && status.checkpoint_lsn) { - logger.info(`${slotName} Initial replication already done`); - - let last_error = null; - - // Check that replication slot exists - for (let i = 120; i >= 0; i--) { - await touch(); - - if (i == 0) { - container.reporter.captureException(last_error, { - level: errors.ErrorSeverity.ERROR, - metadata: { - replication_slot: slotName - } - }); - - throw last_error; - } - try { - // We peek a large number of changes here, to make it more likely to pick up replication slot errors. - // For example, "publication does not exist" only occurs here if the peek actually includes changes related - // to the slot. - await this.connections.pool.query({ - statement: `SELECT * - FROM pg_catalog.pg_logical_slot_peek_binary_changes($1, NULL, 1000, 'proto_version', '1', - 'publication_names', $2)`, - params: [ - { type: 'varchar', value: slotName }, - { type: 'varchar', value: this.publication_name } - ] - }); - // Success - logger.info(`Slot ${slotName} appears healthy`); - return { needsInitialSync: false }; - } catch (e) { - last_error = e; - logger.warn(`${slotName} Replication slot error`, e); - - if (this.stopped) { - throw e; - } - - // Could also be `publication "powersync" does not exist`, although this error may show up much later - // in some cases. - - if ( - /incorrect prev-link/.test(e.message) || - /replication slot.*does not exist/.test(e.message) || - /publication.*does not exist/.test(e.message) - ) { - container.reporter.captureException(e, { - level: errors.ErrorSeverity.WARNING, - metadata: { - try_index: i, - replication_slot: slotName - } - }); - // Sample: record with incorrect prev-link 10000/10000 at 0/18AB778 - // Seen during development. Some internal error, fixed by re-creating slot. - // - // Sample: publication "powersync" does not exist - // Happens when publication deleted or never created. - // Slot must be re-created in this case. - logger.info(`${slotName} does not exist anymore, will create new slot`); - - throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`); - } - // Try again after a pause - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - } - } - - return { needsInitialSync: true }; - } - - async estimatedCount(db: pgwire.PgConnection, table: storage.SourceTable): Promise { - const results = await db.query({ - statement: `SELECT reltuples::bigint AS estimate -FROM pg_class -WHERE oid = $1::regclass`, - params: [{ value: table.qualifiedName, type: 'varchar' }] - }); - const row = results.rows[0]; - if ((row?.[0] ?? -1n) == -1n) { - return '?'; - } else { - return `~${row[0]}`; - } - } - - /** - * Start initial replication. - * - * If (partial) replication was done before on this slot, this clears the state - * and starts again from scratch. - */ - async startInitialReplication(replicationConnection: pgwire.PgConnection) { - // If anything here errors, the entire replication process is aborted, - // and all connections closed, including this one. - const db = await this.connections.snapshotConnection(); - - const slotName = this.slot_name; - - await this.storage.clear(); - - await db.query({ - statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', - params: [{ type: 'varchar', value: slotName }] - }); - - // We use the replication connection here, not a pool. - // This connection needs to stay open at least until the snapshot is used below. - const result = await replicationConnection.query( - `CREATE_REPLICATION_SLOT ${slotName} LOGICAL pgoutput EXPORT_SNAPSHOT` - ); - const columns = result.columns; - const row = result.rows[0]!; - if (columns[1]?.name != 'consistent_point' || columns[2]?.name != 'snapshot_name' || row == null) { - throw new Error(`Invalid CREATE_REPLICATION_SLOT output: ${JSON.stringify(columns)}`); - } - // This LSN could be used in initialReplication below. - // But it's also safe to just use ZERO_LSN - we won't get any changes older than this lsn - // with streaming replication. - const lsn = pgwire.lsnMakeComparable(row[1]); - const snapshot = row[2]; - logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`); - - // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn - await db.query('BEGIN'); - // Use the snapshot exported above. - // Using SERIALIZABLE isolation level may give stronger guarantees, but that complicates - // the replication slot + snapshot above. And we still won't have SERIALIZABLE - // guarantees with streaming replication. - // See: ./docs/serializability.md for details. - // - // Another alternative here is to use the same pgwire connection for initial replication as well, - // instead of synchronizing a separate transaction to the snapshot. - - try { - await db.query(`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`); - await db.query(`SET TRANSACTION READ ONLY`); - await db.query(`SET TRANSACTION SNAPSHOT '${snapshot}'`); - - // Disable statement timeout for the duration of this transaction. - // On Supabase, the default is 2 minutes. - await db.query(`set local statement_timeout = 0`); - - logger.info(`${slotName} Starting initial replication`); - await this.initialReplication(db, lsn); - logger.info(`${slotName} Initial replication done`); - await db.query('COMMIT'); - } catch (e) { - await db.query('ROLLBACK'); - throw e; - } - } - - async initialReplication(db: pgwire.PgConnection, lsn: string) { - const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({}, async (batch) => { - for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, db, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, db, table); - await batch.markSnapshotDone([table], lsn); - - await touch(); - } - } - await batch.commit(lsn); - }); - } - - static *getQueryData(results: Iterable): Generator { - for (let row of results) { - yield toSyncRulesRow(row); - } - } - - private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) { - logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); - const estimatedCount = await this.estimatedCount(db, table); - let at = 0; - let lastLogIndex = 0; - const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); - let columns: { i: number; name: string }[] = []; - // pgwire streams rows in chunks. - // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. - - for await (let chunk of cursor) { - if (chunk.tag == 'RowDescription') { - let i = 0; - columns = chunk.payload.map((c) => { - return { i: i++, name: c.name }; - }); - continue; - } - - const rows = chunk.rows.map((row) => { - let q: DatabaseInputRow = {}; - for (let c of columns) { - q[c.name] = row[c.i]; - } - return q; - }); - if (rows.length > 0 && at - lastLogIndex >= 5000) { - logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`); - lastLogIndex = at; - } - if (this.abort_signal.aborted) { - throw new Error(`Aborted initial replication of ${this.slot_name}`); - } - - for (let record of WalStream.getQueryData(rows)) { - // This auto-flushes when the batch reaches its size limit - await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); - } - at += rows.length; - Metrics.getInstance().rows_replicated_total.add(rows.length); - - await touch(); - } - - await batch.flush(); - } - - async handleRelation(batch: storage.BucketStorageBatch, relation: PgRelation, snapshot: boolean) { - if (relation.relationId == null || typeof relation.relationId != 'number') { - throw new Error('relationId expected'); - } - const result = await this.storage.resolveTable({ - group_id: this.group_id, - connection_id: this.connection_id, - connection_tag: this.connectionTag, - relation: relation, - sync_rules: this.sync_rules - }); - this.relation_cache.set(relation.relationId, result.table); - - // Drop conflicting tables. This includes for example renamed tables. - await batch.drop(result.dropTables); - - // Snapshot if: - // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) - // 2. Snapshot is not already done, AND: - // 3. The table is used in sync rules. - const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; - - if (shouldSnapshot) { - // Truncate this table, in case a previous snapshot was interrupted. - await batch.truncate([result.table]); - - let lsn: string = ZERO_LSN; - // Start the snapshot inside a transaction. - // We use a dedicated connection for this. - const db = await this.connections.snapshotConnection(); - try { - await db.query('BEGIN'); - try { - // Get the current LSN. - // The data will only be consistent once incremental replication - // has passed that point. - const rs = await db.query(`select pg_current_wal_lsn() as lsn`); - lsn = rs.rows[0][0]; - await this.snapshotTable(batch, db, result.table); - await db.query('COMMIT'); - } catch (e) { - await db.query('ROLLBACK'); - throw e; - } - } finally { - await db.end(); - } - const [table] = await batch.markSnapshotDone([result.table], lsn); - return table; - } - - return result.table; - } - - private getTable(relationId: number): storage.SourceTable { - const table = this.relation_cache.get(relationId); - if (table == null) { - // We should always receive a replication message before the relation is used. - // If we can't find it, it's a bug. - throw new Error(`Missing relation cache for ${relationId}`); - } - return table; - } - - async writeChange( - batch: storage.BucketStorageBatch, - msg: pgwire.PgoutputMessage - ): Promise { - if (msg.lsn == null) { - return null; - } - if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { - const table = this.getTable(getRelId(msg.relation)); - if (!table.syncAny) { - logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); - return null; - } - - if (msg.tag == 'insert') { - Metrics.getInstance().rows_replicated_total.add(1); - const baseRecord = util.constructAfterRecord(msg); - return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); - } else if (msg.tag == 'update') { - Metrics.getInstance().rows_replicated_total.add(1); - // "before" may be null if the replica id columns are unchanged - // It's fine to treat that the same as an insert. - const before = util.constructBeforeRecord(msg); - const after = util.constructAfterRecord(msg); - return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); - } else if (msg.tag == 'delete') { - Metrics.getInstance().rows_replicated_total.add(1); - const before = util.constructBeforeRecord(msg)!; - - return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); - } - } else if (msg.tag == 'truncate') { - let tables: storage.SourceTable[] = []; - for (let relation of msg.relations) { - const table = this.getTable(getRelId(relation)); - tables.push(table); - } - return await batch.truncate(tables); - } - return null; - } - - async replicate() { - try { - // If anything errors here, the entire replication process is halted, and - // all connections automatically closed, including this one. - const replicationConnection = await this.connections.replicationConnection(); - await this.initReplication(replicationConnection); - await this.streamChanges(replicationConnection); - } catch (e) { - await this.storage.reportError(e); - throw e; - } - } - - async initReplication(replicationConnection: pgwire.PgConnection) { - const result = await this.initSlot(); - if (result.needsInitialSync) { - await this.startInitialReplication(replicationConnection); - } - } - - async streamChanges(replicationConnection: pgwire.PgConnection) { - // When changing any logic here, check /docs/wal-lsns.md. - - const replicationStream = replicationConnection.logicalReplication({ - slot: this.slot_name, - options: { - proto_version: '1', - publication_names: this.publication_name - } - }); - this.startedStreaming = true; - - // Auto-activate as soon as initial replication is done - await this.storage.autoActivate(); - - await this.storage.startBatch({}, async (batch) => { - // Replication never starts in the middle of a transaction - let inTx = false; - let count = 0; - - for await (const chunk of replicationStream.pgoutputDecode()) { - await touch(); - - if (this.abort_signal.aborted) { - break; - } - - // chunkLastLsn may come from normal messages in the chunk, - // or from a PrimaryKeepalive message. - const { messages, lastLsn: chunkLastLsn } = chunk; - - for (const msg of messages) { - if (msg.tag == 'relation') { - await this.handleRelation(batch, getPgOutputRelation(msg), true); - } else if (msg.tag == 'begin') { - inTx = true; - } else if (msg.tag == 'commit') { - Metrics.getInstance().transactions_replicated_total.add(1); - inTx = false; - await batch.commit(msg.lsn!); - await this.ack(msg.lsn!, replicationStream); - } else { - if (count % 100 == 0) { - logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); - } - - count += 1; - const result = await this.writeChange(batch, msg); - } - } - - if (!inTx) { - // In a transaction, we ack and commit according to the transaction progress. - // Outside transactions, we use the PrimaryKeepalive messages to advance progress. - // Big caveat: This _must not_ be used to skip individual messages, since this LSN - // may be in the middle of the next transaction. - // It must only be used to associate checkpoints with LSNs. - if (await batch.keepalive(chunkLastLsn)) { - await this.ack(chunkLastLsn, replicationStream); - } - } - - Metrics.getInstance().chunks_replicated_total.add(1); - } - }); - } - - async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { - if (lsn == ZERO_LSN) { - return; - } - - replicationStream.ack(lsn); - } -} - -async function touch() { - // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. - // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, - // or reduce PING_INTERVAL here. - return container.probes.touch(); -} +// import * as pgwire from '@powersync/service-jpgwire'; +// import { container, errors, logger } from '@powersync/lib-services-framework'; +// import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; + +// import * as storage from '../storage/storage-index.js'; +// import * as util from '../util/util-index.js'; + +// import { getPgOutputRelation, getRelId, PgRelation } from './PgRelation.js'; +// import { getReplicationIdentityColumns } from './util.js'; +// import { WalConnection } from './WalConnection.js'; +// import { Metrics } from '../metrics/Metrics.js'; + +// export const ZERO_LSN = '00000000/00000000'; + +// export interface WalStreamOptions { +// connections: util.PgManager; +// factory: storage.BucketStorageFactory; +// storage: storage.SyncRulesBucketStorage; +// abort_signal: AbortSignal; +// } + +// interface InitResult { +// needsInitialSync: boolean; +// } + +// export class MissingReplicationSlotError extends Error { +// constructor(message: string) { +// super(message); +// } +// } + +// export class WalStream { +// sync_rules: SqlSyncRules; +// group_id: number; + +// wal_connection: WalConnection; +// connection_id = 1; + +// private readonly storage: storage.SyncRulesBucketStorage; + +// private slot_name: string; + +// private connections: util.PgManager; + +// private abort_signal: AbortSignal; + +// private relation_cache = new Map(); + +// private startedStreaming = false; + +// constructor(options: WalStreamOptions) { +// this.storage = options.storage; +// this.sync_rules = options.storage.sync_rules; +// this.group_id = options.storage.group_id; +// this.slot_name = options.storage.slot_name; +// this.connections = options.connections; + +// this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); +// this.abort_signal = options.abort_signal; +// this.abort_signal.addEventListener( +// 'abort', +// () => { +// if (this.startedStreaming) { +// // Ping to speed up cancellation of streaming replication +// // We're not using pg_snapshot here, since it could be in the middle of +// // an initial replication transaction. +// const promise = util.retriedQuery( +// this.connections.pool, +// `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` +// ); +// promise.catch((e) => { +// // Failures here are okay - this only speeds up stopping the process. +// logger.warn('Failed to ping connection', e); +// }); +// } else { +// // If we haven't started streaming yet, it could be due to something like +// // and invalid password. In that case, don't attempt to ping. +// } +// }, +// { once: true } +// ); +// } + +// get publication_name() { +// return this.wal_connection.publication_name; +// } + +// get connectionTag() { +// return this.wal_connection.connectionTag; +// } + +// get stopped() { +// return this.abort_signal.aborted; +// } + +// async getQualifiedTableNames( +// batch: storage.BucketStorageBatch, +// db: pgwire.PgConnection, +// tablePattern: TablePattern +// ): Promise { +// const schema = tablePattern.schema; +// if (tablePattern.connectionTag != this.connectionTag) { +// return []; +// } + +// let tableRows: any[]; +// const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; +// if (tablePattern.isWildcard) { +// const result = await db.query({ +// statement: `SELECT c.oid AS relid, c.relname AS table_name +// FROM pg_class c +// JOIN pg_namespace n ON n.oid = c.relnamespace +// WHERE n.nspname = $1 +// AND c.relkind = 'r' +// AND c.relname LIKE $2`, +// params: [ +// { type: 'varchar', value: schema }, +// { type: 'varchar', value: tablePattern.tablePattern } +// ] +// }); +// tableRows = pgwire.pgwireRows(result); +// } else { +// const result = await db.query({ +// statement: `SELECT c.oid AS relid, c.relname AS table_name +// FROM pg_class c +// JOIN pg_namespace n ON n.oid = c.relnamespace +// WHERE n.nspname = $1 +// AND c.relkind = 'r' +// AND c.relname = $2`, +// params: [ +// { type: 'varchar', value: schema }, +// { type: 'varchar', value: tablePattern.tablePattern } +// ] +// }); + +// tableRows = pgwire.pgwireRows(result); +// } +// let result: storage.SourceTable[] = []; + +// for (let row of tableRows) { +// const name = row.table_name as string; +// if (typeof row.relid != 'bigint') { +// throw new Error(`missing relid for ${name}`); +// } +// const relid = Number(row.relid as bigint); + +// if (prefix && !name.startsWith(prefix)) { +// continue; +// } + +// const rs = await db.query({ +// statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, +// params: [ +// { type: 'varchar', value: this.publication_name }, +// { type: 'varchar', value: tablePattern.schema }, +// { type: 'varchar', value: name } +// ] +// }); +// if (rs.rows.length == 0) { +// logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); +// continue; +// } + +// const cresult = await getReplicationIdentityColumns(db, relid); + +// const table = await this.handleRelation( +// batch, +// { +// name, +// schema, +// relationId: relid, +// replicaIdentity: cresult.replicationIdentity, +// replicationColumns: cresult.columns +// }, +// false +// ); + +// result.push(table); +// } +// return result; +// } + +// async initSlot(): Promise { +// await this.wal_connection.checkSourceConfiguration(); + +// const slotName = this.slot_name; + +// const status = await this.storage.getStatus(); +// if (status.snapshot_done && status.checkpoint_lsn) { +// logger.info(`${slotName} Initial replication already done`); + +// let last_error = null; + +// // Check that replication slot exists +// for (let i = 120; i >= 0; i--) { +// await touch(); + +// if (i == 0) { +// container.reporter.captureException(last_error, { +// level: errors.ErrorSeverity.ERROR, +// metadata: { +// replication_slot: slotName +// } +// }); + +// throw last_error; +// } +// try { +// // We peek a large number of changes here, to make it more likely to pick up replication slot errors. +// // For example, "publication does not exist" only occurs here if the peek actually includes changes related +// // to the slot. +// await this.connections.pool.query({ +// statement: `SELECT * +// FROM pg_catalog.pg_logical_slot_peek_binary_changes($1, NULL, 1000, 'proto_version', '1', +// 'publication_names', $2)`, +// params: [ +// { type: 'varchar', value: slotName }, +// { type: 'varchar', value: this.publication_name } +// ] +// }); +// // Success +// logger.info(`Slot ${slotName} appears healthy`); +// return { needsInitialSync: false }; +// } catch (e) { +// last_error = e; +// logger.warn(`${slotName} Replication slot error`, e); + +// if (this.stopped) { +// throw e; +// } + +// // Could also be `publication "powersync" does not exist`, although this error may show up much later +// // in some cases. + +// if ( +// /incorrect prev-link/.test(e.message) || +// /replication slot.*does not exist/.test(e.message) || +// /publication.*does not exist/.test(e.message) +// ) { +// container.reporter.captureException(e, { +// level: errors.ErrorSeverity.WARNING, +// metadata: { +// try_index: i, +// replication_slot: slotName +// } +// }); +// // Sample: record with incorrect prev-link 10000/10000 at 0/18AB778 +// // Seen during development. Some internal error, fixed by re-creating slot. +// // +// // Sample: publication "powersync" does not exist +// // Happens when publication deleted or never created. +// // Slot must be re-created in this case. +// logger.info(`${slotName} does not exist anymore, will create new slot`); + +// throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`); +// } +// // Try again after a pause +// await new Promise((resolve) => setTimeout(resolve, 1000)); +// } +// } +// } + +// return { needsInitialSync: true }; +// } + +// async estimatedCount(db: pgwire.PgConnection, table: storage.SourceTable): Promise { +// const results = await db.query({ +// statement: `SELECT reltuples::bigint AS estimate +// FROM pg_class +// WHERE oid = $1::regclass`, +// params: [{ value: table.qualifiedName, type: 'varchar' }] +// }); +// const row = results.rows[0]; +// if ((row?.[0] ?? -1n) == -1n) { +// return '?'; +// } else { +// return `~${row[0]}`; +// } +// } + +// /** +// * Start initial replication. +// * +// * If (partial) replication was done before on this slot, this clears the state +// * and starts again from scratch. +// */ +// async startInitialReplication(replicationConnection: pgwire.PgConnection) { +// // If anything here errors, the entire replication process is aborted, +// // and all connections closed, including this one. +// const db = await this.connections.snapshotConnection(); + +// const slotName = this.slot_name; + +// await this.storage.clear(); + +// await db.query({ +// statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', +// params: [{ type: 'varchar', value: slotName }] +// }); + +// // We use the replication connection here, not a pool. +// // This connection needs to stay open at least until the snapshot is used below. +// const result = await replicationConnection.query( +// `CREATE_REPLICATION_SLOT ${slotName} LOGICAL pgoutput EXPORT_SNAPSHOT` +// ); +// const columns = result.columns; +// const row = result.rows[0]!; +// if (columns[1]?.name != 'consistent_point' || columns[2]?.name != 'snapshot_name' || row == null) { +// throw new Error(`Invalid CREATE_REPLICATION_SLOT output: ${JSON.stringify(columns)}`); +// } +// // This LSN could be used in initialReplication below. +// // But it's also safe to just use ZERO_LSN - we won't get any changes older than this lsn +// // with streaming replication. +// const lsn = pgwire.lsnMakeComparable(row[1]); +// const snapshot = row[2]; +// logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`); + +// // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn +// await db.query('BEGIN'); +// // Use the snapshot exported above. +// // Using SERIALIZABLE isolation level may give stronger guarantees, but that complicates +// // the replication slot + snapshot above. And we still won't have SERIALIZABLE +// // guarantees with streaming replication. +// // See: ./docs/serializability.md for details. +// // +// // Another alternative here is to use the same pgwire connection for initial replication as well, +// // instead of synchronizing a separate transaction to the snapshot. + +// try { +// await db.query(`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`); +// await db.query(`SET TRANSACTION READ ONLY`); +// await db.query(`SET TRANSACTION SNAPSHOT '${snapshot}'`); + +// // Disable statement timeout for the duration of this transaction. +// // On Supabase, the default is 2 minutes. +// await db.query(`set local statement_timeout = 0`); + +// logger.info(`${slotName} Starting initial replication`); +// await this.initialReplication(db, lsn); +// logger.info(`${slotName} Initial replication done`); +// await db.query('COMMIT'); +// } catch (e) { +// await db.query('ROLLBACK'); +// throw e; +// } +// } + +// async initialReplication(db: pgwire.PgConnection, lsn: string) { +// const sourceTables = this.sync_rules.getSourceTables(); +// await this.storage.startBatch({}, async (batch) => { +// for (let tablePattern of sourceTables) { +// const tables = await this.getQualifiedTableNames(batch, db, tablePattern); +// for (let table of tables) { +// await this.snapshotTable(batch, db, table); +// await batch.markSnapshotDone([table], lsn); + +// await touch(); +// } +// } +// await batch.commit(lsn); +// }); +// } + +// static *getQueryData(results: Iterable): Generator { +// for (let row of results) { +// yield toSyncRulesRow(row); +// } +// } + +// private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) { +// logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); +// const estimatedCount = await this.estimatedCount(db, table); +// let at = 0; +// let lastLogIndex = 0; +// const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); +// let columns: { i: number; name: string }[] = []; +// // pgwire streams rows in chunks. +// // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. + +// for await (let chunk of cursor) { +// if (chunk.tag == 'RowDescription') { +// let i = 0; +// columns = chunk.payload.map((c) => { +// return { i: i++, name: c.name }; +// }); +// continue; +// } + +// const rows = chunk.rows.map((row) => { +// let q: DatabaseInputRow = {}; +// for (let c of columns) { +// q[c.name] = row[c.i]; +// } +// return q; +// }); +// if (rows.length > 0 && at - lastLogIndex >= 5000) { +// logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`); +// lastLogIndex = at; +// } +// if (this.abort_signal.aborted) { +// throw new Error(`Aborted initial replication of ${this.slot_name}`); +// } + +// for (let record of WalStream.getQueryData(rows)) { +// // This auto-flushes when the batch reaches its size limit +// await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); +// } +// at += rows.length; +// Metrics.getInstance().rows_replicated_total.add(rows.length); + +// await touch(); +// } + +// await batch.flush(); +// } + +// async handleRelation(batch: storage.BucketStorageBatch, relation: PgRelation, snapshot: boolean) { +// if (relation.relationId == null || typeof relation.relationId != 'number') { +// throw new Error('relationId expected'); +// } +// const result = await this.storage.resolveTable({ +// group_id: this.group_id, +// connection_id: this.connection_id, +// connection_tag: this.connectionTag, +// relation: relation, +// sync_rules: this.sync_rules +// }); +// this.relation_cache.set(relation.relationId, result.table); + +// // Drop conflicting tables. This includes for example renamed tables. +// await batch.drop(result.dropTables); + +// // Snapshot if: +// // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) +// // 2. Snapshot is not already done, AND: +// // 3. The table is used in sync rules. +// const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; + +// if (shouldSnapshot) { +// // Truncate this table, in case a previous snapshot was interrupted. +// await batch.truncate([result.table]); + +// let lsn: string = ZERO_LSN; +// // Start the snapshot inside a transaction. +// // We use a dedicated connection for this. +// const db = await this.connections.snapshotConnection(); +// try { +// await db.query('BEGIN'); +// try { +// // Get the current LSN. +// // The data will only be consistent once incremental replication +// // has passed that point. +// const rs = await db.query(`select pg_current_wal_lsn() as lsn`); +// lsn = rs.rows[0][0]; +// await this.snapshotTable(batch, db, result.table); +// await db.query('COMMIT'); +// } catch (e) { +// await db.query('ROLLBACK'); +// throw e; +// } +// } finally { +// await db.end(); +// } +// const [table] = await batch.markSnapshotDone([result.table], lsn); +// return table; +// } + +// return result.table; +// } + +// private getTable(relationId: number): storage.SourceTable { +// const table = this.relation_cache.get(relationId); +// if (table == null) { +// // We should always receive a replication message before the relation is used. +// // If we can't find it, it's a bug. +// throw new Error(`Missing relation cache for ${relationId}`); +// } +// return table; +// } + +// async writeChange( +// batch: storage.BucketStorageBatch, +// msg: pgwire.PgoutputMessage +// ): Promise { +// if (msg.lsn == null) { +// return null; +// } +// if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { +// const table = this.getTable(getRelId(msg.relation)); +// if (!table.syncAny) { +// logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); +// return null; +// } + +// if (msg.tag == 'insert') { +// Metrics.getInstance().rows_replicated_total.add(1); +// const baseRecord = util.constructAfterRecord(msg); +// return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); +// } else if (msg.tag == 'update') { +// Metrics.getInstance().rows_replicated_total.add(1); +// // "before" may be null if the replica id columns are unchanged +// // It's fine to treat that the same as an insert. +// const before = util.constructBeforeRecord(msg); +// const after = util.constructAfterRecord(msg); +// return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); +// } else if (msg.tag == 'delete') { +// Metrics.getInstance().rows_replicated_total.add(1); +// const before = util.constructBeforeRecord(msg)!; + +// return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); +// } +// } else if (msg.tag == 'truncate') { +// let tables: storage.SourceTable[] = []; +// for (let relation of msg.relations) { +// const table = this.getTable(getRelId(relation)); +// tables.push(table); +// } +// return await batch.truncate(tables); +// } +// return null; +// } + +// async replicate() { +// try { +// // If anything errors here, the entire replication process is halted, and +// // all connections automatically closed, including this one. +// const replicationConnection = await this.connections.replicationConnection(); +// await this.initReplication(replicationConnection); +// await this.streamChanges(replicationConnection); +// } catch (e) { +// await this.storage.reportError(e); +// throw e; +// } +// } + +// async initReplication(replicationConnection: pgwire.PgConnection) { +// const result = await this.initSlot(); +// if (result.needsInitialSync) { +// await this.startInitialReplication(replicationConnection); +// } +// } + +// async streamChanges(replicationConnection: pgwire.PgConnection) { +// // When changing any logic here, check /docs/wal-lsns.md. + +// const replicationStream = replicationConnection.logicalReplication({ +// slot: this.slot_name, +// options: { +// proto_version: '1', +// publication_names: this.publication_name +// } +// }); +// this.startedStreaming = true; + +// // Auto-activate as soon as initial replication is done +// await this.storage.autoActivate(); + +// await this.storage.startBatch({}, async (batch) => { +// // Replication never starts in the middle of a transaction +// let inTx = false; +// let count = 0; + +// for await (const chunk of replicationStream.pgoutputDecode()) { +// await touch(); + +// if (this.abort_signal.aborted) { +// break; +// } + +// // chunkLastLsn may come from normal messages in the chunk, +// // or from a PrimaryKeepalive message. +// const { messages, lastLsn: chunkLastLsn } = chunk; + +// for (const msg of messages) { +// if (msg.tag == 'relation') { +// await this.handleRelation(batch, getPgOutputRelation(msg), true); +// } else if (msg.tag == 'begin') { +// inTx = true; +// } else if (msg.tag == 'commit') { +// Metrics.getInstance().transactions_replicated_total.add(1); +// inTx = false; +// await batch.commit(msg.lsn!); +// await this.ack(msg.lsn!, replicationStream); +// } else { +// if (count % 100 == 0) { +// logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); +// } + +// count += 1; +// const result = await this.writeChange(batch, msg); +// } +// } + +// if (!inTx) { +// // In a transaction, we ack and commit according to the transaction progress. +// // Outside transactions, we use the PrimaryKeepalive messages to advance progress. +// // Big caveat: This _must not_ be used to skip individual messages, since this LSN +// // may be in the middle of the next transaction. +// // It must only be used to associate checkpoints with LSNs. +// if (await batch.keepalive(chunkLastLsn)) { +// await this.ack(chunkLastLsn, replicationStream); +// } +// } + +// Metrics.getInstance().chunks_replicated_total.add(1); +// } +// }); +// } + +// async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { +// if (lsn == ZERO_LSN) { +// return; +// } + +// replicationStream.ack(lsn); +// } +// } + +// async function touch() { +// // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. +// // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, +// // or reduce PING_INTERVAL here. +// return container.probes.touch(); +// } diff --git a/modules/module-postgres/src/replication/WalStreamManager.ts b/modules/module-postgres/src/replication/WalStreamManager.ts index 10dc1c268..1533a37f6 100644 --- a/modules/module-postgres/src/replication/WalStreamManager.ts +++ b/modules/module-postgres/src/replication/WalStreamManager.ts @@ -1,213 +1,213 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import { hrtime } from 'node:process'; - -import * as storage from '../storage/storage-index.js'; -import * as util from '../util/util-index.js'; - -import { DefaultErrorRateLimiter } from './ErrorRateLimiter.js'; -import { WalStreamRunner } from './WalStreamRunner.js'; -import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; -import { container, logger } from '@powersync/lib-services-framework'; - -// 5 minutes -const PING_INTERVAL = 1_000_000_000n * 300n; - -export class WalStreamManager { - private streams = new Map(); - - private system: CorePowerSyncSystem; - - private stopped = false; - - // First ping is only after 5 minutes, not when starting - private lastPing = hrtime.bigint(); - - private storage: storage.BucketStorageFactory; - - /** - * This limits the effect of retries when there is a persistent issue. - */ - private rateLimiter = new DefaultErrorRateLimiter(); - - constructor(system: CorePowerSyncSystem) { - this.system = system; - this.storage = system.storage; - } - - start() { - this.runLoop().catch((e) => { - logger.error(`Fatal WalStream error`, e); - container.reporter.captureException(e); - setTimeout(() => { - process.exit(1); - }, 1000); - }); - } - - async stop() { - this.stopped = true; - let promises: Promise[] = []; - for (let stream of this.streams.values()) { - promises.push(stream.stop()); - } - await Promise.all(promises); - } - - private async runLoop() { - const configured_sync_rules = await util.loadSyncRules(this.system.config); - let configured_lock: storage.ReplicationLock | undefined = undefined; - if (configured_sync_rules != null) { - logger.info('Loading sync rules from configuration'); - try { - // Configure new sync rules, if it has changed. - // In that case, also immediately take out a lock, so that another process doesn't start replication on it. - const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { - lock: true - }); - if (lock) { - configured_lock = lock; - } - } catch (e) { - // Log, but continue with previous sync rules - logger.error(`Failed to load sync rules from configuration`, e); - } - } else { - logger.info('No sync rules configured - configure via API'); - } - while (!this.stopped) { - await container.probes.touch(); - try { - const pool = this.system.pgwire_pool; - if (pool) { - await this.refresh({ configured_lock }); - // The lock is only valid on the first refresh. - configured_lock = undefined; - - // TODO: Ping on all connections when we have multiple - // Perhaps WalStreamRunner would be a better place to do pings? - // We don't ping while in error retry back-off, to avoid having too - // many authentication failures. - if (this.rateLimiter.mayPing()) { - await this.ping(pool); - } - } - } catch (e) { - logger.error(`Failed to refresh wal streams`, e); - } - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - - /** - * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. - * - * The old WAL files are only deleted once no replication slot still references it. - * - * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot - * advancing**. - * - * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. - * - * **This may be a bug in pgwire or how we're using it. - */ - private async ping(db: pgwire.PgClient) { - const now = hrtime.bigint(); - if (now - this.lastPing >= PING_INTERVAL) { - try { - await db.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`); - } catch (e) { - logger.warn(`Failed to ping`, e); - } - this.lastPing = now; - } - } - - private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { - if (this.stopped) { - return; - } - - let configured_lock = options?.configured_lock; - - const existingStreams = new Map(this.streams.entries()); - const replicating = await this.storage.getReplicatingSyncRules(); - const newStreams = new Map(); - for (let syncRules of replicating) { - const existing = existingStreams.get(syncRules.id); - if (existing && !existing.stopped) { - // No change - existingStreams.delete(syncRules.id); - newStreams.set(syncRules.id, existing); - } else if (existing && existing.stopped) { - // Stopped (e.g. fatal error, slot rename). - // Remove from the list. Next refresh call will restart the stream. - existingStreams.delete(syncRules.id); - } else { - // New (or resume after restart) - try { - let lock: storage.ReplicationLock; - if (configured_lock?.sync_rules_id == syncRules.id) { - lock = configured_lock; - } else { - lock = await syncRules.lock(); - } - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const stream = new WalStreamRunner({ - factory: this.storage, - storage: storage, - source_db: this.system.config.connection!, - lock, - rateLimiter: this.rateLimiter - }); - newStreams.set(syncRules.id, stream); - stream.start(); - } catch (e) { - // Could be a sync rules parse error, - // for example from stricter validation that was added. - // This will be retried every couple of seconds. - // When new (valid) sync rules are deployed and processed, this one be disabled. - logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); - } - } - } - - this.streams = newStreams; - - // TODO: Should this termination be happening in the "background" instead? - // That becomes tricky to manage - - for (let stream of existingStreams.values()) { - // Old - stop and remove. - try { - await stream.terminate(); - } catch (e) { - // This will be retried - logger.warn(`Failed to terminate ${stream.slot_name}`, e); - } - } - - // Sync rules stopped previously or by a different process. - const stopped = await this.storage.getStoppedSyncRules(); - for (let syncRules of stopped) { - try { - const lock = await syncRules.lock(); - try { - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const stream = new WalStreamRunner({ - factory: this.storage, - storage: storage, - source_db: this.system.config.connection!, - lock - }); - await stream.terminate(); - } finally { - await lock.release(); - } - } catch (e) { - logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); - } - } - } -} +// import * as pgwire from '@powersync/service-jpgwire'; +// import { hrtime } from 'node:process'; + +// import * as storage from '../storage/storage-index.js'; +// import * as util from '../util/util-index.js'; + +// import { DefaultErrorRateLimiter } from './ErrorRateLimiter.js'; +// import { WalStreamRunner } from './WalStreamRunner.js'; +// import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; +// import { container, logger } from '@powersync/lib-services-framework'; + +// // 5 minutes +// const PING_INTERVAL = 1_000_000_000n * 300n; + +// export class WalStreamManager { +// private streams = new Map(); + +// private system: CorePowerSyncSystem; + +// private stopped = false; + +// // First ping is only after 5 minutes, not when starting +// private lastPing = hrtime.bigint(); + +// private storage: storage.BucketStorageFactory; + +// /** +// * This limits the effect of retries when there is a persistent issue. +// */ +// private rateLimiter = new DefaultErrorRateLimiter(); + +// constructor(system: CorePowerSyncSystem) { +// this.system = system; +// this.storage = system.storage; +// } + +// start() { +// this.runLoop().catch((e) => { +// logger.error(`Fatal WalStream error`, e); +// container.reporter.captureException(e); +// setTimeout(() => { +// process.exit(1); +// }, 1000); +// }); +// } + +// async stop() { +// this.stopped = true; +// let promises: Promise[] = []; +// for (let stream of this.streams.values()) { +// promises.push(stream.stop()); +// } +// await Promise.all(promises); +// } + +// private async runLoop() { +// const configured_sync_rules = await util.loadSyncRules(this.system.config); +// let configured_lock: storage.ReplicationLock | undefined = undefined; +// if (configured_sync_rules != null) { +// logger.info('Loading sync rules from configuration'); +// try { +// // Configure new sync rules, if it has changed. +// // In that case, also immediately take out a lock, so that another process doesn't start replication on it. +// const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { +// lock: true +// }); +// if (lock) { +// configured_lock = lock; +// } +// } catch (e) { +// // Log, but continue with previous sync rules +// logger.error(`Failed to load sync rules from configuration`, e); +// } +// } else { +// logger.info('No sync rules configured - configure via API'); +// } +// while (!this.stopped) { +// await container.probes.touch(); +// try { +// const pool = this.system.pgwire_pool; +// if (pool) { +// await this.refresh({ configured_lock }); +// // The lock is only valid on the first refresh. +// configured_lock = undefined; + +// // TODO: Ping on all connections when we have multiple +// // Perhaps WalStreamRunner would be a better place to do pings? +// // We don't ping while in error retry back-off, to avoid having too +// // many authentication failures. +// if (this.rateLimiter.mayPing()) { +// await this.ping(pool); +// } +// } +// } catch (e) { +// logger.error(`Failed to refresh wal streams`, e); +// } +// await new Promise((resolve) => setTimeout(resolve, 5000)); +// } +// } + +// /** +// * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. +// * +// * The old WAL files are only deleted once no replication slot still references it. +// * +// * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot +// * advancing**. +// * +// * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. +// * +// * **This may be a bug in pgwire or how we're using it. +// */ +// private async ping(db: pgwire.PgClient) { +// const now = hrtime.bigint(); +// if (now - this.lastPing >= PING_INTERVAL) { +// try { +// await db.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`); +// } catch (e) { +// logger.warn(`Failed to ping`, e); +// } +// this.lastPing = now; +// } +// } + +// private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { +// if (this.stopped) { +// return; +// } + +// let configured_lock = options?.configured_lock; + +// const existingStreams = new Map(this.streams.entries()); +// const replicating = await this.storage.getReplicatingSyncRules(); +// const newStreams = new Map(); +// for (let syncRules of replicating) { +// const existing = existingStreams.get(syncRules.id); +// if (existing && !existing.stopped) { +// // No change +// existingStreams.delete(syncRules.id); +// newStreams.set(syncRules.id, existing); +// } else if (existing && existing.stopped) { +// // Stopped (e.g. fatal error, slot rename). +// // Remove from the list. Next refresh call will restart the stream. +// existingStreams.delete(syncRules.id); +// } else { +// // New (or resume after restart) +// try { +// let lock: storage.ReplicationLock; +// if (configured_lock?.sync_rules_id == syncRules.id) { +// lock = configured_lock; +// } else { +// lock = await syncRules.lock(); +// } +// const parsed = syncRules.parsed(); +// const storage = this.storage.getInstance(parsed); +// const stream = new WalStreamRunner({ +// factory: this.storage, +// storage: storage, +// source_db: this.system.config.connection!, +// lock, +// rateLimiter: this.rateLimiter +// }); +// newStreams.set(syncRules.id, stream); +// stream.start(); +// } catch (e) { +// // Could be a sync rules parse error, +// // for example from stricter validation that was added. +// // This will be retried every couple of seconds. +// // When new (valid) sync rules are deployed and processed, this one be disabled. +// logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); +// } +// } +// } + +// this.streams = newStreams; + +// // TODO: Should this termination be happening in the "background" instead? +// // That becomes tricky to manage + +// for (let stream of existingStreams.values()) { +// // Old - stop and remove. +// try { +// await stream.terminate(); +// } catch (e) { +// // This will be retried +// logger.warn(`Failed to terminate ${stream.slot_name}`, e); +// } +// } + +// // Sync rules stopped previously or by a different process. +// const stopped = await this.storage.getStoppedSyncRules(); +// for (let syncRules of stopped) { +// try { +// const lock = await syncRules.lock(); +// try { +// const parsed = syncRules.parsed(); +// const storage = this.storage.getInstance(parsed); +// const stream = new WalStreamRunner({ +// factory: this.storage, +// storage: storage, +// source_db: this.system.config.connection!, +// lock +// }); +// await stream.terminate(); +// } finally { +// await lock.release(); +// } +// } catch (e) { +// logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); +// } +// } +// } +// } diff --git a/modules/module-postgres/src/replication/WalStreamRunner.ts b/modules/module-postgres/src/replication/WalStreamRunner.ts index ce3ff8759..e0845fd22 100644 --- a/modules/module-postgres/src/replication/WalStreamRunner.ts +++ b/modules/module-postgres/src/replication/WalStreamRunner.ts @@ -1,180 +1,180 @@ -import * as pgwire from '@powersync/service-jpgwire'; - -import * as storage from '../storage/storage-index.js'; -import * as util from '../util/util-index.js'; - -import { ErrorRateLimiter } from './ErrorRateLimiter.js'; -import { MissingReplicationSlotError, WalStream } from './WalStream.js'; -import { ResolvedConnection } from '../util/config/types.js'; -import { container, logger } from '@powersync/lib-services-framework'; - -export interface WalStreamRunnerOptions { - factory: storage.BucketStorageFactory; - storage: storage.SyncRulesBucketStorage; - source_db: ResolvedConnection; - lock: storage.ReplicationLock; - rateLimiter?: ErrorRateLimiter; -} - -export class WalStreamRunner { - private abortController = new AbortController(); - - private runPromise?: Promise; - - private connections: util.PgManager | null = null; - - private rateLimiter?: ErrorRateLimiter; - - constructor(public options: WalStreamRunnerOptions) { - this.rateLimiter = options.rateLimiter; - } - - start() { - this.runPromise = this.run(); - } - - get slot_name() { - return this.options.storage.slot_name; - } - - get stopped() { - return this.abortController.signal.aborted; - } - - async run() { - try { - await this.replicateLoop(); - } catch (e) { - // Fatal exception - container.reporter.captureException(e, { - metadata: { - replication_slot: this.slot_name - } - }); - logger.error(`Replication failed on ${this.slot_name}`, e); - - if (e instanceof MissingReplicationSlotError) { - // This stops replication on this slot, and creates a new slot - await this.options.storage.factory.slotRemoved(this.slot_name); - } - } finally { - this.abortController.abort(); - } - await this.options.lock.release(); - } - - async replicateLoop() { - while (!this.stopped) { - await this.replicateOnce(); - - if (!this.stopped) { - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - } - - async replicateOnce() { - // New connections on every iteration (every error with retry), - // otherwise we risk repeating errors related to the connection, - // such as caused by cached PG schemas. - let connections = new util.PgManager(this.options.source_db, { - // Pool connections are only used intermittently. - idleTimeout: 30_000, - maxSize: 2 - }); - this.connections = connections; - try { - await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); - if (this.stopped) { - return; - } - const stream = new WalStream({ - abort_signal: this.abortController.signal, - factory: this.options.factory, - storage: this.options.storage, - connections - }); - await stream.replicate(); - } catch (e) { - logger.error(`Replication error`, e); - if (e.cause != null) { - // Example: - // PgError.conn_ended: Unable to do postgres query on ended connection - // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) - // at stream.next () - // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) - // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) - // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) - // ... - // cause: TypeError: match is not iterable - // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) - // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) - // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) - // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) - // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) - // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) - // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), - // [Symbol(pg.ErrorCode)]: 'conn_ended', - // [Symbol(pg.ErrorResponse)]: undefined - // } - // Without this additional log, the cause would not be visible in the logs. - logger.error(`cause`, e.cause); - } - if (e instanceof MissingReplicationSlotError) { - throw e; - } else { - // Report the error if relevant, before retrying - container.reporter.captureException(e, { - metadata: { - replication_slot: this.slot_name - } - }); - // This sets the retry delay - this.rateLimiter?.reportError(e); - } - } finally { - this.connections = null; - if (connections != null) { - await connections.end(); - } - } - } - - /** - * This will also release the lock if start() was called earlier. - */ - async stop(options?: { force?: boolean }) { - logger.info(`${this.slot_name} Stopping replication`); - // End gracefully - this.abortController.abort(); - - if (options?.force) { - // destroy() is more forceful. - await this.connections?.destroy(); - } - await this.runPromise; - } - - /** - * Terminate this replication stream. This drops the replication slot and deletes the replication data. - * - * Stops replication if needed. - */ - async terminate(options?: { force?: boolean }) { - logger.info(`${this.slot_name} Terminating replication`); - await this.stop(options); - - const slotName = this.slot_name; - const db = await pgwire.connectPgWire(this.options.source_db, { type: 'standard' }); - try { - await db.query({ - statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', - params: [{ type: 'varchar', value: slotName }] - }); - } finally { - await db.end(); - } - - await this.options.storage.terminate(); - } -} +// import * as pgwire from '@powersync/service-jpgwire'; + +// import * as storage from '../storage/storage-index.js'; +// import * as util from '../util/util-index.js'; + +// import { ErrorRateLimiter } from './ErrorRateLimiter.js'; +// import { MissingReplicationSlotError, WalStream } from './WalStream.js'; +// import { ResolvedConnection } from '../util/config/types.js'; +// import { container, logger } from '@powersync/lib-services-framework'; + +// export interface WalStreamRunnerOptions { +// factory: storage.BucketStorageFactory; +// storage: storage.SyncRulesBucketStorage; +// source_db: ResolvedConnection; +// lock: storage.ReplicationLock; +// rateLimiter?: ErrorRateLimiter; +// } + +// export class WalStreamRunner { +// private abortController = new AbortController(); + +// private runPromise?: Promise; + +// private connections: util.PgManager | null = null; + +// private rateLimiter?: ErrorRateLimiter; + +// constructor(public options: WalStreamRunnerOptions) { +// this.rateLimiter = options.rateLimiter; +// } + +// start() { +// this.runPromise = this.run(); +// } + +// get slot_name() { +// return this.options.storage.slot_name; +// } + +// get stopped() { +// return this.abortController.signal.aborted; +// } + +// async run() { +// try { +// await this.replicateLoop(); +// } catch (e) { +// // Fatal exception +// container.reporter.captureException(e, { +// metadata: { +// replication_slot: this.slot_name +// } +// }); +// logger.error(`Replication failed on ${this.slot_name}`, e); + +// if (e instanceof MissingReplicationSlotError) { +// // This stops replication on this slot, and creates a new slot +// await this.options.storage.factory.slotRemoved(this.slot_name); +// } +// } finally { +// this.abortController.abort(); +// } +// await this.options.lock.release(); +// } + +// async replicateLoop() { +// while (!this.stopped) { +// await this.replicateOnce(); + +// if (!this.stopped) { +// await new Promise((resolve) => setTimeout(resolve, 5000)); +// } +// } +// } + +// async replicateOnce() { +// // New connections on every iteration (every error with retry), +// // otherwise we risk repeating errors related to the connection, +// // such as caused by cached PG schemas. +// let connections = new util.PgManager(this.options.source_db, { +// // Pool connections are only used intermittently. +// idleTimeout: 30_000, +// maxSize: 2 +// }); +// this.connections = connections; +// try { +// await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); +// if (this.stopped) { +// return; +// } +// const stream = new WalStream({ +// abort_signal: this.abortController.signal, +// factory: this.options.factory, +// storage: this.options.storage, +// connections +// }); +// await stream.replicate(); +// } catch (e) { +// logger.error(`Replication error`, e); +// if (e.cause != null) { +// // Example: +// // PgError.conn_ended: Unable to do postgres query on ended connection +// // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) +// // at stream.next () +// // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) +// // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) +// // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) +// // ... +// // cause: TypeError: match is not iterable +// // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) +// // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) +// // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) +// // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) +// // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) +// // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) +// // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), +// // [Symbol(pg.ErrorCode)]: 'conn_ended', +// // [Symbol(pg.ErrorResponse)]: undefined +// // } +// // Without this additional log, the cause would not be visible in the logs. +// logger.error(`cause`, e.cause); +// } +// if (e instanceof MissingReplicationSlotError) { +// throw e; +// } else { +// // Report the error if relevant, before retrying +// container.reporter.captureException(e, { +// metadata: { +// replication_slot: this.slot_name +// } +// }); +// // This sets the retry delay +// this.rateLimiter?.reportError(e); +// } +// } finally { +// this.connections = null; +// if (connections != null) { +// await connections.end(); +// } +// } +// } + +// /** +// * This will also release the lock if start() was called earlier. +// */ +// async stop(options?: { force?: boolean }) { +// logger.info(`${this.slot_name} Stopping replication`); +// // End gracefully +// this.abortController.abort(); + +// if (options?.force) { +// // destroy() is more forceful. +// await this.connections?.destroy(); +// } +// await this.runPromise; +// } + +// /** +// * Terminate this replication stream. This drops the replication slot and deletes the replication data. +// * +// * Stops replication if needed. +// */ +// async terminate(options?: { force?: boolean }) { +// logger.info(`${this.slot_name} Terminating replication`); +// await this.stop(options); + +// const slotName = this.slot_name; +// const db = await pgwire.connectPgWire(this.options.source_db, { type: 'standard' }); +// try { +// await db.query({ +// statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', +// params: [{ type: 'varchar', value: slotName }] +// }); +// } finally { +// await db.end(); +// } + +// await this.options.storage.terminate(); +// } +// } diff --git a/modules/module-postgres/src/types/types.ts b/modules/module-postgres/src/types/types.ts index 8a5869071..477ce81e8 100644 --- a/modules/module-postgres/src/types/types.ts +++ b/modules/module-postgres/src/types/types.ts @@ -1,6 +1,6 @@ +import * as service_types from '@powersync/service-types'; import * as t from 'ts-codec'; import * as urijs from 'uri-js'; -import * as service_types from '@powersync/service-types'; export interface NormalizedPostgresConnectionConfig { id: string; diff --git a/modules/module-postgres/src/utils/pgwire_utils.ts b/modules/module-postgres/src/utils/pgwire_utils.ts index c65b419de..69c4095f6 100644 --- a/modules/module-postgres/src/utils/pgwire_utils.ts +++ b/modules/module-postgres/src/utils/pgwire_utils.ts @@ -1,9 +1,9 @@ // Adapted from https://github.com/kagis/pgwire/blob/0dc927f9f8990a903f238737326e53ba1c8d094f/mod.js#L2218 -import * as bson from 'bson'; -import * as uuid from 'uuid'; import * as pgwire from '@powersync/service-jpgwire'; import { SqliteJsonValue, SqliteRow, ToastableSqliteRow, toSyncRulesRow } from '@powersync/service-sync-rules'; +import * as bson from 'bson'; +import * as uuid from 'uuid'; import { logger } from '@powersync/lib-services-framework'; diff --git a/modules/module-postgres/src/utils/utils.ts b/modules/module-postgres/src/utils/utils.ts deleted file mode 100644 index 486582a5e..000000000 --- a/modules/module-postgres/src/utils/utils.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as pgwire from '@powersync/service-jpgwire'; - -import { storage, utils } from '@powersync/service-core'; -import { logger } from '@powersync/lib-services-framework'; - -import * as pgwire_utils from './pgwire_utils.js'; - -// TODO these should probably be on the API provider - -export async function getClientCheckpoint( - db: pgwire.PgClient, - bucketStorage: storage.BucketStorageFactory, - options?: { timeout?: number } -): Promise { - const start = Date.now(); - - const [{ lsn }] = pgwire.pgwireRows( - await db.query(`SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) - ); - - // This old API needs a persisted checkpoint id. - // Since we don't use LSNs anymore, the only way to get that is to wait. - - const timeout = options?.timeout ?? 50_000; - - logger.info(`Waiting for LSN checkpoint: ${lsn}`); - while (Date.now() - start < timeout) { - const cp = await bucketStorage.getActiveCheckpoint(); - if (!cp.hasSyncRules()) { - throw new Error('No sync rules available'); - } - if (cp.lsn >= lsn) { - logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`); - return cp.checkpoint; - } - - await new Promise((resolve) => setTimeout(resolve, 30)); - } - - throw new Error('Timeout while waiting for checkpoint'); -} - -export async function createWriteCheckpoint( - db: pgwire.PgClient, - bucketStorage: storage.BucketStorageFactory, - user_id: string -): Promise { - const [{ lsn }] = pgwire.pgwireRows( - await pgwire_utils.retriedQuery(db, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) - ); - - const id = await bucketStorage.createWriteCheckpoint(user_id, { '1': lsn }); - logger.info(`Write checkpoint 2: ${JSON.stringify({ lsn, id: String(id) })}`); - return id; -} diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index 6d37f48d2..cfe926d59 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -1,6 +1,14 @@ import { SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; +export interface PatternResult { + schema: string; + pattern: string; + wildcard: boolean; + tables?: types.TableInfo[]; + table?: types.TableInfo; +} + /** * Describes all the methods currently required to service the sync API endpoints. */ @@ -60,16 +68,3 @@ export interface RouteAPI { */ shutdown(): Promise; } - -export interface DemoCredentials { - url: string; -} - -// TODO: Export this when the existing definition in WALConnection is removed -interface PatternResult { - schema: string; - pattern: string; - wildcard: boolean; - tables?: types.TableInfo[]; - table?: types.TableInfo; -} diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index c66d53455..de382681e 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -1,9 +1,10 @@ -import { container, logger } from '@powersync/lib-services-framework'; +import { logger } from '@powersync/lib-services-framework'; import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/service-sync-rules'; import { SyncRulesStatus, TableInfo } from '@powersync/service-types'; import * as storage from '../storage/storage-index.js'; -import { ServiceContext } from '../system/ServiceContext.js'; +import * as system from '../system/system-index.js'; + export interface DiagnosticsOptions { /** * Include sync rules content in response. @@ -26,6 +27,7 @@ export interface DiagnosticsOptions { export const DEFAULT_DATASOURCE_ID = 'default'; export async function getSyncRulesStatus( + serviceContext: system.ServiceContext, sync_rules: storage.PersistedSyncRulesContent | null, options: DiagnosticsOptions ): Promise { @@ -50,7 +52,6 @@ export async function getSyncRulesStatus( }; } - const serviceContext = container.getImplementation(ServiceContext); const { storage } = serviceContext; const api = serviceContext.routerEngine.getAPI(); diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index 244bb7146..155e293d1 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -1,12 +1,11 @@ -import { container } from '@powersync/lib-services-framework'; import { internal_routes } from '@powersync/service-types'; -import { ServiceContext } from '../system/ServiceContext.js'; +import * as system from '../system/system-index.js'; -export async function getConnectionsSchema(): Promise { - const { routerEngine } = container.getImplementation(ServiceContext); - - const api = routerEngine.getAPI(); +export async function getConnectionsSchema( + serviceContext: system.ServiceContext +): Promise { + const api = serviceContext.routerEngine.getAPI(); if (!api) { return { connections: [] diff --git a/packages/service-core/src/entry/cli-entry.ts b/packages/service-core/src/entry/cli-entry.ts index aebf4eb5d..8d17ce7eb 100644 --- a/packages/service-core/src/entry/cli-entry.ts +++ b/packages/service-core/src/entry/cli-entry.ts @@ -3,9 +3,10 @@ import { Command } from 'commander'; import { logger } from '@powersync/lib-services-framework'; import * as modules from '../modules/modules-index.js'; import * as utils from '../util/util-index.js'; +import { registerCompactAction } from './commands/compact-action.js'; import { registerMigrationAction } from './commands/migrate-action.js'; +import { registerStartAction } from './commands/start-action.js'; import { registerTearDownAction } from './commands/teardown-action.js'; -import { registerCompactAction, registerStartAction } from './entry-index.js'; /** * Generates a Commander program which serves as the entry point diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index 74f80fcec..47ea806f5 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -3,7 +3,7 @@ import { Command } from 'commander'; import { logger } from '@powersync/lib-services-framework'; import * as v8 from 'v8'; import * as modules from '../../modules/modules-index.js'; -import { createPowerSyncMongo, MongoBucketStorage } from '../../storage/storage-index.js'; +import * as storage from '../../storage/storage-index.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'compact'; @@ -31,12 +31,12 @@ export function registerCompactAction(program: Command, moduleManager: modules.M await moduleManager.initialize(runnerConfig); const { configuration } = moduleManager.serviceContext; - const { storage } = configuration; - const psdb = createPowerSyncMongo(storage); + const { storage: storageConfig } = configuration; + const psdb = storage.createPowerSyncMongo(storageConfig); const client = psdb.client; await client.connect(); try { - const bucketStorage = new MongoBucketStorage(psdb, { slot_name_prefix: configuration.slot_name_prefix }); + const bucketStorage = new storage.MongoBucketStorage(psdb, { slot_name_prefix: configuration.slot_name_prefix }); const active = await bucketStorage.getActiveSyncRules(); if (active == null) { logger.info('No active instance to compact'); diff --git a/packages/service-core/src/entry/commands/migrate-action.ts b/packages/service-core/src/entry/commands/migrate-action.ts index 92dc89df7..0196b8486 100644 --- a/packages/service-core/src/entry/commands/migrate-action.ts +++ b/packages/service-core/src/entry/commands/migrate-action.ts @@ -1,8 +1,7 @@ import { logger } from '@powersync/lib-services-framework'; import { Command } from 'commander'; -import { Direction } from '../../migrations/definitions.js'; -import { migrate } from '../../migrations/migrations.js'; +import * as migrations from '../../migrations/migrations-index.js'; import * as modules from '../../modules/modules-index.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; @@ -16,11 +15,11 @@ export function registerMigrationAction(program: Command, moduleManager: modules return migrationCommand .description('Run migrations') .argument('', 'Migration direction. `up` or `down`') - .action(async (direction: Direction, options) => { + .action(async (direction: migrations.Direction, options) => { await moduleManager.initialize(extractRunnerOptions(options)); try { - await migrate({ + await migrations.migrate({ direction, service_context: moduleManager.serviceContext }); diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index 7b1563afd..5b383ade4 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -19,6 +19,7 @@ export class ModuleManager { for (const module of modules) { if (this.modules.has(module.name)) { logger.warn(`Module ${module.name} already registered, skipping...`); + return; } else { this.modules.set(module.name, module); } diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index fa0fa9702..aa6e85f83 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -1,10 +1,9 @@ import { errors, router, schema } from '@powersync/lib-services-framework'; -import { SqlSyncRules, SqliteValue, StaticSchema, isJsonValue } from '@powersync/service-sync-rules'; +import { SqlSyncRules, StaticSchema } from '@powersync/service-sync-rules'; import { internal_routes } from '@powersync/service-types'; import * as api from '../../api/api-index.js'; - -import { PersistedSyncRulesContent } from '../../storage/BucketStorage.js'; +import * as storage from '../../storage/storage-index.js'; import { authApi } from '../auth.js'; import { routeDefinition } from '../router.js'; @@ -59,13 +58,13 @@ export const diagnostics = routeDefinition({ const active = await storage.getActiveSyncRulesContent(); const next = await storage.getNextSyncRulesContent(); - const active_status = await api.getSyncRulesStatus(active, { + const active_status = await api.getSyncRulesStatus(service_context, active, { include_content, check_connection: status.connected, live_status: true }); - const next_status = await api.getSyncRulesStatus(next, { + const next_status = await api.getSyncRulesStatus(service_context, next, { include_content, check_connection: status.connected, live_status: true @@ -90,8 +89,8 @@ export const getSchema = routeDefinition({ method: router.HTTPMethod.POST, authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }), - handler: async () => { - return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema()); + handler: async (payload) => { + return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(payload.context.service_context)); } }); @@ -150,10 +149,10 @@ export const validate = routeDefinition({ } = payload; const content = payload.params.sync_rules; - const schemaData = await api.getConnectionsSchema(); + const schemaData = await api.getConnectionsSchema(service_context); const schema = new StaticSchema(schemaData.connections); - const sync_rules: PersistedSyncRulesContent = { + const sync_rules: storage.PersistedSyncRulesContent = { // Dummy values id: 0, slot_name: '', @@ -179,7 +178,7 @@ export const validate = routeDefinition({ }); } - const status = (await api.getSyncRulesStatus(sync_rules, { + const status = (await api.getSyncRulesStatus(service_context, sync_rules, { include_content: false, check_connection: connectionStatus.connected, live_status: false @@ -193,14 +192,4 @@ export const validate = routeDefinition({ } }); -function mapColumnValue(value: SqliteValue) { - if (typeof value == 'bigint') { - return Number(value); - } else if (isJsonValue(value)) { - return value; - } else { - return null; - } -} - export const ADMIN_ROUTES = [executeSql, diagnostics, getSchema, reprocess, validate]; diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index e952b7f80..2282a765a 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -3,7 +3,7 @@ import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules'; import type { FastifyPluginAsync } from 'fastify'; import * as t from 'ts-codec'; -import { ServiceContext } from '../../system/ServiceContext.js'; +import * as system from '../../system/system-index.js'; import { authApi } from '../auth.js'; import { routeDefinition } from '../router.js'; @@ -170,7 +170,7 @@ function replyPrettyJson(payload: any) { }); } -async function debugSyncRules(serviceContext: ServiceContext, sync_rules: string) { +async function debugSyncRules(serviceContext: system.ServiceContext, sync_rules: string) { try { const rules = SqlSyncRules.fromYaml(sync_rules); const source_table_patterns = rules.getSourceTables(); diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index 99f80ce4c..c1e117407 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -8,7 +8,6 @@ import * as util from '../../util/util-index.js'; import { Metrics } from '../../metrics/Metrics.js'; import { authUser } from '../auth.js'; import { routeDefinition } from '../router.js'; -import { RequestTracker } from '../../sync/RequestTracker.js'; export enum SyncRoutes { STREAM = '/sync/stream' @@ -44,7 +43,7 @@ export const syncStreamed = routeDefinition({ }); } const controller = new AbortController(); - const tracker = new RequestTracker(); + const tracker = new sync.RequestTracker(); try { Metrics.getInstance().concurrent_connections.add(1); const stream = Readable.from( diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index a1b6181f2..3db5217bf 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -2,9 +2,9 @@ import { SqliteRow, SqlSyncRules } from '@powersync/service-sync-rules'; import * as bson from 'bson'; import * as mongo from 'mongodb'; -import * as util from '../../util/util-index.js'; -import * as replication from '../../replication/replication-index.js'; import { container, errors, logger } from '@powersync/lib-services-framework'; +import * as replication from '../../replication/replication-index.js'; +import * as util from '../../util/util-index.js'; import { BucketStorageBatch, FlushedResult, mergeToast, SaveOptions } from '../BucketStorage.js'; import { SourceTable } from '../SourceTable.js'; import { PowerSyncMongo } from './db.js'; diff --git a/packages/service-core/src/sync/sync.ts b/packages/service-core/src/sync/sync.ts index 16714d42b..ae4c9c528 100644 --- a/packages/service-core/src/sync/sync.ts +++ b/packages/service-core/src/sync/sync.ts @@ -8,10 +8,9 @@ import * as storage from '../storage/storage-index.js'; import * as util from '../util/util-index.js'; import { logger } from '@powersync/lib-services-framework'; -import { Metrics } from '../metrics/Metrics.js'; import { mergeAsyncIterables } from './merge.js'; -import { TokenStreamOptions, tokenStream } from './util.js'; import { RequestTracker } from './RequestTracker.js'; +import { TokenStreamOptions, tokenStream } from './util.js'; /** * Maximum number of connections actively fetching data. diff --git a/packages/service-core/src/sync/util.ts b/packages/service-core/src/sync/util.ts index 362706487..2437d38f5 100644 --- a/packages/service-core/src/sync/util.ts +++ b/packages/service-core/src/sync/util.ts @@ -1,7 +1,6 @@ import * as timers from 'timers/promises'; import * as util from '../util/util-index.js'; -import { Metrics } from '../metrics/Metrics.js'; import { RequestTracker } from './RequestTracker.js'; export type TokenStreamOptions = { From 8d465046af6e57a8ca50ef217b2e8ecde577ab2d Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 14:26:19 +0200 Subject: [PATCH 048/298] begining of teardown workflow --- .../src/module/PostgresModule.ts | 47 +++++++-- modules/module-postgres/src/utils/teardown.ts | 68 +++++++++++++ .../src/entry/commands/teardown-action.ts | 8 +- .../src/modules/AbstractModule.ts | 2 +- packages/service-core/src/runner/teardown.ts | 98 +------------------ 5 files changed, 119 insertions(+), 104 deletions(-) create mode 100644 modules/module-postgres/src/utils/teardown.ts diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index f1414b8e0..fa4b31db2 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,17 +1,19 @@ import * as t from 'ts-codec'; -import { api, replication } from '@powersync/service-core'; +import { api, db, replication, storage } from '@powersync/service-core'; +import { logger } from '@powersync/lib-services-framework'; import { ServiceContext } from '@powersync/service-core/src/system/ServiceContext.js'; import { PostgresSyncAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; import { normalizeConnectionConfig, PostgresConnectionConfig, ResolvedConnectionConfig } from '../types/types.js'; +import { terminateReplicators } from '../utils/teardown.js'; export class PostgresModule extends replication.ReplicationModule { constructor() { super({ name: 'Postgres', - type: 'postgres' + type: 'postgresql' }); } @@ -30,10 +32,6 @@ export class PostgresModule extends replication.ReplicationModule { return new PostgresReplicationAdapter(this.resolveConfig(config)); } - public teardown(): Promise { - throw new Error('Method not implemented.'); - } - /** * Combines base config with normalized connection settings */ @@ -43,4 +41,41 @@ export class PostgresModule extends replication.ReplicationModule { ...normalizeConnectionConfig(config) }; } + + async teardown(context: ServiceContext): Promise { + const mongoDB = storage.createPowerSyncMongo(context.configuration.storage); + try { + // TODO this should not be necessary since the service context + // has already been initialized. + // However we need a direct mongo connection for this. + // Maybe we can add termination methods to the storage. + // TODO improve this when other storage methods or connections are implemented + logger.info(`Waiting for auth`); + await db.mongo.waitForAuth(mongoDB.db); + + logger.info(`Terminating replication slots`); + const connection = context.configuration.data_sources?.find( + (c) => c.type == 'postgresql' + ) as PostgresConnectionConfig; + + if (connection) { + await terminateReplicators(context.storage, this.resolveConfig(connection)); + } + + const database = mongoDB.db; + logger.info(`Dropping database ${database.namespace}`); + await database.dropDatabase(); + logger.info(`Done`); + await mongoDB.client.close(); + + // If there was an error connecting to postgress, the process may stay open indefinitely. + // This forces an exit. + // We do not consider those errors a teardown failure. + process.exit(0); + } catch (e) { + logger.error(`Teardown failure`, e); + await mongoDB.client.close(); + process.exit(1); + } + } } diff --git a/modules/module-postgres/src/utils/teardown.ts b/modules/module-postgres/src/utils/teardown.ts new file mode 100644 index 000000000..69c7b9a5d --- /dev/null +++ b/modules/module-postgres/src/utils/teardown.ts @@ -0,0 +1,68 @@ +import { logger } from '@powersync/lib-services-framework'; +import { storage } from '@powersync/service-core'; +import * as timers from 'timers/promises'; +import { ResolvedConnectionConfig } from '../types/types.js'; + +/** + * Terminate all replicating sync rules, deleting the replication slots. + * + * Retries lock and other errors for up to two minutes. + * + * This is a best-effort attempt. In some cases it may not be possible to delete the replication + * slot, such as when the postgres instance is unreachable. + */ +export async function terminateReplicators( + storageFactory: storage.BucketStorageFactory, + connection: ResolvedConnectionConfig +) { + const start = Date.now(); + while (Date.now() - start < 12_000) { + let retry = false; + const replicationRules = await storageFactory.getReplicatingSyncRules(); + for (let syncRules of replicationRules) { + try { + await terminateReplicator(storageFactory, connection, syncRules); + } catch (e) { + retry = true; + console.error(e); + logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); + } + } + if (!retry) { + break; + } + await timers.setTimeout(5_000); + } +} + +/** + * Attempt to terminate a single sync rules instance. + * + * This may fail with a lock error. + */ +async function terminateReplicator( + storageFactory: storage.BucketStorageFactory, + connection: ResolvedConnectionConfig, + syncRules: storage.PersistedSyncRulesContent +) { + // The lock may still be active if the current replication instance + // hasn't stopped yet. + const lock = await syncRules.lock(); + try { + const parsed = syncRules.parsed(); + const storage = storageFactory.getInstance(parsed); + + // const stream = new replication.WalStreamRunner({ + // factory: storageFactory, + // storage: storage, + // source_db: connection, + // lock + // }); + + // logger.info(`Terminating replication slot ${stream.slot_name}`); + // // await stream.terminate(); + // logger.info(`Terminated replication slot ${stream.slot_name}`); + } finally { + await lock.release(); + } +} diff --git a/packages/service-core/src/entry/commands/teardown-action.ts b/packages/service-core/src/entry/commands/teardown-action.ts index 3f0e402aa..cf27f58e7 100644 --- a/packages/service-core/src/entry/commands/teardown-action.ts +++ b/packages/service-core/src/entry/commands/teardown-action.ts @@ -1,11 +1,12 @@ import { Command } from 'commander'; -import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; +import * as modules from '../../modules/modules-index.js'; import { teardown } from '../../runner/teardown.js'; +import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'teardown'; -export function registerTearDownAction(program: Command) { +export function registerTearDownAction(program: Command, moduleManager: modules.ModuleManager) { const teardownCommand = program.command(COMMAND_NAME); wrapConfigCommand(teardownCommand); @@ -18,6 +19,7 @@ export function registerTearDownAction(program: Command) { throw new Error('TEARDOWN was not acknowledged.'); } - await teardown(extractRunnerOptions(options)); + await moduleManager.initialize(extractRunnerOptions(options)); + await teardown(moduleManager); }); } diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index 060e0229b..02b906b8d 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -33,5 +33,5 @@ export abstract class AbstractModule { /** * Terminate and clean up any resources managed by the module right away */ - public abstract teardown(): Promise; + public abstract teardown(context: ServiceContext): Promise; } diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index 8060a4047..f2b148038 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -3,106 +3,16 @@ // 1. The replication slots on the source postgres instance (if available). // 2. The mongo database. -import * as timers from 'timers/promises'; - -import * as db from '../db/db-index.js'; -import * as storage from '../storage/storage-index.js'; -import * as utils from '../util/util-index.js'; -import * as replication from '../replication/replication-index.js'; import { logger } from '@powersync/lib-services-framework'; +import * as modules from '../modules/modules-index.js'; -/** - * Attempt to terminate a single sync rules instance. - * - * This may fail with a lock error. - */ -async function terminateReplicator( - storageFactory: storage.BucketStorageFactory, - connection: utils.ResolvedConnection, - syncRules: storage.PersistedSyncRulesContent -) { - // The lock may still be active if the current replication instance - // hasn't stopped yet. - const lock = await syncRules.lock(); - try { - const parsed = syncRules.parsed(); - const storage = storageFactory.getInstance(parsed); - const stream = new replication.WalStreamRunner({ - factory: storageFactory, - storage: storage, - source_db: connection, - lock - }); - - logger.info(`Terminating replication slot ${stream.slot_name}`); - await stream.terminate(); - logger.info(`Terminated replication slot ${stream.slot_name}`); - } finally { - await lock.release(); - } -} - -/** - * Terminate all replicating sync rules, deleting the replication slots. - * - * Retries lock and other errors for up to two minutes. - * - * This is a best-effot attempt. In some cases it may not be possible to delete the replication - * slot, such as when the postgres instance is unreachable. - */ -async function terminateReplicators( - storageFactory: storage.BucketStorageFactory, - connection: utils.ResolvedConnection -) { - const start = Date.now(); - while (Date.now() - start < 12_000) { - let retry = false; - const replicationRules = await storageFactory.getReplicatingSyncRules(); - for (let syncRules of replicationRules) { - try { - await terminateReplicator(storageFactory, connection, syncRules); - } catch (e) { - retry = true; - console.error(e); - logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); - } - } - if (!retry) { - break; - } - await timers.setTimeout(5_000); - } -} - -export async function teardown(runnerConfig: utils.RunnerConfig) { - const config = await utils.loadConfig(runnerConfig); - const mongoDB = storage.createPowerSyncMongo(config.storage); +export async function teardown(moduleManager: modules.ModuleManager) { try { - logger.info(`Waiting for auth`); - await db.mongo.waitForAuth(mongoDB.db); - - const bucketStorage = new storage.MongoBucketStorage(mongoDB, { slot_name_prefix: config.slot_name_prefix }); - const connection = config.connection; - - logger.info(`Terminating replication slots`); - - if (connection) { - await terminateReplicators(bucketStorage, connection); - } - - const database = mongoDB.db; - logger.info(`Dropping database ${database.namespace}`); - await database.dropDatabase(); - logger.info(`Done`); - await mongoDB.client.close(); - - // If there was an error connecting to postgress, the process may stay open indefinitely. - // This forces an exit. - // We do not consider those errors a teardown failure. + // teardown all modules + await moduleManager.tearDown(); process.exit(0); } catch (e) { logger.error(`Teardown failure`, e); - await mongoDB.client.close(); process.exit(1); } } From 208199fe72135aa9337a31ab1743342ea6e28596 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 15:05:12 +0200 Subject: [PATCH 049/298] incremnetal build error fixes --- packages/service-core/src/entry/cli-entry.ts | 2 +- packages/service-core/src/index.ts | 4 ++-- packages/service-core/src/metrics/Metrics.ts | 14 ++++++------- .../service-core/src/modules/ModuleManager.ts | 2 +- .../src/replication/core/ReplicationModule.ts | 21 ++++++++++--------- packages/service-core/src/util/config.ts | 14 +------------ 6 files changed, 23 insertions(+), 34 deletions(-) diff --git a/packages/service-core/src/entry/cli-entry.ts b/packages/service-core/src/entry/cli-entry.ts index 8d17ce7eb..f2d541dac 100644 --- a/packages/service-core/src/entry/cli-entry.ts +++ b/packages/service-core/src/entry/cli-entry.ts @@ -21,7 +21,7 @@ export function generateEntryProgram( const entryProgram = new Command(); entryProgram.name('powersync-runner').description('CLI to initiate a PowerSync service runner'); - registerTearDownAction(entryProgram); + registerTearDownAction(entryProgram, moduleManager); registerMigrationAction(entryProgram, moduleManager); registerCompactAction(entryProgram, moduleManager); diff --git a/packages/service-core/src/index.ts b/packages/service-core/src/index.ts index 7ef5f7276..a365d77d4 100644 --- a/packages/service-core/src/index.ts +++ b/packages/service-core/src/index.ts @@ -18,8 +18,8 @@ export * as framework from '@powersync/lib-services-framework'; export * from './metrics/Metrics.js'; export * as metrics from './metrics/Metrics.js'; -export * from './migrations/migrations.js'; export * as migrations from './migrations/migrations-index.js'; +export * from './migrations/migrations.js'; export * from './modules/modules-index.js'; export * as modules from './modules/modules-index.js'; @@ -36,7 +36,7 @@ export * as storage from './storage/storage-index.js'; export * from './sync/sync-index.js'; export * as sync from './sync/sync-index.js'; -export * from './system/CorePowerSyncSystem.js'; +export * from './system/system-index.js'; export * as system from './system/system-index.js'; export * from './util/util-index.js'; diff --git a/packages/service-core/src/metrics/Metrics.ts b/packages/service-core/src/metrics/Metrics.ts index 2ec759104..cf5098243 100644 --- a/packages/service-core/src/metrics/Metrics.ts +++ b/packages/service-core/src/metrics/Metrics.ts @@ -1,13 +1,13 @@ import { Attributes, Counter, ObservableGauge, UpDownCounter, ValueType } from '@opentelemetry/api'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; +import { Resource } from '@opentelemetry/resources'; import { MeterProvider, MetricReader, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; -import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; +import { logger } from '@powersync/lib-services-framework'; import * as jpgwire from '@powersync/service-jpgwire'; -import * as util from '../util/util-index.js'; import * as storage from '../storage/storage-index.js'; -import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; -import { Resource } from '@opentelemetry/resources'; -import { logger } from '@powersync/lib-services-framework'; +import * as system from '../system/system-index.js'; +import * as util from '../util/util-index.js'; export interface MetricsOptions { disable_telemetry_sharing: boolean; @@ -202,7 +202,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable this.concurrent_connections.add(0); } - public configureReplicationMetrics(system: CorePowerSyncSystem) { + public configureReplicationMetrics(serviceContext: system.ServiceContext) { // Rate limit collection of these stats, since it may be an expensive query const MINIMUM_INTERVAL = 60_000; @@ -211,7 +211,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable function getMetrics() { if (cachedRequest == null || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) { - cachedRequest = system.storage.getStorageMetrics().catch((e) => { + cachedRequest = serviceContext.storage.getStorageMetrics().catch((e) => { logger.error(`Failed to get storage metrics`, e); return null; }); diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index 5b383ade4..64a039693 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -44,7 +44,7 @@ export class ModuleManager { async tearDown() { for (const module of this.modules.values()) { - await module.teardown(); + await module.teardown(this.serviceContext); } } } diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index f80a21bde..27c32625e 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -1,12 +1,13 @@ +import { logger, schema } from '@powersync/lib-services-framework'; import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSyncConfig.js'; import * as t from 'ts-codec'; -import { logger, schema } from '@powersync/lib-services-framework'; + +import * as api from '../../api/api-index.js'; +import * as modules from '../../modules/modules-index.js'; +import * as system from '../../system/system-index.js'; import { ReplicationAdapter } from './ReplicationAdapter.js'; -import { SyncAPI } from '../../api/SyncAPI.js'; -import { AbstractModule, AbstractModuleOptions } from '../../modules/AbstractModule.js'; -import { ServiceContext } from '../../system/ServiceContext.js'; -export interface ReplicationModuleOptions extends AbstractModuleOptions { +export interface ReplicationModuleOptions extends modules.AbstractModuleOptions { type: string; } @@ -14,10 +15,10 @@ export interface ReplicationModuleOptions extends AbstractModuleOptions { * A replication module describes all the functionality that PowerSync requires to * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. */ -export abstract class ReplicationModule extends AbstractModule { +export abstract class ReplicationModule extends modules.AbstractModule { protected type: string; - protected apiAdapters: Set; + protected apiAdapters: Set; protected replicationAdapters: Set; /** @@ -35,7 +36,7 @@ export abstract class ReplicationModule extends AbstractModule { * Create the API adapter for the DataSource required by the sync API * endpoints. */ - protected abstract createSyncAPIAdapter(config: DataSourceConfig): SyncAPI; + protected abstract createSyncAPIAdapter(config: DataSourceConfig): api.RouteAPI; /** * Create the ReplicationAdapter to be used by PowerSync replicator. @@ -50,7 +51,7 @@ export abstract class ReplicationModule extends AbstractModule { /** * Register this module's replication adapters and sync API providers if the required configuration is present. */ - public async initialize(context: ServiceContext): Promise { + public async initialize(context: system.ServiceContext): Promise { if (!context.configuration.data_sources) { // No data source configuration found in the config skip for now // TODO: Consider a mechanism to check for config in the ENV variables as well @@ -76,7 +77,7 @@ export abstract class ReplicationModule extends AbstractModule { const apiAdapter = this.createSyncAPIAdapter(decodedConfig); this.apiAdapters.add(apiAdapter); - context.syncAPIProvider.register(apiAdapter); + context.routerEngine.registerAPI(apiAdapter); } catch (e) { logger.error(e); } diff --git a/packages/service-core/src/util/config.ts b/packages/service-core/src/util/config.ts index d5663cb92..45f3aee85 100644 --- a/packages/service-core/src/util/config.ts +++ b/packages/service-core/src/util/config.ts @@ -1,18 +1,6 @@ import * as fs from 'fs/promises'; -import { container } from '@powersync/lib-services-framework'; -import { ServiceContext } from '../system/ServiceContext.js'; -import { CompoundConfigCollector } from './config/compound-config-collector.js'; -import { ResolvedPowerSyncConfig, RunnerConfig } from './config/types.js'; - -export async function loadConfig(runnerConfig: RunnerConfig = {}) { - const collector = new CompoundConfigCollector(); - const config = await collector.collectConfig(runnerConfig); - const serviceContext = new ServiceContext(config); - container.register(ServiceContext, serviceContext); - await serviceContext.initialize(); - return config; -} +import { ResolvedPowerSyncConfig } from './config/types.js'; export async function loadSyncRules(config: ResolvedPowerSyncConfig): Promise { const sync_rules = config.sync_rules; From 7491daf8ee9a4e79c58de918dc87bf7c57083112 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 15:59:57 +0200 Subject: [PATCH 050/298] restore moved utils. Register supabase key collector. --- .../src/api/PostgresSyncAPIAdapter.ts | 53 +++++++-------- .../src/auth/SupabaseKeyCollector.ts | 8 ++- .../src/module/PostgresModule.ts | 46 ++++++++++--- .../src/replication/replication-utils.ts | 46 ------------- modules/module-postgres/src/types/types.ts | 4 +- .../module-postgres/src/utils/pgwire_utils.ts | 27 +------- packages/service-core/src/auth/KeyStore.ts | 6 +- .../src/replication/core/ReplicationModule.ts | 9 +-- .../service-core/src/routes/RouterEngine.ts | 5 ++ .../util/config/compound-config-collector.ts | 18 ++--- .../service-core/src/util/config/types.ts | 3 +- packages/service-core/src/util/utils.ts | 67 +++++++++++++++++++ 12 files changed, 158 insertions(+), 134 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts index 1f04d7d90..8c9c6631b 100644 --- a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts @@ -1,28 +1,20 @@ import { api, storage } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; -import { DEFAULT_TAG, isJsonValue, SqlSyncRules, TablePattern, toSyncRulesValue } from '@powersync/service-sync-rules'; -import { - configFile, - ConnectionStatusV2, - DatabaseSchema, - internal_routes, - ReplicationError, - TableInfo -} from '@powersync/service-types'; - +import * as sync_rules from '@powersync/service-sync-rules'; +import * as service_types from '@powersync/service-types'; import * as replication_utils from '../replication/replication-utils.js'; -import { baseUri, ResolvedConnectionConfig } from '../types/types.js'; +import * as types from '../types/types.js'; import * as pg_utils from '../utils/pgwire_utils.js'; export class PostgresSyncAPIAdapter implements api.RouteAPI { protected pool: pgwire.PgClient; // TODO this should probably be configurable one day - connectionTag = DEFAULT_TAG; + connectionTag = sync_rules.DEFAULT_TAG; publication_name = 'powersync'; - constructor(protected config: ResolvedConnectionConfig) { + constructor(protected config: types.ResolvedConnectionConfig) { this.pool = pgwire.connectPgWirePool(config, { idleTimeout: 30_000 }); @@ -32,14 +24,14 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { await this.pool.end(); } - async getSourceConfig(): Promise { + async getSourceConfig(): Promise { return this.config; } - async getConnectionStatus(): Promise { + async getConnectionStatus(): Promise { const base = { id: this.config.id, - uri: baseUri(this.config) + uri: types.baseUri(this.config) }; try { @@ -69,9 +61,9 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { }; } - async executeQuery(query: string, params: any[]): Promise { + async executeQuery(query: string, params: any[]): Promise { if (!this.config.debug_api) { - return internal_routes.ExecuteSqlResponse.encode({ + return service_types.internal_routes.ExecuteSqlResponse.encode({ results: { columns: [], rows: [] @@ -87,16 +79,16 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { params: params.map(pg_utils.autoParameter) }); - return internal_routes.ExecuteSqlResponse.encode({ + return service_types.internal_routes.ExecuteSqlResponse.encode({ success: true, results: { columns: result.columns.map((c) => c.name), rows: result.rows.map((row) => { return row.map((value) => { - const sqlValue = toSyncRulesValue(value); + const sqlValue = sync_rules.toSyncRulesValue(value); if (typeof sqlValue == 'bigint') { return Number(value); - } else if (isJsonValue(sqlValue)) { + } else if (sync_rules.isJsonValue(sqlValue)) { return sqlValue; } else { return null; @@ -106,7 +98,7 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { } }); } catch (e) { - return internal_routes.ExecuteSqlResponse.encode({ + return service_types.internal_routes.ExecuteSqlResponse.encode({ results: { columns: [], rows: [] @@ -117,7 +109,10 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { } } - async getDebugTablesInfo(tablePatterns: TablePattern[], sqlSyncRules: SqlSyncRules): Promise { + async getDebugTablesInfo( + tablePatterns: sync_rules.TablePattern[], + sqlSyncRules: sync_rules.SqlSyncRules + ): Promise { let result: api.PatternResult[] = []; for (let tablePattern of tablePatterns) { @@ -184,11 +179,11 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { } protected async getDebugTableInfo( - tablePattern: TablePattern, + tablePattern: sync_rules.TablePattern, name: string, relationId: number | null, - syncRules: SqlSyncRules - ): Promise { + syncRules: sync_rules.SqlSyncRules + ): Promise { const schema = tablePattern.schema; let id_columns_result: replication_utils.ReplicaIdentityResult | undefined = undefined; let id_columns_error = null; @@ -259,7 +254,9 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { replication_id: id_columns.map((c) => c.name), data_queries: syncData, parameter_queries: syncParameters, - errors: [id_columns_error, selectError, replicateError].filter((error) => error != null) as ReplicationError[] + errors: [id_columns_error, selectError, replicateError].filter( + (error) => error != null + ) as service_types.ReplicationError[] }; } @@ -288,7 +285,7 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, return String(lsn); } - async getConnectionSchema(): Promise { + async getConnectionSchema(): Promise { // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts const results = await pg_utils.retriedQuery( this.pool, diff --git a/modules/module-postgres/src/auth/SupabaseKeyCollector.ts b/modules/module-postgres/src/auth/SupabaseKeyCollector.ts index 1cd66f215..c52a9abe2 100644 --- a/modules/module-postgres/src/auth/SupabaseKeyCollector.ts +++ b/modules/module-postgres/src/auth/SupabaseKeyCollector.ts @@ -2,7 +2,7 @@ import { auth } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import * as jose from 'jose'; -import { ResolvedConnectionConfig } from '../types/types.js'; +import * as types from '../types/types.js'; import * as pgwire_utils from '../utils/pgwire_utils.js'; /** @@ -19,7 +19,7 @@ export class SupabaseKeyCollector implements auth.KeyCollector { maxLifetimeSeconds: 86400 * 7 + 1200 // 1 week + 20 minutes margin }; - constructor(connectionConfig: ResolvedConnectionConfig) { + constructor(connectionConfig: types.ResolvedConnectionConfig) { this.pool = pgwire.connectPgWirePool(connectionConfig, { // To avoid overloading the source database with open connections, // limit to a single connection, and close the connection shortly @@ -29,6 +29,10 @@ export class SupabaseKeyCollector implements auth.KeyCollector { }); } + shutdown() { + return this.pool.end(); + } + async getKeys() { let row: { jwt_secret: string }; try { diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index fa4b31db2..d80d3f23b 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,10 +1,12 @@ import * as t from 'ts-codec'; -import { api, db, replication, storage } from '@powersync/service-core'; +import { api, auth, db, replication, storage, system } from '@powersync/service-core'; import { logger } from '@powersync/lib-services-framework'; -import { ServiceContext } from '@powersync/service-core/src/system/ServiceContext.js'; +import * as types from '../types/types.js'; + import { PostgresSyncAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; +import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; import { normalizeConnectionConfig, PostgresConnectionConfig, ResolvedConnectionConfig } from '../types/types.js'; import { terminateReplicators } from '../utils/teardown.js'; @@ -13,17 +15,41 @@ export class PostgresModule extends replication.ReplicationModule { constructor() { super({ name: 'Postgres', - type: 'postgresql' + type: types.POSTGRES_CONNECTION_TYPE }); } - async register(context: ServiceContext): Promise {} - protected configSchema(): t.AnyCodec { // Intersection types have some limitations in codec typing return PostgresConnectionConfig; } + async register(context: system.ServiceContext): Promise {} + + async initialize(context: system.ServiceContext): Promise { + // Register the Supabase key collector(s) + (context.configuration.data_sources ?? []) + .map((baseConfig) => { + if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { + return; + } + try { + return this.resolveConfig(PostgresConnectionConfig.decode(baseConfig as any)); + } catch (ex) { + logger.warn('Failed to decode configuration in Postgres module initialization.'); + } + }) + .filter((c) => !!c) + .forEach((config) => { + const keyCollector = new SupabaseKeyCollector(config); + context.withLifecycle(keyCollector, { + // Close the internal pool + stop: (collector) => collector.shutdown() + }); + context.configuration.client_keystore.add(new auth.CachedKeyCollector(keyCollector)); + }); + } + protected createSyncAPIAdapter(config: PostgresConnectionConfig): api.RouteAPI { throw new PostgresSyncAPIAdapter(this.resolveConfig(config)); } @@ -42,7 +68,7 @@ export class PostgresModule extends replication.ReplicationModule { }; } - async teardown(context: ServiceContext): Promise { + async teardown(context: system.ServiceContext): Promise { const mongoDB = storage.createPowerSyncMongo(context.configuration.storage); try { // TODO this should not be necessary since the service context @@ -54,11 +80,11 @@ export class PostgresModule extends replication.ReplicationModule { await db.mongo.waitForAuth(mongoDB.db); logger.info(`Terminating replication slots`); - const connection = context.configuration.data_sources?.find( - (c) => c.type == 'postgresql' - ) as PostgresConnectionConfig; + const connections = (context.configuration.data_sources ?? []) + .filter((c) => c.type == 'postgresql') + .map((c) => PostgresConnectionConfig.decode(c as any)); - if (connection) { + for (const connection of connections) { await terminateReplicators(context.storage, this.resolveConfig(connection)); } diff --git a/modules/module-postgres/src/replication/replication-utils.ts b/modules/module-postgres/src/replication/replication-utils.ts index 9d430df9d..50b768689 100644 --- a/modules/module-postgres/src/replication/replication-utils.ts +++ b/modules/module-postgres/src/replication/replication-utils.ts @@ -1,10 +1,5 @@ -import bson from 'bson'; -import * as uuid from 'uuid'; - import * as pgwire from '@powersync/service-jpgwire'; -import * as sync_rules from '@powersync/service-sync-rules'; - import * as pgwire_utils from '../utils/pgwire_utils.js'; import { ReplicationColumn, ReplicationIdentity } from './PgRelation.js'; @@ -123,44 +118,3 @@ $$ LANGUAGE plpgsql;` throw new Error(`'${publication_name}' uses publish_via_partition_root, which is not supported.`); } } - -function getRawReplicaIdentity( - tuple: sync_rules.ToastableSqliteRow, - columns: ReplicationColumn[] -): Record { - let result: Record = {}; - for (let column of columns) { - const name = column.name; - result[name] = tuple[name]; - } - return result; -} - -export function getUuidReplicaIdentityString( - tuple: sync_rules.ToastableSqliteRow, - columns: ReplicationColumn[] -): string { - const rawIdentity = getRawReplicaIdentity(tuple, columns); - - return uuidForRow(rawIdentity); -} - -export function uuidForRow(row: sync_rules.SqliteRow): string { - // Important: This must not change, since it will affect how ids are generated. - // Use BSON so that it's a well-defined format without encoding ambiguities. - const repr = bson.serialize(row); - return uuid.v5(repr, pgwire_utils.ID_NAMESPACE); -} - -export function getUuidReplicaIdentityBson( - tuple: sync_rules.ToastableSqliteRow, - columns: ReplicationColumn[] -): bson.UUID { - if (columns.length == 0) { - // REPLICA IDENTITY NOTHING - generate random id - return new bson.UUID(uuid.v4()); - } - const rawIdentity = getRawReplicaIdentity(tuple, columns); - - return pgwire_utils.uuidForRowBson(rawIdentity); -} diff --git a/modules/module-postgres/src/types/types.ts b/modules/module-postgres/src/types/types.ts index 477ce81e8..4f4acbb83 100644 --- a/modules/module-postgres/src/types/types.ts +++ b/modules/module-postgres/src/types/types.ts @@ -2,6 +2,8 @@ import * as service_types from '@powersync/service-types'; import * as t from 'ts-codec'; import * as urijs from 'uri-js'; +export const POSTGRES_CONNECTION_TYPE = 'postgresql' as const; + export interface NormalizedPostgresConnectionConfig { id: string; tag: string; @@ -22,7 +24,7 @@ export interface NormalizedPostgresConnectionConfig { export const PostgresConnectionConfig = service_types.configFile.dataSourceConfig.and( t.object({ - type: t.literal('postgresql'), + type: t.literal(POSTGRES_CONNECTION_TYPE), /** Unique identifier for the connection - optional when a single connection is present. */ id: t.string.optional(), /** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */ diff --git a/modules/module-postgres/src/utils/pgwire_utils.ts b/modules/module-postgres/src/utils/pgwire_utils.ts index 69c4095f6..9a349e06c 100644 --- a/modules/module-postgres/src/utils/pgwire_utils.ts +++ b/modules/module-postgres/src/utils/pgwire_utils.ts @@ -1,14 +1,10 @@ // Adapted from https://github.com/kagis/pgwire/blob/0dc927f9f8990a903f238737326e53ba1c8d094f/mod.js#L2218 import * as pgwire from '@powersync/service-jpgwire'; -import { SqliteJsonValue, SqliteRow, ToastableSqliteRow, toSyncRulesRow } from '@powersync/service-sync-rules'; -import * as bson from 'bson'; -import * as uuid from 'uuid'; +import { SqliteJsonValue, SqliteRow, toSyncRulesRow } from '@powersync/service-sync-rules'; import { logger } from '@powersync/lib-services-framework'; -export const ID_NAMESPACE = 'a396dd91-09fc-4017-a28d-3df722f651e9'; - /** * pgwire message -> SQLite row. * @param message @@ -20,19 +16,6 @@ export function constructAfterRecord(message: pgwire.PgoutputInsert | pgwire.Pgo return toSyncRulesRow(record); } -export function hasToastedValues(row: ToastableSqliteRow) { - for (let key in row) { - if (typeof row[key] == 'undefined') { - return true; - } - } - return false; -} - -export function isCompleteRow(row: ToastableSqliteRow): row is SqliteRow { - return !hasToastedValues(row); -} - /** * pgwire message -> SQLite row. * @param message @@ -46,14 +29,6 @@ export function constructBeforeRecord(message: pgwire.PgoutputDelete | pgwire.Pg return toSyncRulesRow(record); } -export function uuidForRowBson(row: SqliteRow): bson.UUID { - // Important: This must not change, since it will affect how ids are generated. - // Use BSON so that it's a well-defined format without encoding ambiguities. - const repr = bson.serialize(row); - const buffer = Buffer.alloc(16); - return new bson.UUID(uuid.v5(repr, ID_NAMESPACE, buffer)); -} - export function escapeIdentifier(identifier: string) { return `"${identifier.replace(/"/g, '""').replace(/\./g, '"."')}"`; } diff --git a/packages/service-core/src/auth/KeyStore.ts b/packages/service-core/src/auth/KeyStore.ts index 9392d1cd3..4135c1785 100644 --- a/packages/service-core/src/auth/KeyStore.ts +++ b/packages/service-core/src/auth/KeyStore.ts @@ -1,9 +1,9 @@ +import { logger } from '@powersync/lib-services-framework'; import * as jose from 'jose'; import secs from '../util/secs.js'; -import { KeyOptions, KeySpec, SUPPORTED_ALGORITHMS } from './KeySpec.js'; -import { KeyCollector } from './KeyCollector.js'; import { JwtPayload } from './JwtPayload.js'; -import { logger } from '@powersync/lib-services-framework'; +import { KeyCollector } from './KeyCollector.js'; +import { KeyOptions, KeySpec, SUPPORTED_ALGORITHMS } from './KeySpec.js'; /** * KeyStore to get keys and verify tokens. diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 27c32625e..5419b6f4f 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -18,7 +18,6 @@ export interface ReplicationModuleOptions extends modules.AbstractModuleOptions export abstract class ReplicationModule extends modules.AbstractModule { protected type: string; - protected apiAdapters: Set; protected replicationAdapters: Set; /** @@ -28,7 +27,6 @@ export abstract class ReplicationModule extends modules.AbstractModule { protected constructor(options: ReplicationModuleOptions) { super(options); this.type = options.type; - this.apiAdapters = new Set(); this.replicationAdapters = new Set(); } @@ -54,7 +52,6 @@ export abstract class ReplicationModule extends modules.AbstractModule { public async initialize(context: system.ServiceContext): Promise { if (!context.configuration.data_sources) { // No data source configuration found in the config skip for now - // TODO: Consider a mechanism to check for config in the ENV variables as well return; } @@ -76,7 +73,6 @@ export abstract class ReplicationModule extends modules.AbstractModule { context.replicationEngine.register(replicationAdapter); const apiAdapter = this.createSyncAPIAdapter(decodedConfig); - this.apiAdapters.add(apiAdapter); context.routerEngine.registerAPI(apiAdapter); } catch (e) { logger.error(e); @@ -96,10 +92,7 @@ export abstract class ReplicationModule extends modules.AbstractModule { } public async shutdown(): Promise { - for (const api of this.apiAdapters) { - await api.shutdown(); - } - + // TODO maybe manage this in the Replication engine for (const replication of this.replicationAdapters) { await replication.shutdown(); } diff --git a/packages/service-core/src/routes/RouterEngine.ts b/packages/service-core/src/routes/RouterEngine.ts index affa39dea..231955c14 100644 --- a/packages/service-core/src/routes/RouterEngine.ts +++ b/packages/service-core/src/routes/RouterEngine.ts @@ -88,7 +88,12 @@ export class RouterEngine { } logger.info(`Running close cleanup`); + + // Typically closes the server await this.cleanupHandler?.(); + + // Close the api handlers + await this.api?.shutdown(); } /** diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 2bcde30b9..448ab7873 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -1,15 +1,15 @@ +import { logger } from '@powersync/lib-services-framework'; import { configFile } from '@powersync/service-types'; -import { ConfigCollector } from './collectors/config-collector.js'; -import { ResolvedPowerSyncConfig, RunnerConfig, SyncRulesConfig } from './types.js'; import * as auth from '../../auth/auth-index.js'; -import { SyncRulesCollector } from './sync-rules/sync-collector.js'; +import { ConfigCollector } from './collectors/config-collector.js'; import { Base64ConfigCollector } from './collectors/impl/base64-config-collector.js'; +import { FallbackConfigCollector } from './collectors/impl/fallback-config-collector.js'; import { FileSystemConfigCollector } from './collectors/impl/filesystem-config-collector.js'; import { Base64SyncRulesCollector } from './sync-rules/impl/base64-sync-rules-collector.js'; -import { InlineSyncRulesCollector } from './sync-rules/impl/inline-sync-rules-collector.js'; import { FileSystemSyncRulesCollector } from './sync-rules/impl/filesystem-sync-rules-collector.js'; -import { FallbackConfigCollector } from './collectors/impl/fallback-config-collector.js'; -import { logger } from '@powersync/lib-services-framework'; +import { InlineSyncRulesCollector } from './sync-rules/impl/inline-sync-rules-collector.js'; +import { SyncRulesCollector } from './sync-rules/sync-collector.js'; +import { ResolvedPowerSyncConfig, RunnerConfig, SyncRulesConfig } from './types.js'; const POWERSYNC_DEV_KID = 'powersync-dev'; @@ -43,8 +43,8 @@ export class CompoundConfigCollector { /** * Collects and resolves base config */ - async collectConfig(runner_config: RunnerConfig = {}): Promise { - const baseConfig = await this.collectBaseConfig(runner_config); + async collectConfig(runnerConfig: RunnerConfig = {}): Promise { + const baseConfig = await this.collectBaseConfig(runnerConfig); const dataSources = baseConfig.replication?.data_sources ?? []; if (dataSources.length > 1) { @@ -79,7 +79,7 @@ export class CompoundConfigCollector { devKey = await auth.KeySpec.importKey(baseDevKey); } - const sync_rules = await this.collectSyncRules(baseConfig, runner_config); + const sync_rules = await this.collectSyncRules(baseConfig, runnerConfig); let jwt_audiences: string[] = baseConfig.client_auth?.audience ?? []; diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index b98709b76..aaa6f0874 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -1,4 +1,5 @@ import { configFile } from '@powersync/service-types'; +import { CompoundKeyCollector } from '../../auth/CompoundKeyCollector.js'; import { KeySpec } from '../../auth/KeySpec.js'; import { KeyStore } from '../../auth/KeyStore.js'; import { ServiceContext } from '../../system/ServiceContext.js'; @@ -40,7 +41,7 @@ export type ResolvedPowerSyncConfig = { */ dev_key?: KeySpec; }; - client_keystore: KeyStore; + client_keystore: CompoundKeyCollector; /** * Keystore for development tokens. */ diff --git a/packages/service-core/src/util/utils.ts b/packages/service-core/src/util/utils.ts index 4d2b7f1ff..fcd98efd3 100644 --- a/packages/service-core/src/util/utils.ts +++ b/packages/service-core/src/util/utils.ts @@ -1,8 +1,13 @@ +import * as sync_rules from '@powersync/service-sync-rules'; +import bson from 'bson'; import crypto from 'crypto'; +import * as uuid from 'uuid'; import { BucketChecksum, OpId } from './protocol-types.js'; export type ChecksumMap = Map; +export const ID_NAMESPACE = 'a396dd91-09fc-4017-a28d-3df722f651e9'; + export function escapeIdentifier(identifier: string) { return `"${identifier.replace(/"/g, '""').replace(/\./g, '"."')}"`; } @@ -73,3 +78,65 @@ export function addBucketChecksums(a: BucketChecksum, b: BucketChecksum | null): }; } } + +function getRawReplicaIdentity( + tuple: sync_rules.ToastableSqliteRow, + columns: ReplicationColumn[] +): Record { + let result: Record = {}; + for (let column of columns) { + const name = column.name; + result[name] = tuple[name]; + } + return result; +} + +export function getUuidReplicaIdentityString( + tuple: sync_rules.ToastableSqliteRow, + columns: ReplicationColumn[] +): string { + const rawIdentity = getRawReplicaIdentity(tuple, columns); + + return uuidForRow(rawIdentity); +} + +export function uuidForRow(row: sync_rules.SqliteRow): string { + // Important: This must not change, since it will affect how ids are generated. + // Use BSON so that it's a well-defined format without encoding ambiguities. + const repr = bson.serialize(row); + return uuid.v5(repr, ID_NAMESPACE); +} + +export function getUuidReplicaIdentityBson( + tuple: sync_rules.ToastableSqliteRow, + columns: ReplicationColumn[] +): bson.UUID { + if (columns.length == 0) { + // REPLICA IDENTITY NOTHING - generate random id + return new bson.UUID(uuid.v4()); + } + const rawIdentity = getRawReplicaIdentity(tuple, columns); + + return uuidForRowBson(rawIdentity); +} + +export function uuidForRowBson(row: sync_rules.SqliteRow): bson.UUID { + // Important: This must not change, since it will affect how ids are generated. + // Use BSON so that it's a well-defined format without encoding ambiguities. + const repr = bson.serialize(row); + const buffer = Buffer.alloc(16); + return new bson.UUID(uuid.v5(repr, ID_NAMESPACE, buffer)); +} + +export function hasToastedValues(row: sync_rules.ToastableSqliteRow) { + for (let key in row) { + if (typeof row[key] == 'undefined') { + return true; + } + } + return false; +} + +export function isCompleteRow(row: sync_rules.ToastableSqliteRow): row is sync_rules.SqliteRow { + return !hasToastedValues(row); +} From 3a689b3fd0801669ee8051f092c9cbbbbdb53904 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 16:07:09 +0200 Subject: [PATCH 051/298] typing for keystore --- modules/module-postgres/src/module/PostgresModule.ts | 2 +- packages/service-core/src/auth/KeyStore.ts | 9 ++++++--- .../src/replication/core/ReplicationEngine.ts | 6 +++--- packages/service-core/src/replication/core/Replicator.ts | 6 +++--- packages/service-core/src/util/config/types.ts | 2 +- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index d80d3f23b..c42137c71 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -46,7 +46,7 @@ export class PostgresModule extends replication.ReplicationModule { // Close the internal pool stop: (collector) => collector.shutdown() }); - context.configuration.client_keystore.add(new auth.CachedKeyCollector(keyCollector)); + context.configuration.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); }); } diff --git a/packages/service-core/src/auth/KeyStore.ts b/packages/service-core/src/auth/KeyStore.ts index 4135c1785..aed671a13 100644 --- a/packages/service-core/src/auth/KeyStore.ts +++ b/packages/service-core/src/auth/KeyStore.ts @@ -32,10 +32,13 @@ import { KeyOptions, KeySpec, SUPPORTED_ALGORITHMS } from './KeySpec.js'; * If we have a matching kid, we can generally get a detailed error (e.g. signature verification failed, invalid algorithm, etc). * If we don't have a matching kid, we'll generally just get an error "Could not find an appropriate key...". */ -export class KeyStore { - private collector: KeyCollector; +export class KeyStore { + /** + * @internal + */ + collector: Collector; - constructor(collector: KeyCollector) { + constructor(collector: Collector) { this.collector = collector; } diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index abcbb346f..e9aec6472 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -1,5 +1,5 @@ -import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +import { ReplicationAdapter } from './ReplicationAdapter.js'; import { Replicator } from './Replicator.js'; export interface ReplicationEngineOptions { @@ -8,7 +8,7 @@ export interface ReplicationEngineOptions { export class ReplicationEngine { private readonly options: ReplicationEngineOptions; - private readonly replicators: Map, Replicator> = new Map(); + private readonly replicators: Map = new Map(); constructor(options: ReplicationEngineOptions) { this.options = options; @@ -20,7 +20,7 @@ export class ReplicationEngine { * * @param adapter */ - public register(adapter: ReplicationAdapter) { + public register(adapter: ReplicationAdapter) { if (this.replicators.has(adapter)) { throw new Error(`Replicator for type ${adapter.name} already registered`); } diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts index 047f9b688..2426641d4 100644 --- a/packages/service-core/src/replication/core/Replicator.ts +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -1,8 +1,8 @@ -import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +import { ReplicationAdapter } from './ReplicationAdapter.js'; export interface ReplicatorOptions { - adapter: ReplicationAdapter; + adapter: ReplicationAdapter; storage: BucketStorageFactory; } @@ -12,7 +12,7 @@ export interface ReplicatorOptions { * TODO: Implement this. This will replace the current WallStreamManager */ export class Replicator { - private readonly adapter: ReplicationAdapter; + private readonly adapter: ReplicationAdapter; private storage: BucketStorageFactory; constructor(options: ReplicatorOptions) { diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index aaa6f0874..9fb035952 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -41,7 +41,7 @@ export type ResolvedPowerSyncConfig = { */ dev_key?: KeySpec; }; - client_keystore: CompoundKeyCollector; + client_keystore: KeyStore; /** * Keystore for development tokens. */ From 4d0fbb007c76078b449f19d26b22aa0e68b7411d Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 30 Jul 2024 16:15:46 +0200 Subject: [PATCH 052/298] Replaced PgRelation with SourceEntityDescriptor to remove postgres specific fields. --- .../service-core/src/replication/WalStream.ts | 22 +++++------ packages/service-core/src/replication/util.ts | 39 +++++++++++-------- .../service-core/src/storage/BucketStorage.ts | 5 +-- .../service-core/src/storage/SourceEntity.ts | 21 ++++++++++ .../service-core/src/storage/SourceTable.ts | 7 ++-- .../src/storage/mongo/MongoBucketBatch.ts | 2 +- .../storage/mongo/MongoSyncBucketStorage.ts | 20 ++++++---- .../service-core/src/storage/mongo/models.ts | 4 +- .../service-core/src/util/pgwire_utils.ts | 19 ++++----- 9 files changed, 83 insertions(+), 56 deletions(-) create mode 100644 packages/service-core/src/storage/SourceEntity.ts diff --git a/packages/service-core/src/replication/WalStream.ts b/packages/service-core/src/replication/WalStream.ts index c672b133b..2d6e82469 100644 --- a/packages/service-core/src/replication/WalStream.ts +++ b/packages/service-core/src/replication/WalStream.ts @@ -5,10 +5,11 @@ import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow import * as storage from '../storage/storage-index.js'; import * as util from '../util/util-index.js'; -import { getPgOutputRelation, getRelId, PgRelation } from './PgRelation.js'; +import { getPgOutputRelation, getRelId } from './PgRelation.js'; import { getReplicationIdentityColumns } from './util.js'; import { WalConnection } from './WalConnection.js'; import { Metrics } from '../metrics/Metrics.js'; +import { SourceEntityDescriptor } from '../storage/SourceEntity.js'; export const ZERO_LSN = '00000000/00000000'; @@ -44,7 +45,7 @@ export class WalStream { private abort_signal: AbortSignal; - private relation_cache = new Map(); + private relation_cache = new Map(); private startedStreaming = false; @@ -168,10 +169,9 @@ export class WalStream { { name, schema, - relationId: relid, - replicaIdentity: cresult.replicationIdentity, - replicationColumns: cresult.columns - }, + objectId: relid, + replicationColumns: cresult.replicationColumns + } as SourceEntityDescriptor, false ); @@ -414,18 +414,18 @@ WHERE oid = $1::regclass`, await batch.flush(); } - async handleRelation(batch: storage.BucketStorageBatch, relation: PgRelation, snapshot: boolean) { - if (relation.relationId == null || typeof relation.relationId != 'number') { - throw new Error('relationId expected'); + async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { + if (!descriptor.objectId) { + throw new Error('objectId expected'); } const result = await this.storage.resolveTable({ group_id: this.group_id, connection_id: this.connection_id, connection_tag: this.connectionTag, - relation: relation, + entity_descriptor: descriptor, sync_rules: this.sync_rules }); - this.relation_cache.set(relation.relationId, result.table); + this.relation_cache.set(descriptor.objectId, result.table); // Drop conflicting tables. This includes for example renamed tables. await batch.drop(result.dropTables); diff --git a/packages/service-core/src/replication/util.ts b/packages/service-core/src/replication/util.ts index 8c719205a..7c0b4b4da 100644 --- a/packages/service-core/src/replication/util.ts +++ b/packages/service-core/src/replication/util.ts @@ -1,10 +1,11 @@ import * as pgwire from '@powersync/service-jpgwire'; import * as util from '../util/util-index.js'; -import { ReplicationColumn, ReplicationIdentity } from './PgRelation.js'; +import { ReplicationIdentity } from './PgRelation.js'; +import { ColumnDescriptor } from '../storage/SourceEntity.js'; export interface ReplicaIdentityResult { - columns: ReplicationColumn[]; + replicationColumns: ColumnDescriptor[]; replicationIdentity: ReplicationIdentity; } @@ -12,14 +13,13 @@ export async function getPrimaryKeyColumns( db: pgwire.PgClient, relationId: number, mode: 'primary' | 'replident' -): Promise { +): Promise { const indexFlag = mode == 'primary' ? `i.indisprimary` : `i.indisreplident`; const attrRows = await util.retriedQuery(db, { - statement: `SELECT a.attname as name, a.atttypid as typeid, a.attnum as attnum + statement: `SELECT a.attname as name, a.atttypid as typeid, t.typname as type, a.attnum as attnum FROM pg_index i - JOIN pg_attribute a - ON a.attrelid = i.indrelid - AND a.attnum = ANY (i.indkey) + JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY (i.indkey) + JOIN pg_type t ON a.atttypid = t.oid WHERE i.indrelid = $1::oid AND ${indexFlag} AND a.attnum > 0 @@ -28,21 +28,22 @@ export async function getPrimaryKeyColumns( }); return attrRows.rows.map((row) => { - return { name: row[0] as string, typeOid: row[1] as number }; + return { name: row[0] as string, typeOid: row[1] as number, type: row[2] as string }; }); } -export async function getAllColumns(db: pgwire.PgClient, relationId: number): Promise { +export async function getAllColumns(db: pgwire.PgClient, relationId: number): Promise { const attrRows = await util.retriedQuery(db, { - statement: `SELECT a.attname as name, a.atttypid as typeid, a.attnum as attnum + statement: `SELECT a.attname as name, a.atttypid as typeid, t.typname as type, a.attnum as attnum FROM pg_attribute a + JOIN pg_type t ON a.atttypid = t.oid WHERE a.attrelid = $1::oid AND attnum > 0 ORDER BY a.attnum`, params: [{ type: 'varchar', value: relationId }] }); return attrRows.rows.map((row) => { - return { name: row[0] as string, typeOid: row[1] as number }; + return { name: row[0] as string, typeOid: row[1] as number, type: row[2] as string }; }); } @@ -63,14 +64,20 @@ WHERE oid = $1::oid LIMIT 1`, }); const idType: string = rows.rows[0]?.[0]; if (idType == 'nothing' || idType == null) { - return { replicationIdentity: 'nothing', columns: [] }; + return { replicationIdentity: 'nothing', replicationColumns: [] }; } else if (idType == 'full') { - return { replicationIdentity: 'full', columns: await getAllColumns(db, relationId) }; + return { replicationIdentity: 'full', replicationColumns: await getAllColumns(db, relationId) }; } else if (idType == 'default') { - return { replicationIdentity: 'default', columns: await getPrimaryKeyColumns(db, relationId, 'primary') }; + return { + replicationIdentity: 'default', + replicationColumns: await getPrimaryKeyColumns(db, relationId, 'primary') + }; } else if (idType == 'index') { - return { replicationIdentity: 'index', columns: await getPrimaryKeyColumns(db, relationId, 'replident') }; + return { + replicationIdentity: 'index', + replicationColumns: await getPrimaryKeyColumns(db, relationId, 'replident') + }; } else { - return { replicationIdentity: 'nothing', columns: [] }; + return { replicationIdentity: 'nothing', replicationColumns: [] }; } } diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 24d46e25f..8cf125149 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -7,10 +7,9 @@ import { SqliteRow, ToastableSqliteRow } from '@powersync/service-sync-rules'; - -import * as replication from '../replication/replication-index.js'; import * as util from '../util/util-index.js'; import { SourceTable } from './SourceTable.js'; +import { SourceEntityDescriptor } from './SourceEntity.js'; export interface BucketStorageFactory { /** @@ -279,7 +278,7 @@ export interface ResolveTableOptions { group_id: number; connection_id: number; connection_tag: string; - relation: replication.PgRelation; + entity_descriptor: SourceEntityDescriptor; sync_rules: SqlSyncRules; } diff --git a/packages/service-core/src/storage/SourceEntity.ts b/packages/service-core/src/storage/SourceEntity.ts new file mode 100644 index 000000000..501d5fd9d --- /dev/null +++ b/packages/service-core/src/storage/SourceEntity.ts @@ -0,0 +1,21 @@ +export interface ColumnDescriptor { + name: string; + /** + * The type of the column ie VARCHAR, INT, etc + */ + type: string; + /** + * Some data sources have a type id that can be used to identify the type of the column + */ + typeId?: number; +} + +export interface SourceEntityDescriptor { + /** + * The internal id of the data source structure in the database + */ + objectId: number | string; + schema: string; + name: string; + replicationColumns: ColumnDescriptor[]; +} diff --git a/packages/service-core/src/storage/SourceTable.ts b/packages/service-core/src/storage/SourceTable.ts index 960ee4881..6379732f5 100644 --- a/packages/service-core/src/storage/SourceTable.ts +++ b/packages/service-core/src/storage/SourceTable.ts @@ -1,7 +1,6 @@ import { DEFAULT_SCHEMA, DEFAULT_TAG } from '@powersync/service-sync-rules'; - -import * as replication from '../replication/replication-index.js'; import * as util from '../util/util-index.js'; +import { ColumnDescriptor } from './SourceEntity.js'; export class SourceTable { static readonly DEFAULT_SCHEMA = DEFAULT_SCHEMA; @@ -28,11 +27,11 @@ export class SourceTable { constructor( public readonly id: any, public readonly connectionTag: string, - public readonly relationId: number, + public readonly objectId: number | string, public readonly schema: string, public readonly table: string, - public readonly replicaIdColumns: replication.ReplicationColumn[], + public readonly replicaIdColumns: ColumnDescriptor[], public readonly snapshotComplete: boolean ) {} diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index a1b6181f2..a2642f3bf 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -743,7 +743,7 @@ export class MongoBucketBatch implements BucketStorageBatch { const copy = new SourceTable( table.id, table.connectionTag, - table.relationId, + table.objectId, table.schema, table.table, table.replicaIdColumns, diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 2039b34cf..7ef2f4e63 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -90,11 +90,15 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { } async resolveTable(options: ResolveTableOptions): Promise { - const { group_id, connection_id, connection_tag, relation } = options; + const { group_id, connection_id, connection_tag, entity_descriptor } = options; - const { schema, name: table, relationId, replicationColumns } = relation; + const { schema, name: table, objectId, replicationColumns } = entity_descriptor; - const columns = replicationColumns.map((column) => ({ name: column.name, type_oid: column.typeOid })); + const columns = replicationColumns.map((column) => ({ + name: column.name, + type: column.type, + type_oid: column.typeId + })); let result: ResolveTableResult | null = null; await this.db.client.withSession(async (session) => { const col = this.db.source_tables; @@ -102,7 +106,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { { group_id: group_id, connection_id: connection_id, - relation_id: relationId, + relation_id: objectId, schema_name: schema, table_name: table, replica_id_columns2: columns @@ -114,7 +118,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { _id: new bson.ObjectId(), group_id: group_id, connection_id: connection_id, - relation_id: relationId, + relation_id: objectId, schema_name: schema, table_name: table, replica_id_columns: null, @@ -127,7 +131,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { const sourceTable = new SourceTable( doc._id, connection_tag, - relationId, + objectId, schema, table, replicationColumns, @@ -142,7 +146,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { group_id: group_id, connection_id: connection_id, _id: { $ne: doc._id }, - $or: [{ relation_id: relationId }, { schema_name: schema, table_name: table }] + $or: [{ relation_id: objectId }, { schema_name: schema, table_name: table }] }, { session } ) @@ -157,7 +161,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { doc.relation_id ?? 0, doc.schema_name, doc.table_name, - doc.replica_id_columns2?.map((c) => ({ name: c.name, typeOid: c.type_oid })) ?? [], + doc.replica_id_columns2?.map((c) => ({ name: c.name, typeOid: c.type_oid, type: c.type })) ?? [], doc.snapshot_done ?? true ) ) diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index 8ac4ed301..3c88eab62 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -57,11 +57,11 @@ export interface SourceTableDocument { _id: bson.ObjectId; group_id: number; connection_id: number; - relation_id: number | undefined; + relation_id: number | undefined | string; schema_name: string; table_name: string; replica_id_columns: string[] | null; - replica_id_columns2: { name: string; type_oid: number }[] | undefined; + replica_id_columns2: { name: string; type_oid: number | undefined; type: string }[] | undefined; snapshot_done: boolean | undefined; } diff --git a/packages/service-core/src/util/pgwire_utils.ts b/packages/service-core/src/util/pgwire_utils.ts index 9aa042980..3efef6a63 100644 --- a/packages/service-core/src/util/pgwire_utils.ts +++ b/packages/service-core/src/util/pgwire_utils.ts @@ -5,8 +5,8 @@ import * as uuid from 'uuid'; import * as pgwire from '@powersync/service-jpgwire'; import { SqliteJsonValue, SqliteRow, ToastableSqliteRow, toSyncRulesRow } from '@powersync/service-sync-rules'; -import * as replication from '../replication/replication-index.js'; import { logger } from '@powersync/lib-services-framework'; +import { ColumnDescriptor } from '../storage/SourceEntity.js'; /** * pgwire message -> SQLite row. @@ -45,12 +45,9 @@ export function constructBeforeRecord(message: pgwire.PgoutputDelete | pgwire.Pg return toSyncRulesRow(record); } -function getRawReplicaIdentity( - tuple: ToastableSqliteRow, - columns: replication.ReplicationColumn[] -): Record { +function getRawReplicaIdentity(tuple: ToastableSqliteRow, replicationColumns: ColumnDescriptor[]): Record { let result: Record = {}; - for (let column of columns) { + for (let column of replicationColumns) { const name = column.name; result[name] = tuple[name]; } @@ -60,9 +57,9 @@ const ID_NAMESPACE = 'a396dd91-09fc-4017-a28d-3df722f651e9'; export function getUuidReplicaIdentityString( tuple: ToastableSqliteRow, - columns: replication.ReplicationColumn[] + replicationColumns: ColumnDescriptor[] ): string { - const rawIdentity = getRawReplicaIdentity(tuple, columns); + const rawIdentity = getRawReplicaIdentity(tuple, replicationColumns); return uuidForRow(rawIdentity); } @@ -76,13 +73,13 @@ export function uuidForRow(row: SqliteRow): string { export function getUuidReplicaIdentityBson( tuple: ToastableSqliteRow, - columns: replication.ReplicationColumn[] + replicationColumns: ColumnDescriptor[] ): bson.UUID { - if (columns.length == 0) { + if (replicationColumns.length == 0) { // REPLICA IDENTITY NOTHING - generate random id return new bson.UUID(uuid.v4()); } - const rawIdentity = getRawReplicaIdentity(tuple, columns); + const rawIdentity = getRawReplicaIdentity(tuple, replicationColumns); return uuidForRowBson(rawIdentity); } From e1565d5e0935dd13854f9dae5abd15419b8ded87 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 30 Jul 2024 16:20:30 +0200 Subject: [PATCH 053/298] Add missed changed files to commit --- .../src/replication/PgRelation.ts | 27 ++++++------------- .../src/replication/WalConnection.ts | 2 +- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/packages/service-core/src/replication/PgRelation.ts b/packages/service-core/src/replication/PgRelation.ts index 1635487c0..6dd95547f 100644 --- a/packages/service-core/src/replication/PgRelation.ts +++ b/packages/service-core/src/replication/PgRelation.ts @@ -1,42 +1,31 @@ import { PgoutputRelation } from '@powersync/service-jpgwire'; - -export interface PgRelation { - readonly relationId: number; - readonly schema: string; - readonly name: string; - readonly replicaIdentity: ReplicationIdentity; - readonly replicationColumns: ReplicationColumn[]; -} +import { ColumnDescriptor, SourceEntityDescriptor } from '../storage/SourceEntity.js'; export type ReplicationIdentity = 'default' | 'nothing' | 'full' | 'index'; -export interface ReplicationColumn { - readonly name: string; - readonly typeOid: number; -} - -export function getReplicaIdColumns(relation: PgoutputRelation): ReplicationColumn[] { +export function getReplicaIdColumns(relation: PgoutputRelation): ColumnDescriptor[] { if (relation.replicaIdentity == 'nothing') { return []; } else { - return relation.columns.filter((c) => (c.flags & 0b1) != 0).map((c) => ({ name: c.name, typeOid: c.typeOid })); + return relation.columns + .filter((c) => (c.flags & 0b1) != 0) + .map((c) => ({ name: c.name, typeId: c.typeOid, type: c.typeName ?? '' })); } } export function getRelId(source: PgoutputRelation): number { // Source types are wrong here const relId = (source as any).relationOid as number; - if (relId == null || typeof relId != 'number') { + if (!relId) { throw new Error(`No relation id!`); } return relId; } -export function getPgOutputRelation(source: PgoutputRelation): PgRelation { +export function getPgOutputRelation(source: PgoutputRelation): SourceEntityDescriptor { return { name: source.name, schema: source.schema, - relationId: getRelId(source), - replicaIdentity: source.replicaIdentity, + objectId: getRelId(source), replicationColumns: getReplicaIdColumns(source) }; } diff --git a/packages/service-core/src/replication/WalConnection.ts b/packages/service-core/src/replication/WalConnection.ts index 5fa3cf8a7..bee1f4385 100644 --- a/packages/service-core/src/replication/WalConnection.ts +++ b/packages/service-core/src/replication/WalConnection.ts @@ -46,7 +46,7 @@ export class WalConnection { } } - const id_columns = id_columns_result?.columns ?? []; + const id_columns = id_columns_result?.replicationColumns ?? []; const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); From ed4435a051eabb60b2d886bc03ba90e5b5bd5e00 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 16:28:43 +0200 Subject: [PATCH 054/298] delete duplicate --- .../src/replication/WalStream.ts | 624 ------------------ 1 file changed, 624 deletions(-) delete mode 100644 modules/module-postgres/src/replication/WalStream.ts diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts deleted file mode 100644 index 130790bc8..000000000 --- a/modules/module-postgres/src/replication/WalStream.ts +++ /dev/null @@ -1,624 +0,0 @@ -// import * as pgwire from '@powersync/service-jpgwire'; -// import { container, errors, logger } from '@powersync/lib-services-framework'; -// import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; - -// import * as storage from '../storage/storage-index.js'; -// import * as util from '../util/util-index.js'; - -// import { getPgOutputRelation, getRelId, PgRelation } from './PgRelation.js'; -// import { getReplicationIdentityColumns } from './util.js'; -// import { WalConnection } from './WalConnection.js'; -// import { Metrics } from '../metrics/Metrics.js'; - -// export const ZERO_LSN = '00000000/00000000'; - -// export interface WalStreamOptions { -// connections: util.PgManager; -// factory: storage.BucketStorageFactory; -// storage: storage.SyncRulesBucketStorage; -// abort_signal: AbortSignal; -// } - -// interface InitResult { -// needsInitialSync: boolean; -// } - -// export class MissingReplicationSlotError extends Error { -// constructor(message: string) { -// super(message); -// } -// } - -// export class WalStream { -// sync_rules: SqlSyncRules; -// group_id: number; - -// wal_connection: WalConnection; -// connection_id = 1; - -// private readonly storage: storage.SyncRulesBucketStorage; - -// private slot_name: string; - -// private connections: util.PgManager; - -// private abort_signal: AbortSignal; - -// private relation_cache = new Map(); - -// private startedStreaming = false; - -// constructor(options: WalStreamOptions) { -// this.storage = options.storage; -// this.sync_rules = options.storage.sync_rules; -// this.group_id = options.storage.group_id; -// this.slot_name = options.storage.slot_name; -// this.connections = options.connections; - -// this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); -// this.abort_signal = options.abort_signal; -// this.abort_signal.addEventListener( -// 'abort', -// () => { -// if (this.startedStreaming) { -// // Ping to speed up cancellation of streaming replication -// // We're not using pg_snapshot here, since it could be in the middle of -// // an initial replication transaction. -// const promise = util.retriedQuery( -// this.connections.pool, -// `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` -// ); -// promise.catch((e) => { -// // Failures here are okay - this only speeds up stopping the process. -// logger.warn('Failed to ping connection', e); -// }); -// } else { -// // If we haven't started streaming yet, it could be due to something like -// // and invalid password. In that case, don't attempt to ping. -// } -// }, -// { once: true } -// ); -// } - -// get publication_name() { -// return this.wal_connection.publication_name; -// } - -// get connectionTag() { -// return this.wal_connection.connectionTag; -// } - -// get stopped() { -// return this.abort_signal.aborted; -// } - -// async getQualifiedTableNames( -// batch: storage.BucketStorageBatch, -// db: pgwire.PgConnection, -// tablePattern: TablePattern -// ): Promise { -// const schema = tablePattern.schema; -// if (tablePattern.connectionTag != this.connectionTag) { -// return []; -// } - -// let tableRows: any[]; -// const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; -// if (tablePattern.isWildcard) { -// const result = await db.query({ -// statement: `SELECT c.oid AS relid, c.relname AS table_name -// FROM pg_class c -// JOIN pg_namespace n ON n.oid = c.relnamespace -// WHERE n.nspname = $1 -// AND c.relkind = 'r' -// AND c.relname LIKE $2`, -// params: [ -// { type: 'varchar', value: schema }, -// { type: 'varchar', value: tablePattern.tablePattern } -// ] -// }); -// tableRows = pgwire.pgwireRows(result); -// } else { -// const result = await db.query({ -// statement: `SELECT c.oid AS relid, c.relname AS table_name -// FROM pg_class c -// JOIN pg_namespace n ON n.oid = c.relnamespace -// WHERE n.nspname = $1 -// AND c.relkind = 'r' -// AND c.relname = $2`, -// params: [ -// { type: 'varchar', value: schema }, -// { type: 'varchar', value: tablePattern.tablePattern } -// ] -// }); - -// tableRows = pgwire.pgwireRows(result); -// } -// let result: storage.SourceTable[] = []; - -// for (let row of tableRows) { -// const name = row.table_name as string; -// if (typeof row.relid != 'bigint') { -// throw new Error(`missing relid for ${name}`); -// } -// const relid = Number(row.relid as bigint); - -// if (prefix && !name.startsWith(prefix)) { -// continue; -// } - -// const rs = await db.query({ -// statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, -// params: [ -// { type: 'varchar', value: this.publication_name }, -// { type: 'varchar', value: tablePattern.schema }, -// { type: 'varchar', value: name } -// ] -// }); -// if (rs.rows.length == 0) { -// logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); -// continue; -// } - -// const cresult = await getReplicationIdentityColumns(db, relid); - -// const table = await this.handleRelation( -// batch, -// { -// name, -// schema, -// relationId: relid, -// replicaIdentity: cresult.replicationIdentity, -// replicationColumns: cresult.columns -// }, -// false -// ); - -// result.push(table); -// } -// return result; -// } - -// async initSlot(): Promise { -// await this.wal_connection.checkSourceConfiguration(); - -// const slotName = this.slot_name; - -// const status = await this.storage.getStatus(); -// if (status.snapshot_done && status.checkpoint_lsn) { -// logger.info(`${slotName} Initial replication already done`); - -// let last_error = null; - -// // Check that replication slot exists -// for (let i = 120; i >= 0; i--) { -// await touch(); - -// if (i == 0) { -// container.reporter.captureException(last_error, { -// level: errors.ErrorSeverity.ERROR, -// metadata: { -// replication_slot: slotName -// } -// }); - -// throw last_error; -// } -// try { -// // We peek a large number of changes here, to make it more likely to pick up replication slot errors. -// // For example, "publication does not exist" only occurs here if the peek actually includes changes related -// // to the slot. -// await this.connections.pool.query({ -// statement: `SELECT * -// FROM pg_catalog.pg_logical_slot_peek_binary_changes($1, NULL, 1000, 'proto_version', '1', -// 'publication_names', $2)`, -// params: [ -// { type: 'varchar', value: slotName }, -// { type: 'varchar', value: this.publication_name } -// ] -// }); -// // Success -// logger.info(`Slot ${slotName} appears healthy`); -// return { needsInitialSync: false }; -// } catch (e) { -// last_error = e; -// logger.warn(`${slotName} Replication slot error`, e); - -// if (this.stopped) { -// throw e; -// } - -// // Could also be `publication "powersync" does not exist`, although this error may show up much later -// // in some cases. - -// if ( -// /incorrect prev-link/.test(e.message) || -// /replication slot.*does not exist/.test(e.message) || -// /publication.*does not exist/.test(e.message) -// ) { -// container.reporter.captureException(e, { -// level: errors.ErrorSeverity.WARNING, -// metadata: { -// try_index: i, -// replication_slot: slotName -// } -// }); -// // Sample: record with incorrect prev-link 10000/10000 at 0/18AB778 -// // Seen during development. Some internal error, fixed by re-creating slot. -// // -// // Sample: publication "powersync" does not exist -// // Happens when publication deleted or never created. -// // Slot must be re-created in this case. -// logger.info(`${slotName} does not exist anymore, will create new slot`); - -// throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`); -// } -// // Try again after a pause -// await new Promise((resolve) => setTimeout(resolve, 1000)); -// } -// } -// } - -// return { needsInitialSync: true }; -// } - -// async estimatedCount(db: pgwire.PgConnection, table: storage.SourceTable): Promise { -// const results = await db.query({ -// statement: `SELECT reltuples::bigint AS estimate -// FROM pg_class -// WHERE oid = $1::regclass`, -// params: [{ value: table.qualifiedName, type: 'varchar' }] -// }); -// const row = results.rows[0]; -// if ((row?.[0] ?? -1n) == -1n) { -// return '?'; -// } else { -// return `~${row[0]}`; -// } -// } - -// /** -// * Start initial replication. -// * -// * If (partial) replication was done before on this slot, this clears the state -// * and starts again from scratch. -// */ -// async startInitialReplication(replicationConnection: pgwire.PgConnection) { -// // If anything here errors, the entire replication process is aborted, -// // and all connections closed, including this one. -// const db = await this.connections.snapshotConnection(); - -// const slotName = this.slot_name; - -// await this.storage.clear(); - -// await db.query({ -// statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', -// params: [{ type: 'varchar', value: slotName }] -// }); - -// // We use the replication connection here, not a pool. -// // This connection needs to stay open at least until the snapshot is used below. -// const result = await replicationConnection.query( -// `CREATE_REPLICATION_SLOT ${slotName} LOGICAL pgoutput EXPORT_SNAPSHOT` -// ); -// const columns = result.columns; -// const row = result.rows[0]!; -// if (columns[1]?.name != 'consistent_point' || columns[2]?.name != 'snapshot_name' || row == null) { -// throw new Error(`Invalid CREATE_REPLICATION_SLOT output: ${JSON.stringify(columns)}`); -// } -// // This LSN could be used in initialReplication below. -// // But it's also safe to just use ZERO_LSN - we won't get any changes older than this lsn -// // with streaming replication. -// const lsn = pgwire.lsnMakeComparable(row[1]); -// const snapshot = row[2]; -// logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`); - -// // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn -// await db.query('BEGIN'); -// // Use the snapshot exported above. -// // Using SERIALIZABLE isolation level may give stronger guarantees, but that complicates -// // the replication slot + snapshot above. And we still won't have SERIALIZABLE -// // guarantees with streaming replication. -// // See: ./docs/serializability.md for details. -// // -// // Another alternative here is to use the same pgwire connection for initial replication as well, -// // instead of synchronizing a separate transaction to the snapshot. - -// try { -// await db.query(`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`); -// await db.query(`SET TRANSACTION READ ONLY`); -// await db.query(`SET TRANSACTION SNAPSHOT '${snapshot}'`); - -// // Disable statement timeout for the duration of this transaction. -// // On Supabase, the default is 2 minutes. -// await db.query(`set local statement_timeout = 0`); - -// logger.info(`${slotName} Starting initial replication`); -// await this.initialReplication(db, lsn); -// logger.info(`${slotName} Initial replication done`); -// await db.query('COMMIT'); -// } catch (e) { -// await db.query('ROLLBACK'); -// throw e; -// } -// } - -// async initialReplication(db: pgwire.PgConnection, lsn: string) { -// const sourceTables = this.sync_rules.getSourceTables(); -// await this.storage.startBatch({}, async (batch) => { -// for (let tablePattern of sourceTables) { -// const tables = await this.getQualifiedTableNames(batch, db, tablePattern); -// for (let table of tables) { -// await this.snapshotTable(batch, db, table); -// await batch.markSnapshotDone([table], lsn); - -// await touch(); -// } -// } -// await batch.commit(lsn); -// }); -// } - -// static *getQueryData(results: Iterable): Generator { -// for (let row of results) { -// yield toSyncRulesRow(row); -// } -// } - -// private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) { -// logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); -// const estimatedCount = await this.estimatedCount(db, table); -// let at = 0; -// let lastLogIndex = 0; -// const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); -// let columns: { i: number; name: string }[] = []; -// // pgwire streams rows in chunks. -// // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. - -// for await (let chunk of cursor) { -// if (chunk.tag == 'RowDescription') { -// let i = 0; -// columns = chunk.payload.map((c) => { -// return { i: i++, name: c.name }; -// }); -// continue; -// } - -// const rows = chunk.rows.map((row) => { -// let q: DatabaseInputRow = {}; -// for (let c of columns) { -// q[c.name] = row[c.i]; -// } -// return q; -// }); -// if (rows.length > 0 && at - lastLogIndex >= 5000) { -// logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`); -// lastLogIndex = at; -// } -// if (this.abort_signal.aborted) { -// throw new Error(`Aborted initial replication of ${this.slot_name}`); -// } - -// for (let record of WalStream.getQueryData(rows)) { -// // This auto-flushes when the batch reaches its size limit -// await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); -// } -// at += rows.length; -// Metrics.getInstance().rows_replicated_total.add(rows.length); - -// await touch(); -// } - -// await batch.flush(); -// } - -// async handleRelation(batch: storage.BucketStorageBatch, relation: PgRelation, snapshot: boolean) { -// if (relation.relationId == null || typeof relation.relationId != 'number') { -// throw new Error('relationId expected'); -// } -// const result = await this.storage.resolveTable({ -// group_id: this.group_id, -// connection_id: this.connection_id, -// connection_tag: this.connectionTag, -// relation: relation, -// sync_rules: this.sync_rules -// }); -// this.relation_cache.set(relation.relationId, result.table); - -// // Drop conflicting tables. This includes for example renamed tables. -// await batch.drop(result.dropTables); - -// // Snapshot if: -// // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) -// // 2. Snapshot is not already done, AND: -// // 3. The table is used in sync rules. -// const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; - -// if (shouldSnapshot) { -// // Truncate this table, in case a previous snapshot was interrupted. -// await batch.truncate([result.table]); - -// let lsn: string = ZERO_LSN; -// // Start the snapshot inside a transaction. -// // We use a dedicated connection for this. -// const db = await this.connections.snapshotConnection(); -// try { -// await db.query('BEGIN'); -// try { -// // Get the current LSN. -// // The data will only be consistent once incremental replication -// // has passed that point. -// const rs = await db.query(`select pg_current_wal_lsn() as lsn`); -// lsn = rs.rows[0][0]; -// await this.snapshotTable(batch, db, result.table); -// await db.query('COMMIT'); -// } catch (e) { -// await db.query('ROLLBACK'); -// throw e; -// } -// } finally { -// await db.end(); -// } -// const [table] = await batch.markSnapshotDone([result.table], lsn); -// return table; -// } - -// return result.table; -// } - -// private getTable(relationId: number): storage.SourceTable { -// const table = this.relation_cache.get(relationId); -// if (table == null) { -// // We should always receive a replication message before the relation is used. -// // If we can't find it, it's a bug. -// throw new Error(`Missing relation cache for ${relationId}`); -// } -// return table; -// } - -// async writeChange( -// batch: storage.BucketStorageBatch, -// msg: pgwire.PgoutputMessage -// ): Promise { -// if (msg.lsn == null) { -// return null; -// } -// if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { -// const table = this.getTable(getRelId(msg.relation)); -// if (!table.syncAny) { -// logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); -// return null; -// } - -// if (msg.tag == 'insert') { -// Metrics.getInstance().rows_replicated_total.add(1); -// const baseRecord = util.constructAfterRecord(msg); -// return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); -// } else if (msg.tag == 'update') { -// Metrics.getInstance().rows_replicated_total.add(1); -// // "before" may be null if the replica id columns are unchanged -// // It's fine to treat that the same as an insert. -// const before = util.constructBeforeRecord(msg); -// const after = util.constructAfterRecord(msg); -// return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); -// } else if (msg.tag == 'delete') { -// Metrics.getInstance().rows_replicated_total.add(1); -// const before = util.constructBeforeRecord(msg)!; - -// return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); -// } -// } else if (msg.tag == 'truncate') { -// let tables: storage.SourceTable[] = []; -// for (let relation of msg.relations) { -// const table = this.getTable(getRelId(relation)); -// tables.push(table); -// } -// return await batch.truncate(tables); -// } -// return null; -// } - -// async replicate() { -// try { -// // If anything errors here, the entire replication process is halted, and -// // all connections automatically closed, including this one. -// const replicationConnection = await this.connections.replicationConnection(); -// await this.initReplication(replicationConnection); -// await this.streamChanges(replicationConnection); -// } catch (e) { -// await this.storage.reportError(e); -// throw e; -// } -// } - -// async initReplication(replicationConnection: pgwire.PgConnection) { -// const result = await this.initSlot(); -// if (result.needsInitialSync) { -// await this.startInitialReplication(replicationConnection); -// } -// } - -// async streamChanges(replicationConnection: pgwire.PgConnection) { -// // When changing any logic here, check /docs/wal-lsns.md. - -// const replicationStream = replicationConnection.logicalReplication({ -// slot: this.slot_name, -// options: { -// proto_version: '1', -// publication_names: this.publication_name -// } -// }); -// this.startedStreaming = true; - -// // Auto-activate as soon as initial replication is done -// await this.storage.autoActivate(); - -// await this.storage.startBatch({}, async (batch) => { -// // Replication never starts in the middle of a transaction -// let inTx = false; -// let count = 0; - -// for await (const chunk of replicationStream.pgoutputDecode()) { -// await touch(); - -// if (this.abort_signal.aborted) { -// break; -// } - -// // chunkLastLsn may come from normal messages in the chunk, -// // or from a PrimaryKeepalive message. -// const { messages, lastLsn: chunkLastLsn } = chunk; - -// for (const msg of messages) { -// if (msg.tag == 'relation') { -// await this.handleRelation(batch, getPgOutputRelation(msg), true); -// } else if (msg.tag == 'begin') { -// inTx = true; -// } else if (msg.tag == 'commit') { -// Metrics.getInstance().transactions_replicated_total.add(1); -// inTx = false; -// await batch.commit(msg.lsn!); -// await this.ack(msg.lsn!, replicationStream); -// } else { -// if (count % 100 == 0) { -// logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); -// } - -// count += 1; -// const result = await this.writeChange(batch, msg); -// } -// } - -// if (!inTx) { -// // In a transaction, we ack and commit according to the transaction progress. -// // Outside transactions, we use the PrimaryKeepalive messages to advance progress. -// // Big caveat: This _must not_ be used to skip individual messages, since this LSN -// // may be in the middle of the next transaction. -// // It must only be used to associate checkpoints with LSNs. -// if (await batch.keepalive(chunkLastLsn)) { -// await this.ack(chunkLastLsn, replicationStream); -// } -// } - -// Metrics.getInstance().chunks_replicated_total.add(1); -// } -// }); -// } - -// async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { -// if (lsn == ZERO_LSN) { -// return; -// } - -// replicationStream.ack(lsn); -// } -// } - -// async function touch() { -// // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. -// // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, -// // or reduce PING_INTERVAL here. -// return container.probes.touch(); -// } From 0c01dcf02cf2173b771d4e4b9a0bae5e14a9210d Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 16:29:03 +0200 Subject: [PATCH 055/298] move walstream --- .../module-postgres}/src/replication/WalStream.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {packages/service-core => modules/module-postgres}/src/replication/WalStream.ts (100%) diff --git a/packages/service-core/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts similarity index 100% rename from packages/service-core/src/replication/WalStream.ts rename to modules/module-postgres/src/replication/WalStream.ts From 13c8435ba411e1a894df941d4cb7ec9ef00a308d Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 16:33:45 +0200 Subject: [PATCH 056/298] update interface usages --- .../src/replication/WalStream.ts | 1248 ++++++++--------- .../replication/core/ReplicationAdapter.ts | 3 +- .../storage/mongo/MongoSyncBucketStorage.ts | 2 +- packages/service-core/src/util/utils.ts | 8 +- 4 files changed, 631 insertions(+), 630 deletions(-) diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 2d6e82469..415b7f842 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -1,624 +1,624 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import { container, errors, logger } from '@powersync/lib-services-framework'; -import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; - -import * as storage from '../storage/storage-index.js'; -import * as util from '../util/util-index.js'; - -import { getPgOutputRelation, getRelId } from './PgRelation.js'; -import { getReplicationIdentityColumns } from './util.js'; -import { WalConnection } from './WalConnection.js'; -import { Metrics } from '../metrics/Metrics.js'; -import { SourceEntityDescriptor } from '../storage/SourceEntity.js'; - -export const ZERO_LSN = '00000000/00000000'; - -export interface WalStreamOptions { - connections: util.PgManager; - factory: storage.BucketStorageFactory; - storage: storage.SyncRulesBucketStorage; - abort_signal: AbortSignal; -} - -interface InitResult { - needsInitialSync: boolean; -} - -export class MissingReplicationSlotError extends Error { - constructor(message: string) { - super(message); - } -} - -export class WalStream { - sync_rules: SqlSyncRules; - group_id: number; - - wal_connection: WalConnection; - connection_id = 1; - - private readonly storage: storage.SyncRulesBucketStorage; - - private slot_name: string; - - private connections: util.PgManager; - - private abort_signal: AbortSignal; - - private relation_cache = new Map(); - - private startedStreaming = false; - - constructor(options: WalStreamOptions) { - this.storage = options.storage; - this.sync_rules = options.storage.sync_rules; - this.group_id = options.storage.group_id; - this.slot_name = options.storage.slot_name; - this.connections = options.connections; - - this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); - this.abort_signal = options.abort_signal; - this.abort_signal.addEventListener( - 'abort', - () => { - if (this.startedStreaming) { - // Ping to speed up cancellation of streaming replication - // We're not using pg_snapshot here, since it could be in the middle of - // an initial replication transaction. - const promise = util.retriedQuery( - this.connections.pool, - `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` - ); - promise.catch((e) => { - // Failures here are okay - this only speeds up stopping the process. - logger.warn('Failed to ping connection', e); - }); - } else { - // If we haven't started streaming yet, it could be due to something like - // and invalid password. In that case, don't attempt to ping. - } - }, - { once: true } - ); - } - - get publication_name() { - return this.wal_connection.publication_name; - } - - get connectionTag() { - return this.wal_connection.connectionTag; - } - - get stopped() { - return this.abort_signal.aborted; - } - - async getQualifiedTableNames( - batch: storage.BucketStorageBatch, - db: pgwire.PgConnection, - tablePattern: TablePattern - ): Promise { - const schema = tablePattern.schema; - if (tablePattern.connectionTag != this.connectionTag) { - return []; - } - - let tableRows: any[]; - const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; - if (tablePattern.isWildcard) { - const result = await db.query({ - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname LIKE $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - tableRows = pgwire.pgwireRows(result); - } else { - const result = await db.query({ - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname = $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - - tableRows = pgwire.pgwireRows(result); - } - let result: storage.SourceTable[] = []; - - for (let row of tableRows) { - const name = row.table_name as string; - if (typeof row.relid != 'bigint') { - throw new Error(`missing relid for ${name}`); - } - const relid = Number(row.relid as bigint); - - if (prefix && !name.startsWith(prefix)) { - continue; - } - - const rs = await db.query({ - statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, - params: [ - { type: 'varchar', value: this.publication_name }, - { type: 'varchar', value: tablePattern.schema }, - { type: 'varchar', value: name } - ] - }); - if (rs.rows.length == 0) { - logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); - continue; - } - - const cresult = await getReplicationIdentityColumns(db, relid); - - const table = await this.handleRelation( - batch, - { - name, - schema, - objectId: relid, - replicationColumns: cresult.replicationColumns - } as SourceEntityDescriptor, - false - ); - - result.push(table); - } - return result; - } - - async initSlot(): Promise { - await this.wal_connection.checkSourceConfiguration(); - - const slotName = this.slot_name; - - const status = await this.storage.getStatus(); - if (status.snapshot_done && status.checkpoint_lsn) { - logger.info(`${slotName} Initial replication already done`); - - let last_error = null; - - // Check that replication slot exists - for (let i = 120; i >= 0; i--) { - await touch(); - - if (i == 0) { - container.reporter.captureException(last_error, { - level: errors.ErrorSeverity.ERROR, - metadata: { - replication_slot: slotName - } - }); - - throw last_error; - } - try { - // We peek a large number of changes here, to make it more likely to pick up replication slot errors. - // For example, "publication does not exist" only occurs here if the peek actually includes changes related - // to the slot. - await this.connections.pool.query({ - statement: `SELECT * - FROM pg_catalog.pg_logical_slot_peek_binary_changes($1, NULL, 1000, 'proto_version', '1', - 'publication_names', $2)`, - params: [ - { type: 'varchar', value: slotName }, - { type: 'varchar', value: this.publication_name } - ] - }); - // Success - logger.info(`Slot ${slotName} appears healthy`); - return { needsInitialSync: false }; - } catch (e) { - last_error = e; - logger.warn(`${slotName} Replication slot error`, e); - - if (this.stopped) { - throw e; - } - - // Could also be `publication "powersync" does not exist`, although this error may show up much later - // in some cases. - - if ( - /incorrect prev-link/.test(e.message) || - /replication slot.*does not exist/.test(e.message) || - /publication.*does not exist/.test(e.message) - ) { - container.reporter.captureException(e, { - level: errors.ErrorSeverity.WARNING, - metadata: { - try_index: i, - replication_slot: slotName - } - }); - // Sample: record with incorrect prev-link 10000/10000 at 0/18AB778 - // Seen during development. Some internal error, fixed by re-creating slot. - // - // Sample: publication "powersync" does not exist - // Happens when publication deleted or never created. - // Slot must be re-created in this case. - logger.info(`${slotName} does not exist anymore, will create new slot`); - - throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`); - } - // Try again after a pause - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - } - } - - return { needsInitialSync: true }; - } - - async estimatedCount(db: pgwire.PgConnection, table: storage.SourceTable): Promise { - const results = await db.query({ - statement: `SELECT reltuples::bigint AS estimate -FROM pg_class -WHERE oid = $1::regclass`, - params: [{ value: table.qualifiedName, type: 'varchar' }] - }); - const row = results.rows[0]; - if ((row?.[0] ?? -1n) == -1n) { - return '?'; - } else { - return `~${row[0]}`; - } - } - - /** - * Start initial replication. - * - * If (partial) replication was done before on this slot, this clears the state - * and starts again from scratch. - */ - async startInitialReplication(replicationConnection: pgwire.PgConnection) { - // If anything here errors, the entire replication process is aborted, - // and all connections closed, including this one. - const db = await this.connections.snapshotConnection(); - - const slotName = this.slot_name; - - await this.storage.clear(); - - await db.query({ - statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', - params: [{ type: 'varchar', value: slotName }] - }); - - // We use the replication connection here, not a pool. - // This connection needs to stay open at least until the snapshot is used below. - const result = await replicationConnection.query( - `CREATE_REPLICATION_SLOT ${slotName} LOGICAL pgoutput EXPORT_SNAPSHOT` - ); - const columns = result.columns; - const row = result.rows[0]!; - if (columns[1]?.name != 'consistent_point' || columns[2]?.name != 'snapshot_name' || row == null) { - throw new Error(`Invalid CREATE_REPLICATION_SLOT output: ${JSON.stringify(columns)}`); - } - // This LSN could be used in initialReplication below. - // But it's also safe to just use ZERO_LSN - we won't get any changes older than this lsn - // with streaming replication. - const lsn = pgwire.lsnMakeComparable(row[1]); - const snapshot = row[2]; - logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`); - - // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn - await db.query('BEGIN'); - // Use the snapshot exported above. - // Using SERIALIZABLE isolation level may give stronger guarantees, but that complicates - // the replication slot + snapshot above. And we still won't have SERIALIZABLE - // guarantees with streaming replication. - // See: ./docs/serializability.md for details. - // - // Another alternative here is to use the same pgwire connection for initial replication as well, - // instead of synchronizing a separate transaction to the snapshot. - - try { - await db.query(`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`); - await db.query(`SET TRANSACTION READ ONLY`); - await db.query(`SET TRANSACTION SNAPSHOT '${snapshot}'`); - - // Disable statement timeout for the duration of this transaction. - // On Supabase, the default is 2 minutes. - await db.query(`set local statement_timeout = 0`); - - logger.info(`${slotName} Starting initial replication`); - await this.initialReplication(db, lsn); - logger.info(`${slotName} Initial replication done`); - await db.query('COMMIT'); - } catch (e) { - await db.query('ROLLBACK'); - throw e; - } - } - - async initialReplication(db: pgwire.PgConnection, lsn: string) { - const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({}, async (batch) => { - for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, db, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, db, table); - await batch.markSnapshotDone([table], lsn); - - await touch(); - } - } - await batch.commit(lsn); - }); - } - - static *getQueryData(results: Iterable): Generator { - for (let row of results) { - yield toSyncRulesRow(row); - } - } - - private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) { - logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); - const estimatedCount = await this.estimatedCount(db, table); - let at = 0; - let lastLogIndex = 0; - const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); - let columns: { i: number; name: string }[] = []; - // pgwire streams rows in chunks. - // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. - - for await (let chunk of cursor) { - if (chunk.tag == 'RowDescription') { - let i = 0; - columns = chunk.payload.map((c) => { - return { i: i++, name: c.name }; - }); - continue; - } - - const rows = chunk.rows.map((row) => { - let q: DatabaseInputRow = {}; - for (let c of columns) { - q[c.name] = row[c.i]; - } - return q; - }); - if (rows.length > 0 && at - lastLogIndex >= 5000) { - logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`); - lastLogIndex = at; - } - if (this.abort_signal.aborted) { - throw new Error(`Aborted initial replication of ${this.slot_name}`); - } - - for (let record of WalStream.getQueryData(rows)) { - // This auto-flushes when the batch reaches its size limit - await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); - } - at += rows.length; - Metrics.getInstance().rows_replicated_total.add(rows.length); - - await touch(); - } - - await batch.flush(); - } - - async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { - if (!descriptor.objectId) { - throw new Error('objectId expected'); - } - const result = await this.storage.resolveTable({ - group_id: this.group_id, - connection_id: this.connection_id, - connection_tag: this.connectionTag, - entity_descriptor: descriptor, - sync_rules: this.sync_rules - }); - this.relation_cache.set(descriptor.objectId, result.table); - - // Drop conflicting tables. This includes for example renamed tables. - await batch.drop(result.dropTables); - - // Snapshot if: - // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) - // 2. Snapshot is not already done, AND: - // 3. The table is used in sync rules. - const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; - - if (shouldSnapshot) { - // Truncate this table, in case a previous snapshot was interrupted. - await batch.truncate([result.table]); - - let lsn: string = ZERO_LSN; - // Start the snapshot inside a transaction. - // We use a dedicated connection for this. - const db = await this.connections.snapshotConnection(); - try { - await db.query('BEGIN'); - try { - // Get the current LSN. - // The data will only be consistent once incremental replication - // has passed that point. - const rs = await db.query(`select pg_current_wal_lsn() as lsn`); - lsn = rs.rows[0][0]; - await this.snapshotTable(batch, db, result.table); - await db.query('COMMIT'); - } catch (e) { - await db.query('ROLLBACK'); - throw e; - } - } finally { - await db.end(); - } - const [table] = await batch.markSnapshotDone([result.table], lsn); - return table; - } - - return result.table; - } - - private getTable(relationId: number): storage.SourceTable { - const table = this.relation_cache.get(relationId); - if (table == null) { - // We should always receive a replication message before the relation is used. - // If we can't find it, it's a bug. - throw new Error(`Missing relation cache for ${relationId}`); - } - return table; - } - - async writeChange( - batch: storage.BucketStorageBatch, - msg: pgwire.PgoutputMessage - ): Promise { - if (msg.lsn == null) { - return null; - } - if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { - const table = this.getTable(getRelId(msg.relation)); - if (!table.syncAny) { - logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); - return null; - } - - if (msg.tag == 'insert') { - Metrics.getInstance().rows_replicated_total.add(1); - const baseRecord = util.constructAfterRecord(msg); - return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); - } else if (msg.tag == 'update') { - Metrics.getInstance().rows_replicated_total.add(1); - // "before" may be null if the replica id columns are unchanged - // It's fine to treat that the same as an insert. - const before = util.constructBeforeRecord(msg); - const after = util.constructAfterRecord(msg); - return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); - } else if (msg.tag == 'delete') { - Metrics.getInstance().rows_replicated_total.add(1); - const before = util.constructBeforeRecord(msg)!; - - return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); - } - } else if (msg.tag == 'truncate') { - let tables: storage.SourceTable[] = []; - for (let relation of msg.relations) { - const table = this.getTable(getRelId(relation)); - tables.push(table); - } - return await batch.truncate(tables); - } - return null; - } - - async replicate() { - try { - // If anything errors here, the entire replication process is halted, and - // all connections automatically closed, including this one. - const replicationConnection = await this.connections.replicationConnection(); - await this.initReplication(replicationConnection); - await this.streamChanges(replicationConnection); - } catch (e) { - await this.storage.reportError(e); - throw e; - } - } - - async initReplication(replicationConnection: pgwire.PgConnection) { - const result = await this.initSlot(); - if (result.needsInitialSync) { - await this.startInitialReplication(replicationConnection); - } - } - - async streamChanges(replicationConnection: pgwire.PgConnection) { - // When changing any logic here, check /docs/wal-lsns.md. - - const replicationStream = replicationConnection.logicalReplication({ - slot: this.slot_name, - options: { - proto_version: '1', - publication_names: this.publication_name - } - }); - this.startedStreaming = true; - - // Auto-activate as soon as initial replication is done - await this.storage.autoActivate(); - - await this.storage.startBatch({}, async (batch) => { - // Replication never starts in the middle of a transaction - let inTx = false; - let count = 0; - - for await (const chunk of replicationStream.pgoutputDecode()) { - await touch(); - - if (this.abort_signal.aborted) { - break; - } - - // chunkLastLsn may come from normal messages in the chunk, - // or from a PrimaryKeepalive message. - const { messages, lastLsn: chunkLastLsn } = chunk; - - for (const msg of messages) { - if (msg.tag == 'relation') { - await this.handleRelation(batch, getPgOutputRelation(msg), true); - } else if (msg.tag == 'begin') { - inTx = true; - } else if (msg.tag == 'commit') { - Metrics.getInstance().transactions_replicated_total.add(1); - inTx = false; - await batch.commit(msg.lsn!); - await this.ack(msg.lsn!, replicationStream); - } else { - if (count % 100 == 0) { - logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); - } - - count += 1; - const result = await this.writeChange(batch, msg); - } - } - - if (!inTx) { - // In a transaction, we ack and commit according to the transaction progress. - // Outside transactions, we use the PrimaryKeepalive messages to advance progress. - // Big caveat: This _must not_ be used to skip individual messages, since this LSN - // may be in the middle of the next transaction. - // It must only be used to associate checkpoints with LSNs. - if (await batch.keepalive(chunkLastLsn)) { - await this.ack(chunkLastLsn, replicationStream); - } - } - - Metrics.getInstance().chunks_replicated_total.add(1); - } - }); - } - - async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { - if (lsn == ZERO_LSN) { - return; - } - - replicationStream.ack(lsn); - } -} - -async function touch() { - // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. - // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, - // or reduce PING_INTERVAL here. - return container.probes.touch(); -} +// import * as pgwire from '@powersync/service-jpgwire'; +// import { container, errors, logger } from '@powersync/lib-services-framework'; +// import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; + +// import * as storage from '../storage/storage-index.js'; +// import * as util from '../util/util-index.js'; + +// import { getPgOutputRelation, getRelId } from './PgRelation.js'; +// import { getReplicationIdentityColumns } from './util.js'; +// import { WalConnection } from './WalConnection.js'; +// import { Metrics } from '../metrics/Metrics.js'; +// import { SourceEntityDescriptor } from '../storage/SourceEntity.js'; + +// export const ZERO_LSN = '00000000/00000000'; + +// export interface WalStreamOptions { +// connections: util.PgManager; +// factory: storage.BucketStorageFactory; +// storage: storage.SyncRulesBucketStorage; +// abort_signal: AbortSignal; +// } + +// interface InitResult { +// needsInitialSync: boolean; +// } + +// export class MissingReplicationSlotError extends Error { +// constructor(message: string) { +// super(message); +// } +// } + +// export class WalStream { +// sync_rules: SqlSyncRules; +// group_id: number; + +// wal_connection: WalConnection; +// connection_id = 1; + +// private readonly storage: storage.SyncRulesBucketStorage; + +// private slot_name: string; + +// private connections: util.PgManager; + +// private abort_signal: AbortSignal; + +// private relation_cache = new Map(); + +// private startedStreaming = false; + +// constructor(options: WalStreamOptions) { +// this.storage = options.storage; +// this.sync_rules = options.storage.sync_rules; +// this.group_id = options.storage.group_id; +// this.slot_name = options.storage.slot_name; +// this.connections = options.connections; + +// this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); +// this.abort_signal = options.abort_signal; +// this.abort_signal.addEventListener( +// 'abort', +// () => { +// if (this.startedStreaming) { +// // Ping to speed up cancellation of streaming replication +// // We're not using pg_snapshot here, since it could be in the middle of +// // an initial replication transaction. +// const promise = util.retriedQuery( +// this.connections.pool, +// `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` +// ); +// promise.catch((e) => { +// // Failures here are okay - this only speeds up stopping the process. +// logger.warn('Failed to ping connection', e); +// }); +// } else { +// // If we haven't started streaming yet, it could be due to something like +// // and invalid password. In that case, don't attempt to ping. +// } +// }, +// { once: true } +// ); +// } + +// get publication_name() { +// return this.wal_connection.publication_name; +// } + +// get connectionTag() { +// return this.wal_connection.connectionTag; +// } + +// get stopped() { +// return this.abort_signal.aborted; +// } + +// async getQualifiedTableNames( +// batch: storage.BucketStorageBatch, +// db: pgwire.PgConnection, +// tablePattern: TablePattern +// ): Promise { +// const schema = tablePattern.schema; +// if (tablePattern.connectionTag != this.connectionTag) { +// return []; +// } + +// let tableRows: any[]; +// const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; +// if (tablePattern.isWildcard) { +// const result = await db.query({ +// statement: `SELECT c.oid AS relid, c.relname AS table_name +// FROM pg_class c +// JOIN pg_namespace n ON n.oid = c.relnamespace +// WHERE n.nspname = $1 +// AND c.relkind = 'r' +// AND c.relname LIKE $2`, +// params: [ +// { type: 'varchar', value: schema }, +// { type: 'varchar', value: tablePattern.tablePattern } +// ] +// }); +// tableRows = pgwire.pgwireRows(result); +// } else { +// const result = await db.query({ +// statement: `SELECT c.oid AS relid, c.relname AS table_name +// FROM pg_class c +// JOIN pg_namespace n ON n.oid = c.relnamespace +// WHERE n.nspname = $1 +// AND c.relkind = 'r' +// AND c.relname = $2`, +// params: [ +// { type: 'varchar', value: schema }, +// { type: 'varchar', value: tablePattern.tablePattern } +// ] +// }); + +// tableRows = pgwire.pgwireRows(result); +// } +// let result: storage.SourceTable[] = []; + +// for (let row of tableRows) { +// const name = row.table_name as string; +// if (typeof row.relid != 'bigint') { +// throw new Error(`missing relid for ${name}`); +// } +// const relid = Number(row.relid as bigint); + +// if (prefix && !name.startsWith(prefix)) { +// continue; +// } + +// const rs = await db.query({ +// statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, +// params: [ +// { type: 'varchar', value: this.publication_name }, +// { type: 'varchar', value: tablePattern.schema }, +// { type: 'varchar', value: name } +// ] +// }); +// if (rs.rows.length == 0) { +// logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); +// continue; +// } + +// const cresult = await getReplicationIdentityColumns(db, relid); + +// const table = await this.handleRelation( +// batch, +// { +// name, +// schema, +// objectId: relid, +// replicationColumns: cresult.replicationColumns +// } as SourceEntityDescriptor, +// false +// ); + +// result.push(table); +// } +// return result; +// } + +// async initSlot(): Promise { +// await this.wal_connection.checkSourceConfiguration(); + +// const slotName = this.slot_name; + +// const status = await this.storage.getStatus(); +// if (status.snapshot_done && status.checkpoint_lsn) { +// logger.info(`${slotName} Initial replication already done`); + +// let last_error = null; + +// // Check that replication slot exists +// for (let i = 120; i >= 0; i--) { +// await touch(); + +// if (i == 0) { +// container.reporter.captureException(last_error, { +// level: errors.ErrorSeverity.ERROR, +// metadata: { +// replication_slot: slotName +// } +// }); + +// throw last_error; +// } +// try { +// // We peek a large number of changes here, to make it more likely to pick up replication slot errors. +// // For example, "publication does not exist" only occurs here if the peek actually includes changes related +// // to the slot. +// await this.connections.pool.query({ +// statement: `SELECT * +// FROM pg_catalog.pg_logical_slot_peek_binary_changes($1, NULL, 1000, 'proto_version', '1', +// 'publication_names', $2)`, +// params: [ +// { type: 'varchar', value: slotName }, +// { type: 'varchar', value: this.publication_name } +// ] +// }); +// // Success +// logger.info(`Slot ${slotName} appears healthy`); +// return { needsInitialSync: false }; +// } catch (e) { +// last_error = e; +// logger.warn(`${slotName} Replication slot error`, e); + +// if (this.stopped) { +// throw e; +// } + +// // Could also be `publication "powersync" does not exist`, although this error may show up much later +// // in some cases. + +// if ( +// /incorrect prev-link/.test(e.message) || +// /replication slot.*does not exist/.test(e.message) || +// /publication.*does not exist/.test(e.message) +// ) { +// container.reporter.captureException(e, { +// level: errors.ErrorSeverity.WARNING, +// metadata: { +// try_index: i, +// replication_slot: slotName +// } +// }); +// // Sample: record with incorrect prev-link 10000/10000 at 0/18AB778 +// // Seen during development. Some internal error, fixed by re-creating slot. +// // +// // Sample: publication "powersync" does not exist +// // Happens when publication deleted or never created. +// // Slot must be re-created in this case. +// logger.info(`${slotName} does not exist anymore, will create new slot`); + +// throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`); +// } +// // Try again after a pause +// await new Promise((resolve) => setTimeout(resolve, 1000)); +// } +// } +// } + +// return { needsInitialSync: true }; +// } + +// async estimatedCount(db: pgwire.PgConnection, table: storage.SourceTable): Promise { +// const results = await db.query({ +// statement: `SELECT reltuples::bigint AS estimate +// FROM pg_class +// WHERE oid = $1::regclass`, +// params: [{ value: table.qualifiedName, type: 'varchar' }] +// }); +// const row = results.rows[0]; +// if ((row?.[0] ?? -1n) == -1n) { +// return '?'; +// } else { +// return `~${row[0]}`; +// } +// } + +// /** +// * Start initial replication. +// * +// * If (partial) replication was done before on this slot, this clears the state +// * and starts again from scratch. +// */ +// async startInitialReplication(replicationConnection: pgwire.PgConnection) { +// // If anything here errors, the entire replication process is aborted, +// // and all connections closed, including this one. +// const db = await this.connections.snapshotConnection(); + +// const slotName = this.slot_name; + +// await this.storage.clear(); + +// await db.query({ +// statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', +// params: [{ type: 'varchar', value: slotName }] +// }); + +// // We use the replication connection here, not a pool. +// // This connection needs to stay open at least until the snapshot is used below. +// const result = await replicationConnection.query( +// `CREATE_REPLICATION_SLOT ${slotName} LOGICAL pgoutput EXPORT_SNAPSHOT` +// ); +// const columns = result.columns; +// const row = result.rows[0]!; +// if (columns[1]?.name != 'consistent_point' || columns[2]?.name != 'snapshot_name' || row == null) { +// throw new Error(`Invalid CREATE_REPLICATION_SLOT output: ${JSON.stringify(columns)}`); +// } +// // This LSN could be used in initialReplication below. +// // But it's also safe to just use ZERO_LSN - we won't get any changes older than this lsn +// // with streaming replication. +// const lsn = pgwire.lsnMakeComparable(row[1]); +// const snapshot = row[2]; +// logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`); + +// // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn +// await db.query('BEGIN'); +// // Use the snapshot exported above. +// // Using SERIALIZABLE isolation level may give stronger guarantees, but that complicates +// // the replication slot + snapshot above. And we still won't have SERIALIZABLE +// // guarantees with streaming replication. +// // See: ./docs/serializability.md for details. +// // +// // Another alternative here is to use the same pgwire connection for initial replication as well, +// // instead of synchronizing a separate transaction to the snapshot. + +// try { +// await db.query(`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`); +// await db.query(`SET TRANSACTION READ ONLY`); +// await db.query(`SET TRANSACTION SNAPSHOT '${snapshot}'`); + +// // Disable statement timeout for the duration of this transaction. +// // On Supabase, the default is 2 minutes. +// await db.query(`set local statement_timeout = 0`); + +// logger.info(`${slotName} Starting initial replication`); +// await this.initialReplication(db, lsn); +// logger.info(`${slotName} Initial replication done`); +// await db.query('COMMIT'); +// } catch (e) { +// await db.query('ROLLBACK'); +// throw e; +// } +// } + +// async initialReplication(db: pgwire.PgConnection, lsn: string) { +// const sourceTables = this.sync_rules.getSourceTables(); +// await this.storage.startBatch({}, async (batch) => { +// for (let tablePattern of sourceTables) { +// const tables = await this.getQualifiedTableNames(batch, db, tablePattern); +// for (let table of tables) { +// await this.snapshotTable(batch, db, table); +// await batch.markSnapshotDone([table], lsn); + +// await touch(); +// } +// } +// await batch.commit(lsn); +// }); +// } + +// static *getQueryData(results: Iterable): Generator { +// for (let row of results) { +// yield toSyncRulesRow(row); +// } +// } + +// private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) { +// logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); +// const estimatedCount = await this.estimatedCount(db, table); +// let at = 0; +// let lastLogIndex = 0; +// const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); +// let columns: { i: number; name: string }[] = []; +// // pgwire streams rows in chunks. +// // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. + +// for await (let chunk of cursor) { +// if (chunk.tag == 'RowDescription') { +// let i = 0; +// columns = chunk.payload.map((c) => { +// return { i: i++, name: c.name }; +// }); +// continue; +// } + +// const rows = chunk.rows.map((row) => { +// let q: DatabaseInputRow = {}; +// for (let c of columns) { +// q[c.name] = row[c.i]; +// } +// return q; +// }); +// if (rows.length > 0 && at - lastLogIndex >= 5000) { +// logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`); +// lastLogIndex = at; +// } +// if (this.abort_signal.aborted) { +// throw new Error(`Aborted initial replication of ${this.slot_name}`); +// } + +// for (let record of WalStream.getQueryData(rows)) { +// // This auto-flushes when the batch reaches its size limit +// await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); +// } +// at += rows.length; +// Metrics.getInstance().rows_replicated_total.add(rows.length); + +// await touch(); +// } + +// await batch.flush(); +// } + +// async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { +// if (!descriptor.objectId) { +// throw new Error('objectId expected'); +// } +// const result = await this.storage.resolveTable({ +// group_id: this.group_id, +// connection_id: this.connection_id, +// connection_tag: this.connectionTag, +// entity_descriptor: descriptor, +// sync_rules: this.sync_rules +// }); +// this.relation_cache.set(descriptor.objectId, result.table); + +// // Drop conflicting tables. This includes for example renamed tables. +// await batch.drop(result.dropTables); + +// // Snapshot if: +// // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) +// // 2. Snapshot is not already done, AND: +// // 3. The table is used in sync rules. +// const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; + +// if (shouldSnapshot) { +// // Truncate this table, in case a previous snapshot was interrupted. +// await batch.truncate([result.table]); + +// let lsn: string = ZERO_LSN; +// // Start the snapshot inside a transaction. +// // We use a dedicated connection for this. +// const db = await this.connections.snapshotConnection(); +// try { +// await db.query('BEGIN'); +// try { +// // Get the current LSN. +// // The data will only be consistent once incremental replication +// // has passed that point. +// const rs = await db.query(`select pg_current_wal_lsn() as lsn`); +// lsn = rs.rows[0][0]; +// await this.snapshotTable(batch, db, result.table); +// await db.query('COMMIT'); +// } catch (e) { +// await db.query('ROLLBACK'); +// throw e; +// } +// } finally { +// await db.end(); +// } +// const [table] = await batch.markSnapshotDone([result.table], lsn); +// return table; +// } + +// return result.table; +// } + +// private getTable(relationId: number): storage.SourceTable { +// const table = this.relation_cache.get(relationId); +// if (table == null) { +// // We should always receive a replication message before the relation is used. +// // If we can't find it, it's a bug. +// throw new Error(`Missing relation cache for ${relationId}`); +// } +// return table; +// } + +// async writeChange( +// batch: storage.BucketStorageBatch, +// msg: pgwire.PgoutputMessage +// ): Promise { +// if (msg.lsn == null) { +// return null; +// } +// if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { +// const table = this.getTable(getRelId(msg.relation)); +// if (!table.syncAny) { +// logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); +// return null; +// } + +// if (msg.tag == 'insert') { +// Metrics.getInstance().rows_replicated_total.add(1); +// const baseRecord = util.constructAfterRecord(msg); +// return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); +// } else if (msg.tag == 'update') { +// Metrics.getInstance().rows_replicated_total.add(1); +// // "before" may be null if the replica id columns are unchanged +// // It's fine to treat that the same as an insert. +// const before = util.constructBeforeRecord(msg); +// const after = util.constructAfterRecord(msg); +// return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); +// } else if (msg.tag == 'delete') { +// Metrics.getInstance().rows_replicated_total.add(1); +// const before = util.constructBeforeRecord(msg)!; + +// return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); +// } +// } else if (msg.tag == 'truncate') { +// let tables: storage.SourceTable[] = []; +// for (let relation of msg.relations) { +// const table = this.getTable(getRelId(relation)); +// tables.push(table); +// } +// return await batch.truncate(tables); +// } +// return null; +// } + +// async replicate() { +// try { +// // If anything errors here, the entire replication process is halted, and +// // all connections automatically closed, including this one. +// const replicationConnection = await this.connections.replicationConnection(); +// await this.initReplication(replicationConnection); +// await this.streamChanges(replicationConnection); +// } catch (e) { +// await this.storage.reportError(e); +// throw e; +// } +// } + +// async initReplication(replicationConnection: pgwire.PgConnection) { +// const result = await this.initSlot(); +// if (result.needsInitialSync) { +// await this.startInitialReplication(replicationConnection); +// } +// } + +// async streamChanges(replicationConnection: pgwire.PgConnection) { +// // When changing any logic here, check /docs/wal-lsns.md. + +// const replicationStream = replicationConnection.logicalReplication({ +// slot: this.slot_name, +// options: { +// proto_version: '1', +// publication_names: this.publication_name +// } +// }); +// this.startedStreaming = true; + +// // Auto-activate as soon as initial replication is done +// await this.storage.autoActivate(); + +// await this.storage.startBatch({}, async (batch) => { +// // Replication never starts in the middle of a transaction +// let inTx = false; +// let count = 0; + +// for await (const chunk of replicationStream.pgoutputDecode()) { +// await touch(); + +// if (this.abort_signal.aborted) { +// break; +// } + +// // chunkLastLsn may come from normal messages in the chunk, +// // or from a PrimaryKeepalive message. +// const { messages, lastLsn: chunkLastLsn } = chunk; + +// for (const msg of messages) { +// if (msg.tag == 'relation') { +// await this.handleRelation(batch, getPgOutputRelation(msg), true); +// } else if (msg.tag == 'begin') { +// inTx = true; +// } else if (msg.tag == 'commit') { +// Metrics.getInstance().transactions_replicated_total.add(1); +// inTx = false; +// await batch.commit(msg.lsn!); +// await this.ack(msg.lsn!, replicationStream); +// } else { +// if (count % 100 == 0) { +// logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); +// } + +// count += 1; +// const result = await this.writeChange(batch, msg); +// } +// } + +// if (!inTx) { +// // In a transaction, we ack and commit according to the transaction progress. +// // Outside transactions, we use the PrimaryKeepalive messages to advance progress. +// // Big caveat: This _must not_ be used to skip individual messages, since this LSN +// // may be in the middle of the next transaction. +// // It must only be used to associate checkpoints with LSNs. +// if (await batch.keepalive(chunkLastLsn)) { +// await this.ack(chunkLastLsn, replicationStream); +// } +// } + +// Metrics.getInstance().chunks_replicated_total.add(1); +// } +// }); +// } + +// async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { +// if (lsn == ZERO_LSN) { +// return; +// } + +// replicationStream.ack(lsn); +// } +// } + +// async function touch() { +// // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. +// // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, +// // or reduce PING_INTERVAL here. +// return container.probes.touch(); +// } diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 42b1527e6..3594062c8 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -1,6 +1,5 @@ import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; import * as storage from '../../storage/storage-index.js'; -import { PgRelation } from '../PgRelation.js'; /** * The ReplicationAdapter describes all the methods that are required by the @@ -101,7 +100,7 @@ type ReplicationUpdate = { * Present when the update is a schema change. Describes the new data source entity * TODO: Redefine PgRelation */ - entityDescriptor?: PgRelation; + entityDescriptor?: storage.SourceEntityDescriptor; /** * Present when the update is a commit. Contains the LSN of the commit */ diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 7ef2f4e63..e34c79300 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -3,7 +3,7 @@ import * as bson from 'bson'; import * as mongo from 'mongodb'; import * as db from '../../db/db-index.js'; -import * as replication from '../../replication/WalStream.js'; +import * as replication from '../../replication/replication-index.js'; import * as util from '../../util/util-index.js'; import { BucketDataBatchOptions, diff --git a/packages/service-core/src/util/utils.ts b/packages/service-core/src/util/utils.ts index fcd98efd3..a555a6d33 100644 --- a/packages/service-core/src/util/utils.ts +++ b/packages/service-core/src/util/utils.ts @@ -4,6 +4,8 @@ import crypto from 'crypto'; import * as uuid from 'uuid'; import { BucketChecksum, OpId } from './protocol-types.js'; +import * as storage from '../storage/storage-index.js'; + export type ChecksumMap = Map; export const ID_NAMESPACE = 'a396dd91-09fc-4017-a28d-3df722f651e9'; @@ -81,7 +83,7 @@ export function addBucketChecksums(a: BucketChecksum, b: BucketChecksum | null): function getRawReplicaIdentity( tuple: sync_rules.ToastableSqliteRow, - columns: ReplicationColumn[] + columns: storage.ColumnDescriptor[] ): Record { let result: Record = {}; for (let column of columns) { @@ -93,7 +95,7 @@ function getRawReplicaIdentity( export function getUuidReplicaIdentityString( tuple: sync_rules.ToastableSqliteRow, - columns: ReplicationColumn[] + columns: storage.ColumnDescriptor[] ): string { const rawIdentity = getRawReplicaIdentity(tuple, columns); @@ -109,7 +111,7 @@ export function uuidForRow(row: sync_rules.SqliteRow): string { export function getUuidReplicaIdentityBson( tuple: sync_rules.ToastableSqliteRow, - columns: ReplicationColumn[] + columns: storage.ColumnDescriptor[] ): bson.UUID { if (columns.length == 0) { // REPLICA IDENTITY NOTHING - generate random id From 0e23297f59ca06222d7e9e752673c6ccb6a59530 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 16:56:18 +0200 Subject: [PATCH 057/298] use connections instead of datasources for compatibility --- .../service-core/src/replication/core/ReplicationModule.ts | 4 ++-- .../service-core/src/util/config/compound-config-collector.ts | 4 ++-- packages/service-core/src/util/config/types.ts | 2 +- packages/types/src/config/PowerSyncConfig.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 5419b6f4f..4b4d11219 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -50,12 +50,12 @@ export abstract class ReplicationModule extends modules.AbstractModule { * Register this module's replication adapters and sync API providers if the required configuration is present. */ public async initialize(context: system.ServiceContext): Promise { - if (!context.configuration.data_sources) { + if (!context.configuration.connections) { // No data source configuration found in the config skip for now return; } - const matchingConfig = context.configuration.data_sources.filter((dataSource) => dataSource.type === this.type); + const matchingConfig = context.configuration.connections.filter((dataSource) => dataSource.type === this.type); if (matchingConfig.length > 1) { logger.warning( diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 448ab7873..e01918a0d 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -46,7 +46,7 @@ export class CompoundConfigCollector { async collectConfig(runnerConfig: RunnerConfig = {}): Promise { const baseConfig = await this.collectBaseConfig(runnerConfig); - const dataSources = baseConfig.replication?.data_sources ?? []; + const dataSources = baseConfig.replication?.connections ?? []; if (dataSources.length > 1) { throw new Error('Only a single replication data source is supported currently'); } @@ -84,7 +84,7 @@ export class CompoundConfigCollector { let jwt_audiences: string[] = baseConfig.client_auth?.audience ?? []; let config: ResolvedPowerSyncConfig = { - data_sources: baseConfig.replication?.data_sources || [], + connections: baseConfig.replication?.connections || [], storage: baseConfig.storage, client_keystore: keyStore, // Dev tokens only use the static keys, no external key sources diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index 9fb035952..84963d533 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -29,7 +29,7 @@ export type SyncRulesConfig = { }; export type ResolvedPowerSyncConfig = { - data_sources?: configFile.DataSourceConfig[]; + connections?: configFile.DataSourceConfig[]; storage: configFile.StorageConfig; dev: { demo_auth: boolean; diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index 84a9bef11..f1c1292c5 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -76,7 +76,7 @@ export type StorageConfig = t.Decoded; export const powerSyncConfig = t.object({ replication: t .object({ - data_sources: t.array(dataSourceConfig).optional() + connections: t.array(dataSourceConfig).optional() }) .optional(), From 5b49678be48b06086801a3f3a1986485b1920ef5 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 17:08:48 +0200 Subject: [PATCH 058/298] remove pgwire dependency from service core --- .../src/api/PostgresSyncAPIAdapter.ts | 5 +- .../src/module/PostgresModule.ts | 28 ++++++--- .../src/utils}/migration_lib.ts | 0 .../src/utils}/populate_test_data.ts | 0 packages/service-core/package.json | 2 - packages/service-core/src/metrics/Metrics.ts | 9 --- packages/service-core/src/util/util-index.ts | 6 +- pnpm-lock.yaml | 62 ++++++------------- service/package.json | 2 +- service/src/runners/stream-worker.ts | 27 +++++--- 10 files changed, 61 insertions(+), 80 deletions(-) rename {packages/service-core/src/util => modules/module-postgres/src/utils}/migration_lib.ts (100%) rename {packages/service-core/src/util => modules/module-postgres/src/utils}/populate_test_data.ts (100%) diff --git a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts index 8c9c6631b..5f41e8237 100644 --- a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts @@ -10,14 +10,15 @@ import * as pg_utils from '../utils/pgwire_utils.js'; export class PostgresSyncAPIAdapter implements api.RouteAPI { protected pool: pgwire.PgClient; + connectionTag: string; // TODO this should probably be configurable one day - connectionTag = sync_rules.DEFAULT_TAG; publication_name = 'powersync'; constructor(protected config: types.ResolvedConnectionConfig) { this.pool = pgwire.connectPgWirePool(config, { idleTimeout: 30_000 }); + this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG; } async shutdown(): Promise { @@ -196,7 +197,7 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { } } - const id_columns = id_columns_result?.columns ?? []; + const id_columns = id_columns_result?.replicationColumns ?? []; const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index c42137c71..9032b6e83 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -8,7 +8,6 @@ import * as types from '../types/types.js'; import { PostgresSyncAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; -import { normalizeConnectionConfig, PostgresConnectionConfig, ResolvedConnectionConfig } from '../types/types.js'; import { terminateReplicators } from '../utils/teardown.js'; export class PostgresModule extends replication.ReplicationModule { @@ -21,20 +20,29 @@ export class PostgresModule extends replication.ReplicationModule { protected configSchema(): t.AnyCodec { // Intersection types have some limitations in codec typing - return PostgresConnectionConfig; + return types.PostgresConnectionConfig; } async register(context: system.ServiceContext): Promise {} async initialize(context: system.ServiceContext): Promise { + // TODO metrics + // const class_scoped_data_replicated_bytes = this.data_replicated_bytes; + // // Record replicated bytes using global jpgwire metrics. + // jpgwire.setMetricsRecorder({ + // addBytesRead(bytes) { + // class_scoped_data_replicated_bytes.add(bytes); + // } + // }); + // Register the Supabase key collector(s) - (context.configuration.data_sources ?? []) + (context.configuration.connections ?? []) .map((baseConfig) => { if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { return; } try { - return this.resolveConfig(PostgresConnectionConfig.decode(baseConfig as any)); + return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); } catch (ex) { logger.warn('Failed to decode configuration in Postgres module initialization.'); } @@ -50,21 +58,21 @@ export class PostgresModule extends replication.ReplicationModule { }); } - protected createSyncAPIAdapter(config: PostgresConnectionConfig): api.RouteAPI { + protected createSyncAPIAdapter(config: types.PostgresConnectionConfig): api.RouteAPI { throw new PostgresSyncAPIAdapter(this.resolveConfig(config)); } - protected createReplicationAdapter(config: PostgresConnectionConfig): PostgresReplicationAdapter { + protected createReplicationAdapter(config: types.PostgresConnectionConfig): PostgresReplicationAdapter { return new PostgresReplicationAdapter(this.resolveConfig(config)); } /** * Combines base config with normalized connection settings */ - private resolveConfig(config: PostgresConnectionConfig): ResolvedConnectionConfig { + private resolveConfig(config: types.PostgresConnectionConfig): types.ResolvedConnectionConfig { return { ...config, - ...normalizeConnectionConfig(config) + ...types.normalizeConnectionConfig(config) }; } @@ -80,9 +88,9 @@ export class PostgresModule extends replication.ReplicationModule { await db.mongo.waitForAuth(mongoDB.db); logger.info(`Terminating replication slots`); - const connections = (context.configuration.data_sources ?? []) + const connections = (context.configuration.connections ?? []) .filter((c) => c.type == 'postgresql') - .map((c) => PostgresConnectionConfig.decode(c as any)); + .map((c) => types.PostgresConnectionConfig.decode(c as any)); for (const connection of connections) { await terminateReplicators(context.storage, this.resolveConfig(connection)); diff --git a/packages/service-core/src/util/migration_lib.ts b/modules/module-postgres/src/utils/migration_lib.ts similarity index 100% rename from packages/service-core/src/util/migration_lib.ts rename to modules/module-postgres/src/utils/migration_lib.ts diff --git a/packages/service-core/src/util/populate_test_data.ts b/modules/module-postgres/src/utils/populate_test_data.ts similarity index 100% rename from packages/service-core/src/util/populate_test_data.ts rename to modules/module-postgres/src/utils/populate_test_data.ts diff --git a/packages/service-core/package.json b/packages/service-core/package.json index 4bdf425dc..3357e6298 100644 --- a/packages/service-core/package.json +++ b/packages/service-core/package.json @@ -23,7 +23,6 @@ "@opentelemetry/resources": "^1.24.1", "@opentelemetry/sdk-metrics": "1.24.1", "@powersync/lib-services-framework": "workspace:*", - "@powersync/service-jpgwire": "workspace:*", "@powersync/service-jsonbig": "workspace:*", "@powersync/service-rsocket-router": "workspace:*", "@powersync/service-sync-rules": "workspace:*", @@ -40,7 +39,6 @@ "lru-cache": "^10.2.2", "mongodb": "^6.7.0", "node-fetch": "^3.3.2", - "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87", "ts-codec": "^1.2.2", "uuid": "^9.0.1", "winston": "^3.13.0", diff --git a/packages/service-core/src/metrics/Metrics.ts b/packages/service-core/src/metrics/Metrics.ts index cf5098243..edf39888e 100644 --- a/packages/service-core/src/metrics/Metrics.ts +++ b/packages/service-core/src/metrics/Metrics.ts @@ -4,7 +4,6 @@ import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; import { Resource } from '@opentelemetry/resources'; import { MeterProvider, MetricReader, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; import { logger } from '@powersync/lib-services-framework'; -import * as jpgwire from '@powersync/service-jpgwire'; import * as storage from '../storage/storage-index.js'; import * as system from '../system/system-index.js'; import * as util from '../util/util-index.js'; @@ -240,14 +239,6 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable result.observe(metrics.replication_size_bytes); } }); - - const class_scoped_data_replicated_bytes = this.data_replicated_bytes; - // Record replicated bytes using global jpgwire metrics. - jpgwire.setMetricsRecorder({ - addBytesRead(bytes) { - class_scoped_data_replicated_bytes.add(bytes); - } - }); } public async getMetricValueForTests(name: string): Promise { diff --git a/packages/service-core/src/util/util-index.ts b/packages/service-core/src/util/util-index.ts index e1a556d32..5581284e6 100644 --- a/packages/service-core/src/util/util-index.ts +++ b/packages/service-core/src/util/util-index.ts @@ -1,23 +1,21 @@ export * from './alerting.js'; export * from './env.js'; export * from './memory-tracking.js'; -export * from './migration_lib.js'; export * from './Mutex.js'; -export * from './populate_test_data.js'; export * from './protocol-types.js'; export * from './secs.js'; export * from './utils.js'; export * from './config.js'; -export * from './config/types.js'; export * from './config/compound-config-collector.js'; +export * from './config/types.js'; export * from './config/collectors/config-collector.js'; export * from './config/collectors/impl/base64-config-collector.js'; export * from './config/collectors/impl/fallback-config-collector.js'; export * from './config/collectors/impl/filesystem-config-collector.js'; -export * from './config/sync-rules/sync-collector.js'; export * from './config/sync-rules/impl/base64-sync-rules-collector.js'; export * from './config/sync-rules/impl/filesystem-sync-rules-collector.js'; export * from './config/sync-rules/impl/inline-sync-rules-collector.js'; +export * from './config/sync-rules/sync-collector.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d55af25d3..04d6c4691 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -218,9 +218,6 @@ importers: '@powersync/lib-services-framework': specifier: workspace:* version: link:../../libs/lib-services - '@powersync/service-jpgwire': - specifier: workspace:* - version: link:../jpgwire '@powersync/service-jsonbig': specifier: workspace:* version: link:../jsonbig @@ -269,9 +266,6 @@ importers: node-fetch: specifier: ^3.3.2 version: 3.3.2 - pgwire: - specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -370,6 +364,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:* version: link:../packages/jsonbig + '@powersync/service-module-postgres': + specifier: workspace:* + version: link:../modules/module-postgres '@powersync/service-rsocket-router': specifier: workspace:* version: link:../packages/rsocket-router @@ -4089,11 +4086,6 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.24.1 - '@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4275,18 +4267,6 @@ snapshots: - supports-color optional: true - '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/api-logs': 0.52.1 - '@types/shimmer': 1.2.0 - import-in-the-middle: 1.9.0 - require-in-the-middle: 7.3.0 - semver: 7.6.2 - shimmer: 1.2.1 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4352,12 +4332,6 @@ snapshots: '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 - '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4412,13 +4386,6 @@ snapshots: '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0)': - dependencies: - '@opentelemetry/api': 1.6.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 @@ -4426,6 +4393,13 @@ snapshots: '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@opentelemetry/semantic-conventions@1.17.0': {} '@opentelemetry/semantic-conventions@1.24.1': {} @@ -4517,8 +4491,8 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) @@ -4535,11 +4509,11 @@ snapshots: '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4547,12 +4521,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.6.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.6.0) - '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.6.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.25.1 '@sentry/core': 8.17.0 '@sentry/types': 8.17.0 diff --git a/service/package.json b/service/package.json index 8cc91e37e..daf5f882d 100644 --- a/service/package.json +++ b/service/package.json @@ -16,7 +16,7 @@ "@opentelemetry/sdk-metrics": "^1.17.0", "@powersync/service-core": "workspace:*", "@powersync/lib-services-framework": "workspace:*", - "@powersync/service-module-postgres": "workapce:*", + "@powersync/service-module-postgres": "workspace:*", "@powersync/service-jpgwire": "workspace:*", "@powersync/service-jsonbig": "workspace:*", "@powersync/service-rsocket-router": "workspace:*", diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index 1f9e4b77f..c7ee05e7f 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -1,9 +1,23 @@ import { container, logger } from '@powersync/lib-services-framework'; -import { Metrics, migrations, replication, system } from '@powersync/service-core'; +import { Metrics, migrations, system } from '@powersync/service-core'; export async function startStreamWorker(serviceContext: system.ServiceContext) { logger.info('Booting'); + serviceContext.withLifecycle(serviceContext.storage, { + async start(storage) { + const instanceId = await storage.getPowerSyncInstanceId(); + await Metrics.initialise({ + powersync_instance_id: instanceId, + disable_telemetry_sharing: serviceContext.configuration.telemetry.disable_telemetry_sharing, + internal_metrics_endpoint: serviceContext.configuration.telemetry.internal_service_endpoint + }); + }, + async stop() { + await Metrics.getInstance().shutdown(); + } + }); + // Self hosted version allows for automatic migrations if (!serviceContext.configuration.migrations?.disable_auto_migration) { await migrations.migrate({ @@ -12,17 +26,14 @@ export async function startStreamWorker(serviceContext: system.ServiceContext) { }); } - // TODO use replication engine - serviceContext.withLifecycle(new replication.WalStreamManager(system), { - start: (manager) => manager.start(), - stop: (manager) => manager.stop() - }); - logger.info('Starting system'); await serviceContext.start(); logger.info('System started'); - Metrics.getInstance().configureReplicationMetrics(system); + // Start the replication engine + await serviceContext.replicationEngine.start(); + + Metrics.getInstance().configureReplicationMetrics(serviceContext); await container.probes.ready(); } From 5dfe1806ba7737c354d0c07903f6526146ef3a8d Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 17:21:01 +0200 Subject: [PATCH 059/298] configure metrics --- .../src/module/PostgresModule.ts | 18 ++++++------- .../service-core/src/system/ServiceContext.ts | 26 +++++++++++++++++++ service/src/runners/server.ts | 16 +----------- service/src/runners/stream-worker.ts | 18 ++----------- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 9032b6e83..ad481b4ac 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,6 +1,6 @@ -import * as t from 'ts-codec'; - import { api, auth, db, replication, storage, system } from '@powersync/service-core'; +import * as jpgwire from '@powersync/service-jpgwire'; +import * as t from 'ts-codec'; import { logger } from '@powersync/lib-services-framework'; import * as types from '../types/types.js'; @@ -26,14 +26,12 @@ export class PostgresModule extends replication.ReplicationModule { async register(context: system.ServiceContext): Promise {} async initialize(context: system.ServiceContext): Promise { - // TODO metrics - // const class_scoped_data_replicated_bytes = this.data_replicated_bytes; - // // Record replicated bytes using global jpgwire metrics. - // jpgwire.setMetricsRecorder({ - // addBytesRead(bytes) { - // class_scoped_data_replicated_bytes.add(bytes); - // } - // }); + // Record replicated bytes using global jpgwire metrics. + jpgwire.setMetricsRecorder({ + addBytesRead(bytes) { + context.metrics.data_replicated_bytes.add(bytes); + } + }); // Register the Supabase key collector(s) (context.configuration.connections ?? []) diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index 25843c980..14ba130c5 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -1,4 +1,5 @@ import { LifeCycledSystem, ServiceIdentifier, container } from '@powersync/lib-services-framework'; +import { Metrics } from '../metrics/Metrics.js'; import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; import { RouterEngine } from '../routes/RouterEngine.js'; import { BucketStorageFactory } from '../storage/BucketStorage.js'; @@ -15,6 +16,7 @@ import { CompoundConfigCollector } from '../util/util-index.js'; export class ServiceContext extends LifeCycledSystem { private _storage: BucketStorageFactory | null; private _configuration: ResolvedPowerSyncConfig | null; + private _metrics: Metrics | null; protected storageProviders: Map; routerEngine: RouterEngine; @@ -35,6 +37,13 @@ export class ServiceContext extends LifeCycledSystem { return this._storage; } + get metrics(): Metrics { + if (!this._metrics) { + throw new Error(`Attempt to use metrics before [initialize] has been called`); + } + return this._metrics; + } + get replicationEngine(): ReplicationEngine { // TODO clean this up return container.getImplementation(ReplicationEngine); @@ -46,6 +55,7 @@ export class ServiceContext extends LifeCycledSystem { // These will only be set once `initialize` has been called this._storage = null; this._configuration = null; + this._metrics = null; this.storageProviders = new Map(); // Mongo storage is available as an option by default @@ -104,6 +114,12 @@ export class ServiceContext extends LifeCycledSystem { stop: () => disposer() }); + // Metrics go here for now + this._metrics = await this.initializeMetrics(); + this.withLifecycle(null, { + stop: () => Metrics.getInstance().shutdown() + }); + // TODO neaten this container.register( ReplicationEngine, @@ -112,4 +128,14 @@ export class ServiceContext extends LifeCycledSystem { }) ); } + + protected async initializeMetrics() { + const instanceId = await this.storage.getPowerSyncInstanceId(); + await Metrics.initialise({ + powersync_instance_id: instanceId, + disable_telemetry_sharing: this.configuration.telemetry.disable_telemetry_sharing, + internal_metrics_endpoint: this.configuration.telemetry.internal_service_endpoint + }); + return Metrics.getInstance(); + } } diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 10ddc5231..1e0ef05d2 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -12,26 +12,12 @@ import { SocketRouter } from '../routes/router.js'; export async function startServer(serviceContext: core.system.ServiceContext) { logger.info('Booting'); - serviceContext.withLifecycle(serviceContext.storage, { - async start(storage) { - const instanceId = await storage.getPowerSyncInstanceId(); - await core.Metrics.initialise({ - powersync_instance_id: instanceId, - disable_telemetry_sharing: serviceContext.configuration.telemetry.disable_telemetry_sharing, - internal_metrics_endpoint: serviceContext.configuration.telemetry.internal_service_endpoint - }); - }, - async stop() { - await core.Metrics.getInstance().shutdown(); - } - }); - logger.info('Starting service'); // TODO cleanup the initialization of metrics await serviceContext.start(); - core.Metrics.getInstance().configureApiMetrics(); + serviceContext.metrics.configureApiMetrics(); await serviceContext.routerEngine.start(async (routes) => { const server = fastify.fastify(); diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index c7ee05e7f..6f2539aff 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -1,23 +1,9 @@ import { container, logger } from '@powersync/lib-services-framework'; -import { Metrics, migrations, system } from '@powersync/service-core'; +import { migrations, system } from '@powersync/service-core'; export async function startStreamWorker(serviceContext: system.ServiceContext) { logger.info('Booting'); - serviceContext.withLifecycle(serviceContext.storage, { - async start(storage) { - const instanceId = await storage.getPowerSyncInstanceId(); - await Metrics.initialise({ - powersync_instance_id: instanceId, - disable_telemetry_sharing: serviceContext.configuration.telemetry.disable_telemetry_sharing, - internal_metrics_endpoint: serviceContext.configuration.telemetry.internal_service_endpoint - }); - }, - async stop() { - await Metrics.getInstance().shutdown(); - } - }); - // Self hosted version allows for automatic migrations if (!serviceContext.configuration.migrations?.disable_auto_migration) { await migrations.migrate({ @@ -33,7 +19,7 @@ export async function startStreamWorker(serviceContext: system.ServiceContext) { // Start the replication engine await serviceContext.replicationEngine.start(); - Metrics.getInstance().configureReplicationMetrics(serviceContext); + serviceContext.metrics.configureReplicationMetrics(serviceContext); await container.probes.ready(); } From ab22dc82c19b9a03c0fe60ac27eebcf5e998c588 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 17:29:14 +0200 Subject: [PATCH 060/298] metrics cleanup --- packages/service-core/src/system/ServiceContext.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index 14ba130c5..e9de95cc8 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -19,9 +19,9 @@ export class ServiceContext extends LifeCycledSystem { private _metrics: Metrics | null; protected storageProviders: Map; + routerEngine: RouterEngine; configCollector: CompoundConfigCollector; - // TODO metrics get configuration(): ResolvedPowerSyncConfig { if (!this._configuration) { @@ -116,8 +116,8 @@ export class ServiceContext extends LifeCycledSystem { // Metrics go here for now this._metrics = await this.initializeMetrics(); - this.withLifecycle(null, { - stop: () => Metrics.getInstance().shutdown() + this.withLifecycle(this.metrics, { + stop: () => this.metrics.shutdown() }); // TODO neaten this From b9f5a998e1a3887c14416da66a975300fdf0d612 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 17:53:42 +0200 Subject: [PATCH 061/298] start moving tests --- .../module-postgres}/test/src/__snapshots__/pg_test.test.ts.snap | 0 .../module-postgres}/test/src/large_batch.test.ts | 0 .../module-postgres}/test/src/pg_test.test.ts | 0 .../module-postgres}/test/src/wal_stream.test.ts | 0 .../module-postgres}/test/src/wal_stream_utils.ts | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {packages/service-core => modules/module-postgres}/test/src/__snapshots__/pg_test.test.ts.snap (100%) rename {packages/service-core => modules/module-postgres}/test/src/large_batch.test.ts (100%) rename {packages/service-core => modules/module-postgres}/test/src/pg_test.test.ts (100%) rename {packages/service-core => modules/module-postgres}/test/src/wal_stream.test.ts (100%) rename {packages/service-core => modules/module-postgres}/test/src/wal_stream_utils.ts (100%) diff --git a/packages/service-core/test/src/__snapshots__/pg_test.test.ts.snap b/modules/module-postgres/test/src/__snapshots__/pg_test.test.ts.snap similarity index 100% rename from packages/service-core/test/src/__snapshots__/pg_test.test.ts.snap rename to modules/module-postgres/test/src/__snapshots__/pg_test.test.ts.snap diff --git a/packages/service-core/test/src/large_batch.test.ts b/modules/module-postgres/test/src/large_batch.test.ts similarity index 100% rename from packages/service-core/test/src/large_batch.test.ts rename to modules/module-postgres/test/src/large_batch.test.ts diff --git a/packages/service-core/test/src/pg_test.test.ts b/modules/module-postgres/test/src/pg_test.test.ts similarity index 100% rename from packages/service-core/test/src/pg_test.test.ts rename to modules/module-postgres/test/src/pg_test.test.ts diff --git a/packages/service-core/test/src/wal_stream.test.ts b/modules/module-postgres/test/src/wal_stream.test.ts similarity index 100% rename from packages/service-core/test/src/wal_stream.test.ts rename to modules/module-postgres/test/src/wal_stream.test.ts diff --git a/packages/service-core/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts similarity index 100% rename from packages/service-core/test/src/wal_stream_utils.ts rename to modules/module-postgres/test/src/wal_stream_utils.ts From 5ed868deef5b779a28efd3b65d1794737f08a498 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 30 Jul 2024 17:53:50 +0200 Subject: [PATCH 062/298] more test moving --- modules/module-postgres/package.json | 3 ++- modules/module-postgres/test/src/env.ts | 8 ++++++++ .../module-postgres/test/src/large_batch.test.ts | 4 ++-- modules/module-postgres/test/src/pg_test.test.ts | 13 +++++++------ .../module-postgres/test/src/placeholder.test.ts | 5 ----- modules/module-postgres/test/src/wal_stream.test.ts | 6 +++--- .../module-postgres/test/src/wal_stream_utils.ts | 6 +++--- modules/module-postgres/test/tsconfig.json | 3 ++- packages/service-core/test/src/compacting.test.ts | 4 ++-- packages/service-core/test/src/env.ts | 1 - .../service-core/test/src/merge_iterable.test.ts | 7 +------ packages/service-core/test/src/util.ts | 10 +++++----- pnpm-lock.yaml | 3 +++ 13 files changed, 38 insertions(+), 35 deletions(-) create mode 100644 modules/module-postgres/test/src/env.ts delete mode 100644 modules/module-postgres/test/src/placeholder.test.ts diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index f48de53a0..3346ddfd5 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -31,6 +31,7 @@ "devDependencies": { "@types/uuid": "^9.0.4", "typescript": "^5.2.2", - "vitest": "^0.34.6" + "vitest": "^0.34.6", + "vite-tsconfig-paths": "^4.3.2" } } diff --git a/modules/module-postgres/test/src/env.ts b/modules/module-postgres/test/src/env.ts new file mode 100644 index 000000000..4e9e1694a --- /dev/null +++ b/modules/module-postgres/test/src/env.ts @@ -0,0 +1,8 @@ +import { utils } from '@powersync/lib-services-framework'; + +export const env = utils.collectEnvironmentVariables({ + MONGO_TEST_URL: utils.type.string.default('mongodb://localhost:27017/powersync_test'), + PG_TEST_URL: utils.type.string.default('postgres://postgres:postgres@localhost:5432/powersync_test'), + CI: utils.type.boolean.default('false'), + SLOW_TESTS: utils.type.boolean.default('false') +}); diff --git a/modules/module-postgres/test/src/large_batch.test.ts b/modules/module-postgres/test/src/large_batch.test.ts index edbc28610..071784f0b 100644 --- a/modules/module-postgres/test/src/large_batch.test.ts +++ b/modules/module-postgres/test/src/large_batch.test.ts @@ -1,8 +1,8 @@ +import { MONGO_STORAGE_FACTORY, StorageFactory, TEST_CONNECTION_OPTIONS } from '@core-tests/util.js'; import { describe, expect, test } from 'vitest'; +import { populateData } from '../../dist/util/populate_test_data.js'; import { env } from './env.js'; -import { MONGO_STORAGE_FACTORY, StorageFactory, TEST_CONNECTION_OPTIONS } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; -import { populateData } from '../../dist/util/populate_test_data.js'; describe('batch replication tests - mongodb', function () { // These are slow but consistent tests. diff --git a/modules/module-postgres/test/src/pg_test.test.ts b/modules/module-postgres/test/src/pg_test.test.ts index 5ea9eb041..ccc16b439 100644 --- a/modules/module-postgres/test/src/pg_test.test.ts +++ b/modules/module-postgres/test/src/pg_test.test.ts @@ -1,10 +1,10 @@ -import { describe, expect, test } from 'vitest'; -import { WalStream } from '../../src/replication/WalStream.js'; +import { constructAfterRecord } from '@/utils/pgwire_utils.js'; +import { clearTestDb, connectPgPool, connectPgWire, TEST_URI } from '@core-tests/util.js'; +import { api } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; -import { clearTestDb, connectPgPool, connectPgWire, TEST_URI } from './util.js'; -import { constructAfterRecord } from '../../src/util/pgwire_utils.js'; import { SqliteRow } from '@powersync/service-sync-rules'; -import { getConnectionSchema } from '../../src/api/schema.js'; +import { describe, expect, test } from 'vitest'; +import { WalStream } from '../../src/replication/WalStream.js'; describe('pg data types', () => { async function setupTable(db: pgwire.PgClient) { @@ -427,7 +427,8 @@ VALUES(10, ARRAY['null']::TEXT[]); await setupTable(db); - const schema = await getConnectionSchema(db); + // TODO need a test for adapter + const schema = await api.getConnectionsSchema(db); expect(schema).toMatchSnapshot(); }); }); diff --git a/modules/module-postgres/test/src/placeholder.test.ts b/modules/module-postgres/test/src/placeholder.test.ts deleted file mode 100644 index 289c3e48d..000000000 --- a/modules/module-postgres/test/src/placeholder.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { describe } from 'vitest'; - -describe('Tests', () => { - // TODO move tests -}); diff --git a/modules/module-postgres/test/src/wal_stream.test.ts b/modules/module-postgres/test/src/wal_stream.test.ts index a6cb83fa1..d330d4f42 100644 --- a/modules/module-postgres/test/src/wal_stream.test.ts +++ b/modules/module-postgres/test/src/wal_stream.test.ts @@ -1,10 +1,10 @@ +import { BucketStorageFactory } from '@/storage/BucketStorage.js'; +import { Metrics } from '@powersync/service-core'; +import { pgwireRows } from '@powersync/service-jpgwire'; import * as crypto from 'crypto'; import { describe, expect, test } from 'vitest'; -import { BucketStorageFactory } from '@/storage/BucketStorage.js'; import { MONGO_STORAGE_FACTORY } from './util.js'; import { putOp, removeOp, walStreamTest } from './wal_stream_utils.js'; -import { pgwireRows } from '@powersync/service-jpgwire'; -import { Metrics } from '@/metrics/Metrics.js'; type StorageFactory = () => Promise; diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index 7c639a6e5..e2cb560bc 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -1,11 +1,11 @@ +import { BucketStorageFactory, SyncRulesBucketStorage } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; +import { JSONBig } from '@powersync/service-jsonbig'; import { WalStream, WalStreamOptions } from '../../src/replication/WalStream.js'; -import { BucketStorageFactory, SyncRulesBucketStorage } from '../../src/storage/BucketStorage.js'; +import { PgManager } from '../../src/util/PgManager.js'; import { OplogEntry } from '../../src/util/protocol-types.js'; import { getClientCheckpoint } from '../../src/util/utils.js'; import { TEST_CONNECTION_OPTIONS, clearTestDb } from './util.js'; -import { PgManager } from '../../src/util/PgManager.js'; -import { JSONBig } from '@powersync/service-jsonbig'; /** * Tests operating on the wal stream need to configure the stream and manage asynchronous diff --git a/modules/module-postgres/test/tsconfig.json b/modules/module-postgres/test/tsconfig.json index 4e77a4239..c2ff2061b 100644 --- a/modules/module-postgres/test/tsconfig.json +++ b/modules/module-postgres/test/tsconfig.json @@ -8,7 +8,8 @@ "skipLibCheck": true, "sourceMap": true, "paths": { - "@/*": ["../src/*"] + "@/*": ["../src/*"], + "@core-tests/*": ["../../../packages/service-core/test/src/*"] } }, "include": ["src"], diff --git a/packages/service-core/test/src/compacting.test.ts b/packages/service-core/test/src/compacting.test.ts index e6bd983b0..7cee42c55 100644 --- a/packages/service-core/test/src/compacting.test.ts +++ b/packages/service-core/test/src/compacting.test.ts @@ -1,9 +1,9 @@ +import { MongoCompactOptions } from '@/storage/mongo/MongoCompactor.js'; import { SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; +import { validateCompactedBucket } from './bucket_validation.js'; import { makeTestTable, MONGO_STORAGE_FACTORY } from './util.js'; import { oneFromAsync } from './wal_stream_utils.js'; -import { MongoCompactOptions } from '@/storage/mongo/MongoCompactor.js'; -import { reduceBucket, validateCompactedBucket, validateBucket } from './bucket_validation.js'; const TEST_TABLE = makeTestTable('test', ['id']); diff --git a/packages/service-core/test/src/env.ts b/packages/service-core/test/src/env.ts index 4e9e1694a..af0ef2eef 100644 --- a/packages/service-core/test/src/env.ts +++ b/packages/service-core/test/src/env.ts @@ -2,7 +2,6 @@ import { utils } from '@powersync/lib-services-framework'; export const env = utils.collectEnvironmentVariables({ MONGO_TEST_URL: utils.type.string.default('mongodb://localhost:27017/powersync_test'), - PG_TEST_URL: utils.type.string.default('postgres://postgres:postgres@localhost:5432/powersync_test'), CI: utils.type.boolean.default('false'), SLOW_TESTS: utils.type.boolean.default('false') }); diff --git a/packages/service-core/test/src/merge_iterable.test.ts b/packages/service-core/test/src/merge_iterable.test.ts index 55550be9e..bf17d7286 100644 --- a/packages/service-core/test/src/merge_iterable.test.ts +++ b/packages/service-core/test/src/merge_iterable.test.ts @@ -1,11 +1,6 @@ import * as timers from 'timers/promises'; import { describe, expect, test } from 'vitest'; -import { - FixedMergeAsyncIterable, - mergeAsyncIterables, - mergeAsyncIterablesNew, - mergeAsyncIterablesOld -} from '../../src/sync/merge.js'; +import { mergeAsyncIterablesNew, mergeAsyncIterablesOld } from '../../src/sync/merge.js'; type MergeIteratorFunction = (source: AsyncIterable[]) => AsyncIterable; diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 06fe8a7e7..1ac63be4a 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -1,16 +1,16 @@ +import { Metrics } from '@/metrics/Metrics.js'; +import { SourceTable } from '@/storage/SourceTable.js'; +import { SyncBucketData } from '@/util/protocol-types.js'; +import { hashData } from '@/util/utils.js'; import * as pgwire from '@powersync/service-jpgwire'; import { normalizeConnection } from '@powersync/service-types'; +import * as bson from 'bson'; import * as mongo from 'mongodb'; import { BucketStorageFactory, SyncBucketDataBatch } from '../../src/storage/BucketStorage.js'; import { MongoBucketStorage } from '../../src/storage/MongoBucketStorage.js'; import { PowerSyncMongo } from '../../src/storage/mongo/db.js'; import { escapeIdentifier } from '../../src/util/pgwire_utils.js'; import { env } from './env.js'; -import { Metrics } from '@/metrics/Metrics.js'; -import { hashData } from '@/util/utils.js'; -import { SourceTable } from '@/storage/SourceTable.js'; -import * as bson from 'bson'; -import { SyncBucketData } from '@/util/protocol-types.js'; // The metrics need to be initialised before they can be used await Metrics.initialise({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04d6c4691..a732e4ee9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,6 +130,9 @@ importers: typescript: specifier: ^5.2.2 version: 5.2.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) vitest: specifier: ^0.34.6 version: 0.34.6 From aac3b1eeddf9e63bdaa5c42e11485f1ebb4ea274 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 10:51:22 +0200 Subject: [PATCH 063/298] move stream test --- .../module-postgres}/test/src/schema_changes.test.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {packages/service-core => modules/module-postgres}/test/src/schema_changes.test.ts (100%) diff --git a/packages/service-core/test/src/schema_changes.test.ts b/modules/module-postgres/test/src/schema_changes.test.ts similarity index 100% rename from packages/service-core/test/src/schema_changes.test.ts rename to modules/module-postgres/test/src/schema_changes.test.ts From 1a4f7d6ac97c5de37539202b466c751e781b65b6 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 11:02:34 +0200 Subject: [PATCH 064/298] wip tests --- .../src/module/PostgresModule.ts | 6 +- .../test/src/large_batch.test.ts | 5 +- .../module-postgres/test/src/pg_test.test.ts | 11 ++-- .../test/src/schema_changes.test.ts | 7 ++- modules/module-postgres/test/src/setup.ts | 7 +++ modules/module-postgres/test/src/util.ts | 56 +++++++++++++++++++ .../test/src/wal_stream.test.ts | 8 +-- .../test/src/wal_stream_utils.ts | 54 ++---------------- modules/module-postgres/test/tsconfig.json | 9 ++- .../src/storage/MongoBucketStorage.ts | 17 +++--- .../src/storage/mongo/MongoBucketBatch.ts | 7 ++- .../storage/mongo/MongoSyncBucketStorage.ts | 5 +- packages/service-core/test/src/auth.test.ts | 14 ++--- .../test/src/broadcast_iterable.test.ts | 2 +- .../service-core/test/src/compacting.test.ts | 2 +- .../test/src/data_storage.test.ts | 4 +- .../test/src/merge_iterable.test.ts | 2 +- .../service-core/test/src/stream_utils.ts | 42 ++++++++++++++ packages/service-core/test/src/util.ts | 54 ++---------------- .../service-core/test/src/validation.test.ts | 2 +- packages/service-core/test/tsconfig.json | 1 - 21 files changed, 170 insertions(+), 145 deletions(-) create mode 100644 modules/module-postgres/test/src/setup.ts create mode 100644 modules/module-postgres/test/src/util.ts create mode 100644 packages/service-core/test/src/stream_utils.ts diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index ad481b4ac..84a1869f8 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -34,8 +34,8 @@ export class PostgresModule extends replication.ReplicationModule { }); // Register the Supabase key collector(s) - (context.configuration.connections ?? []) - .map((baseConfig) => { + context.configuration.connections + ?.map((baseConfig) => { if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { return; } @@ -47,7 +47,7 @@ export class PostgresModule extends replication.ReplicationModule { }) .filter((c) => !!c) .forEach((config) => { - const keyCollector = new SupabaseKeyCollector(config); + const keyCollector = new SupabaseKeyCollector(config!); context.withLifecycle(keyCollector, { // Close the internal pool stop: (collector) => collector.shutdown() diff --git a/modules/module-postgres/test/src/large_batch.test.ts b/modules/module-postgres/test/src/large_batch.test.ts index 071784f0b..6c1d373eb 100644 --- a/modules/module-postgres/test/src/large_batch.test.ts +++ b/modules/module-postgres/test/src/large_batch.test.ts @@ -1,7 +1,8 @@ -import { MONGO_STORAGE_FACTORY, StorageFactory, TEST_CONNECTION_OPTIONS } from '@core-tests/util.js'; +import { MONGO_STORAGE_FACTORY, StorageFactory } from '@core-tests/util.js'; +import { populateData } from '@module/utils/populate_test_data.js'; import { describe, expect, test } from 'vitest'; -import { populateData } from '../../dist/util/populate_test_data.js'; import { env } from './env.js'; +import { TEST_CONNECTION_OPTIONS } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; describe('batch replication tests - mongodb', function () { diff --git a/modules/module-postgres/test/src/pg_test.test.ts b/modules/module-postgres/test/src/pg_test.test.ts index ccc16b439..e97aea837 100644 --- a/modules/module-postgres/test/src/pg_test.test.ts +++ b/modules/module-postgres/test/src/pg_test.test.ts @@ -1,10 +1,9 @@ -import { constructAfterRecord } from '@/utils/pgwire_utils.js'; -import { clearTestDb, connectPgPool, connectPgWire, TEST_URI } from '@core-tests/util.js'; -import { api } from '@powersync/service-core'; +import { constructAfterRecord } from '@module/utils/pgwire_utils.js'; import * as pgwire from '@powersync/service-jpgwire'; import { SqliteRow } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; -import { WalStream } from '../../src/replication/WalStream.js'; +// import { WalStream } from '../../src/replication/WalStream.js'; +import { clearTestDb, connectPgPool, connectPgWire, TEST_URI } from './util.js'; describe('pg data types', () => { async function setupTable(db: pgwire.PgClient) { @@ -428,8 +427,8 @@ VALUES(10, ARRAY['null']::TEXT[]); await setupTable(db); // TODO need a test for adapter - const schema = await api.getConnectionsSchema(db); - expect(schema).toMatchSnapshot(); + // const schema = await api.getConnectionsSchema(db); + // expect(schema).toMatchSnapshot(); }); }); diff --git a/modules/module-postgres/test/src/schema_changes.test.ts b/modules/module-postgres/test/src/schema_changes.test.ts index d14272a77..9fb8d82b9 100644 --- a/modules/module-postgres/test/src/schema_changes.test.ts +++ b/modules/module-postgres/test/src/schema_changes.test.ts @@ -1,7 +1,8 @@ +import { compareIds, putOp, removeOp } from '@core-tests/stream_utils.js'; +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; +import { BucketStorageFactory } from '@powersync/service-core'; import { describe, expect, test } from 'vitest'; -import { BucketStorageFactory } from '../../src/storage/BucketStorage.js'; -import { MONGO_STORAGE_FACTORY } from './util.js'; -import { compareIds, putOp, removeOp, walStreamTest } from './wal_stream_utils.js'; +import { walStreamTest } from './wal_stream_utils.js'; type StorageFactory = () => Promise; diff --git a/modules/module-postgres/test/src/setup.ts b/modules/module-postgres/test/src/setup.ts new file mode 100644 index 000000000..8e4ece4c9 --- /dev/null +++ b/modules/module-postgres/test/src/setup.ts @@ -0,0 +1,7 @@ +import { container } from '@powersync/lib-services-framework'; +import { beforeAll } from 'vitest'; + +beforeAll(() => { + // Your setup code here + container.registerDefaults(); +}); diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts new file mode 100644 index 000000000..4ef8f9945 --- /dev/null +++ b/modules/module-postgres/test/src/util.ts @@ -0,0 +1,56 @@ +import * as types from '@module/types/types.js'; +import * as pg_utils from '@module/utils/pgwire_utils.js'; +import { Metrics } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; +import { env } from './env.js'; + +// The metrics need to be initialized before they can be used +await Metrics.initialise({ + disable_telemetry_sharing: true, + powersync_instance_id: 'test', + internal_metrics_endpoint: 'unused.for.tests.com' +}); +Metrics.getInstance().resetCounters(); + +export const TEST_URI = env.PG_TEST_URL; + +export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ + type: 'postgresql', + uri: TEST_URI, + sslmode: 'disable' +}); + +export async function clearTestDb(db: pgwire.PgClient) { + await db.query( + "select pg_drop_replication_slot(slot_name) from pg_replication_slots where active = false and slot_name like 'test_%'" + ); + + await db.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); + try { + await db.query(`DROP PUBLICATION powersync`); + } catch (e) { + // Ignore + } + + await db.query(`CREATE PUBLICATION powersync FOR ALL TABLES`); + + const tableRows = pgwire.pgwireRows( + await db.query(`SELECT table_name FROM information_schema.tables where table_schema = 'public'`) + ); + for (let row of tableRows) { + const name = row.table_name; + if (name.startsWith('test_')) { + await db.query(`DROP TABLE public.${pg_utils.escapeIdentifier(name)}`); + } + } +} + +export async function connectPgWire(type?: 'replication' | 'standard') { + const db = await pgwire.connectPgWire(TEST_CONNECTION_OPTIONS, { type }); + return db; +} + +export function connectPgPool() { + const db = pgwire.connectPgWirePool(TEST_CONNECTION_OPTIONS); + return db; +} diff --git a/modules/module-postgres/test/src/wal_stream.test.ts b/modules/module-postgres/test/src/wal_stream.test.ts index d330d4f42..a55454654 100644 --- a/modules/module-postgres/test/src/wal_stream.test.ts +++ b/modules/module-postgres/test/src/wal_stream.test.ts @@ -1,10 +1,10 @@ -import { BucketStorageFactory } from '@/storage/BucketStorage.js'; -import { Metrics } from '@powersync/service-core'; +import { putOp, removeOp } from '@core-tests/stream_utils.js'; +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; +import { BucketStorageFactory, Metrics } from '@powersync/service-core'; import { pgwireRows } from '@powersync/service-jpgwire'; import * as crypto from 'crypto'; import { describe, expect, test } from 'vitest'; -import { MONGO_STORAGE_FACTORY } from './util.js'; -import { putOp, removeOp, walStreamTest } from './wal_stream_utils.js'; +import { walStreamTest } from './wal_stream_utils.js'; type StorageFactory = () => Promise; diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index e2cb560bc..0cae43030 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -1,9 +1,5 @@ import { BucketStorageFactory, SyncRulesBucketStorage } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; -import { JSONBig } from '@powersync/service-jsonbig'; -import { WalStream, WalStreamOptions } from '../../src/replication/WalStream.js'; -import { PgManager } from '../../src/util/PgManager.js'; -import { OplogEntry } from '../../src/util/protocol-types.js'; import { getClientCheckpoint } from '../../src/util/utils.js'; import { TEST_CONNECTION_OPTIONS, clearTestDb } from './util.js'; @@ -19,10 +15,10 @@ export function walStreamTest( ): () => Promise { return async () => { const f = await factory(); - const connections = new PgManager(TEST_CONNECTION_OPTIONS, {}); + const pool = pgwire.connectPgWirePool(TEST_CONNECTION_OPTIONS, {}); - await clearTestDb(connections.pool); - const context = new WalStreamTestContext(f, connections); + await clearTestDb(pool); + const context = new WalStreamTestContext(f, pool); try { await test(context); } finally { @@ -38,7 +34,7 @@ export class WalStreamTestContext { public storage?: SyncRulesBucketStorage; private replicationConnection?: pgwire.PgConnection; - constructor(public factory: BucketStorageFactory, public connections: PgManager) {} + constructor(public factory: BucketStorageFactory, public connections: pgwire.PgClient) {} async dispose() { this.abortController.abort(); @@ -47,7 +43,7 @@ export class WalStreamTestContext { } get pool() { - return this.connections.pool; + return this.connections; } async updateSyncRules(content: string) { @@ -114,43 +110,3 @@ export class WalStreamTestContext { return batches[0]?.batch.data ?? []; } } - -export function putOp(table: string, data: Record): Partial { - return { - op: 'PUT', - object_type: table, - object_id: data.id, - data: JSONBig.stringify(data) - }; -} - -export function removeOp(table: string, id: string): Partial { - return { - op: 'REMOVE', - object_type: table, - object_id: id - }; -} - -export function compareIds(a: OplogEntry, b: OplogEntry) { - return a.object_id!.localeCompare(b.object_id!); -} - -export async function fromAsync(source: Iterable | AsyncIterable): Promise { - const items: T[] = []; - for await (const item of source) { - items.push(item); - } - return items; -} - -export async function oneFromAsync(source: Iterable | AsyncIterable): Promise { - const items: T[] = []; - for await (const item of source) { - items.push(item); - } - if (items.length != 1) { - throw new Error(`One item expected, got: ${items.length}`); - } - return items[0]; -} diff --git a/modules/module-postgres/test/tsconfig.json b/modules/module-postgres/test/tsconfig.json index c2ff2061b..a0f67051a 100644 --- a/modules/module-postgres/test/tsconfig.json +++ b/modules/module-postgres/test/tsconfig.json @@ -8,7 +8,8 @@ "skipLibCheck": true, "sourceMap": true, "paths": { - "@/*": ["../src/*"], + "@/*": ["../../../packages/service-core/src/*"], + "@module/*": ["../src/*"], "@core-tests/*": ["../../../packages/service-core/test/src/*"] } }, @@ -16,6 +17,12 @@ "references": [ { "path": "../" + }, + { + "path": "../../../packages/service-core/test" + }, + { + "path": "../../../packages/service-core/" } ] } diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index de7a9901a..1f4cbbe8f 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -1,14 +1,17 @@ -import * as mongo from 'mongodb'; -import * as timers from 'timers/promises'; -import { LRUCache } from 'lru-cache/min'; import { SqlSyncRules } from '@powersync/service-sync-rules'; import { wrapWithAbort } from 'ix/asynciterable/operators/withabort.js'; +import { LRUCache } from 'lru-cache/min'; +import * as mongo from 'mongodb'; +import * as timers from 'timers/promises'; -import * as replication from '../replication/replication-index.js'; +import * as locks from '../locks/locks-index.js'; import * as sync from '../sync/sync-index.js'; import * as util from '../util/util-index.js'; -import * as locks from '../locks/locks-index.js'; +import { ZERO_LSN } from './mongo/MongoBucketBatch.js'; + +import { logger } from '@powersync/lib-services-framework'; +import { v4 as uuid } from 'uuid'; import { ActiveCheckpoint, BucketStorageFactory, @@ -23,8 +26,6 @@ import { MongoSyncBucketStorage } from './mongo/MongoSyncBucketStorage.js'; import { PowerSyncMongo, PowerSyncMongoOptions } from './mongo/db.js'; import { SyncRuleDocument, SyncRuleState } from './mongo/models.js'; import { generateSlotName } from './mongo/util.js'; -import { v4 as uuid } from 'uuid'; -import { logger } from '@powersync/lib-services-framework'; export interface MongoBucketStorageOptions extends PowerSyncMongoOptions {} @@ -370,7 +371,7 @@ export class MongoBucketStorage implements BucketStorageFactory { private makeActiveCheckpoint(doc: SyncRuleDocument | null) { return { checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n), - lsn: doc?.last_checkpoint_lsn ?? replication.ZERO_LSN, + lsn: doc?.last_checkpoint_lsn ?? ZERO_LSN, hasSyncRules() { return doc != null; }, diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index 6fe66bf95..b7d18d914 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -3,7 +3,6 @@ import * as bson from 'bson'; import * as mongo from 'mongodb'; import { container, errors, logger } from '@powersync/lib-services-framework'; -import * as replication from '../../replication/replication-index.js'; import * as util from '../../util/util-index.js'; import { BucketStorageBatch, FlushedResult, mergeToast, SaveOptions } from '../BucketStorage.js'; import { SourceTable } from '../SourceTable.js'; @@ -25,6 +24,10 @@ const MAX_ROW_SIZE = 15 * 1024 * 1024; // // In the future, we can investigate allowing multiple replication streams operating independently. const replicationMutex = new util.Mutex(); + +// FIXME: This +export const ZERO_LSN = '00000000/00000000'; + export class MongoBucketBatch implements BucketStorageBatch { private readonly client: mongo.MongoClient; public readonly db: PowerSyncMongo; @@ -70,7 +73,7 @@ export class MongoBucketBatch implements BucketStorageBatch { this.slot_name = slot_name; this.session = this.client.startSession(); this.last_checkpoint_lsn = last_checkpoint_lsn; - this.no_checkpoint_before_lsn = no_checkpoint_before_lsn ?? replication.ZERO_LSN; + this.no_checkpoint_before_lsn = no_checkpoint_before_lsn ?? ZERO_LSN; } async flush(): Promise { diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index e34c79300..fdd292206 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -3,7 +3,6 @@ import * as bson from 'bson'; import * as mongo from 'mongodb'; import * as db from '../../db/db-index.js'; -import * as replication from '../../replication/replication-index.js'; import * as util from '../../util/util-index.js'; import { BucketDataBatchOptions, @@ -23,7 +22,7 @@ import { MongoBucketStorage } from '../MongoBucketStorage.js'; import { SourceTable } from '../SourceTable.js'; import { PowerSyncMongo } from './db.js'; import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './models.js'; -import { MongoBucketBatch } from './MongoBucketBatch.js'; +import { MongoBucketBatch, ZERO_LSN } from './MongoBucketBatch.js'; import { MongoCompactor } from './MongoCompactor.js'; import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js'; @@ -53,7 +52,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { ); return { checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n), - lsn: doc?.last_checkpoint_lsn ?? replication.ZERO_LSN + lsn: doc?.last_checkpoint_lsn ?? ZERO_LSN }; } diff --git a/packages/service-core/test/src/auth.test.ts b/packages/service-core/test/src/auth.test.ts index e80970ed9..709cfcb51 100644 --- a/packages/service-core/test/src/auth.test.ts +++ b/packages/service-core/test/src/auth.test.ts @@ -1,11 +1,11 @@ -import { describe, expect, test } from 'vitest'; -import { StaticKeyCollector } from '../../src/auth/StaticKeyCollector.js'; +import { CachedKeyCollector } from '@/auth/CachedKeyCollector.js'; +import { KeyResult } from '@/auth/KeyCollector.js'; +import { KeySpec } from '@/auth/KeySpec.js'; +import { KeyStore } from '@/auth/KeyStore.js'; +import { RemoteJWKSCollector } from '@/auth/RemoteJWKSCollector.js'; +import { StaticKeyCollector } from '@/auth/StaticKeyCollector.js'; import * as jose from 'jose'; -import { KeyStore } from '../../src/auth/KeyStore.js'; -import { KeySpec } from '../../src/auth/KeySpec.js'; -import { RemoteJWKSCollector } from '../../src/auth/RemoteJWKSCollector.js'; -import { KeyResult } from '../../src/auth/KeyCollector.js'; -import { CachedKeyCollector } from '../../src/auth/CachedKeyCollector.js'; +import { describe, expect, test } from 'vitest'; const publicKey: jose.JWK = { use: 'sig', diff --git a/packages/service-core/test/src/broadcast_iterable.test.ts b/packages/service-core/test/src/broadcast_iterable.test.ts index 0e70aac28..4d2cced7a 100644 --- a/packages/service-core/test/src/broadcast_iterable.test.ts +++ b/packages/service-core/test/src/broadcast_iterable.test.ts @@ -1,3 +1,4 @@ +import { BroadcastIterable, IterableSource } from '@/sync/BroadcastIterable.js'; import { AsyncIterableX, interval } from 'ix/asynciterable/index.js'; import { delayEach } from 'ix/asynciterable/operators/delayeach.js'; import { take } from 'ix/asynciterable/operators/take.js'; @@ -5,7 +6,6 @@ import { wrapWithAbort } from 'ix/asynciterable/operators/withabort.js'; import { toArray } from 'ix/asynciterable/toarray.js'; import * as timers from 'timers/promises'; import { describe, expect, test } from 'vitest'; -import { BroadcastIterable, IterableSource } from '../../src/sync/BroadcastIterable.js'; describe('BroadcastIterable', () => { test('should iterate', async () => { diff --git a/packages/service-core/test/src/compacting.test.ts b/packages/service-core/test/src/compacting.test.ts index 7cee42c55..8ae3ab588 100644 --- a/packages/service-core/test/src/compacting.test.ts +++ b/packages/service-core/test/src/compacting.test.ts @@ -2,8 +2,8 @@ import { MongoCompactOptions } from '@/storage/mongo/MongoCompactor.js'; import { SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { validateCompactedBucket } from './bucket_validation.js'; +import { oneFromAsync } from './stream_utils.js'; import { makeTestTable, MONGO_STORAGE_FACTORY } from './util.js'; -import { oneFromAsync } from './wal_stream_utils.js'; const TEST_TABLE = makeTestTable('test', ['id']); diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index 088cb5614..45fce8c0b 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -1,8 +1,8 @@ +import { BucketDataBatchOptions } from '@/storage/BucketStorage.js'; import { RequestParameters, SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; -import { BucketDataBatchOptions } from '../../src/storage/BucketStorage.js'; +import { fromAsync, oneFromAsync } from './stream_utils.js'; import { getBatchData, getBatchMeta, makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory } from './util.js'; -import { fromAsync, oneFromAsync } from './wal_stream_utils.js'; const TEST_TABLE = makeTestTable('test', ['id']); diff --git a/packages/service-core/test/src/merge_iterable.test.ts b/packages/service-core/test/src/merge_iterable.test.ts index bf17d7286..bd5123763 100644 --- a/packages/service-core/test/src/merge_iterable.test.ts +++ b/packages/service-core/test/src/merge_iterable.test.ts @@ -1,6 +1,6 @@ +import { mergeAsyncIterablesNew, mergeAsyncIterablesOld } from '@/sync/merge.js'; import * as timers from 'timers/promises'; import { describe, expect, test } from 'vitest'; -import { mergeAsyncIterablesNew, mergeAsyncIterablesOld } from '../../src/sync/merge.js'; type MergeIteratorFunction = (source: AsyncIterable[]) => AsyncIterable; diff --git a/packages/service-core/test/src/stream_utils.ts b/packages/service-core/test/src/stream_utils.ts new file mode 100644 index 000000000..bcbc9168e --- /dev/null +++ b/packages/service-core/test/src/stream_utils.ts @@ -0,0 +1,42 @@ +import { OplogEntry } from '@/util/protocol-types.js'; +import { JSONBig } from '@powersync/service-jsonbig'; + +export function putOp(table: string, data: Record): Partial { + return { + op: 'PUT', + object_type: table, + object_id: data.id, + data: JSONBig.stringify(data) + }; +} + +export function removeOp(table: string, id: string): Partial { + return { + op: 'REMOVE', + object_type: table, + object_id: id + }; +} + +export function compareIds(a: OplogEntry, b: OplogEntry) { + return a.object_id!.localeCompare(b.object_id!); +} + +export async function oneFromAsync(source: Iterable | AsyncIterable): Promise { + const items: T[] = []; + for await (const item of source) { + items.push(item); + } + if (items.length != 1) { + throw new Error(`One item expected, got: ${items.length}`); + } + return items[0]; +} + +export async function fromAsync(source: Iterable | AsyncIterable): Promise { + const items: T[] = []; + for await (const item of source) { + items.push(item); + } + return items; +} diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 1ac63be4a..5e8428256 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -1,15 +1,12 @@ import { Metrics } from '@/metrics/Metrics.js'; +import { BucketStorageFactory, SyncBucketDataBatch } from '@/storage/BucketStorage.js'; +import { MongoBucketStorage } from '@/storage/MongoBucketStorage.js'; import { SourceTable } from '@/storage/SourceTable.js'; +import { PowerSyncMongo } from '@/storage/mongo/db.js'; import { SyncBucketData } from '@/util/protocol-types.js'; import { hashData } from '@/util/utils.js'; -import * as pgwire from '@powersync/service-jpgwire'; -import { normalizeConnection } from '@powersync/service-types'; import * as bson from 'bson'; import * as mongo from 'mongodb'; -import { BucketStorageFactory, SyncBucketDataBatch } from '../../src/storage/BucketStorage.js'; -import { MongoBucketStorage } from '../../src/storage/MongoBucketStorage.js'; -import { PowerSyncMongo } from '../../src/storage/mongo/db.js'; -import { escapeIdentifier } from '../../src/util/pgwire_utils.js'; import { env } from './env.js'; // The metrics need to be initialised before they can be used @@ -20,8 +17,6 @@ await Metrics.initialise({ }); Metrics.getInstance().resetCounters(); -export const TEST_URI = env.PG_TEST_URL; - export type StorageFactory = () => Promise; export const MONGO_STORAGE_FACTORY: StorageFactory = async () => { @@ -30,47 +25,6 @@ export const MONGO_STORAGE_FACTORY: StorageFactory = async () => { return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); }; -export async function clearTestDb(db: pgwire.PgClient) { - await db.query( - "select pg_drop_replication_slot(slot_name) from pg_replication_slots where active = false and slot_name like 'test_%'" - ); - - await db.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`); - try { - await db.query(`DROP PUBLICATION powersync`); - } catch (e) { - // Ignore - } - - await db.query(`CREATE PUBLICATION powersync FOR ALL TABLES`); - - const tableRows = pgwire.pgwireRows( - await db.query(`SELECT table_name FROM information_schema.tables where table_schema = 'public'`) - ); - for (let row of tableRows) { - const name = row.table_name; - if (name.startsWith('test_')) { - await db.query(`DROP TABLE public.${escapeIdentifier(name)}`); - } - } -} - -export const TEST_CONNECTION_OPTIONS = normalizeConnection({ - type: 'postgresql', - uri: TEST_URI, - sslmode: 'disable' -}); - -export async function connectPgWire(type?: 'replication' | 'standard') { - const db = await pgwire.connectPgWire(TEST_CONNECTION_OPTIONS, { type }); - return db; -} - -export function connectPgPool() { - const db = pgwire.connectPgWirePool(TEST_CONNECTION_OPTIONS); - return db; -} - export async function connectMongo() { // Short timeout for tests, to fail fast when the server is not available. // Slightly longer timeouts for CI, to avoid arbitrary test failures @@ -92,7 +46,7 @@ export function makeTestTable(name: string, columns?: string[] | undefined) { relId, SourceTable.DEFAULT_SCHEMA, name, - (columns ?? ['id']).map((column) => ({ name: column, typeOid: 25 })), + (columns ?? ['id']).map((column) => ({ name: column, type: 'VARCHAR', typeOid: 25 })), true ); } diff --git a/packages/service-core/test/src/validation.test.ts b/packages/service-core/test/src/validation.test.ts index e9f914a12..6c404f2c1 100644 --- a/packages/service-core/test/src/validation.test.ts +++ b/packages/service-core/test/src/validation.test.ts @@ -1,7 +1,7 @@ +import { WalConnection } from '@/replication/WalConnection.js'; import { expect, test } from 'vitest'; import { MONGO_STORAGE_FACTORY } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; -import { WalConnection } from '../../src/replication/WalConnection.js'; // Not quite a walStreamTest, but it helps to manage the connection test( diff --git a/packages/service-core/test/tsconfig.json b/packages/service-core/test/tsconfig.json index 4e77a4239..3f02f71a0 100644 --- a/packages/service-core/test/tsconfig.json +++ b/packages/service-core/test/tsconfig.json @@ -2,7 +2,6 @@ "extends": "../../../tsconfig.base.json", "compilerOptions": { "rootDir": "src", - "noEmit": true, "baseUrl": "./", "esModuleInterop": true, "skipLibCheck": true, From 3d1395c2ed4d603bdee52cebf4af4864a9313df0 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 11:07:30 +0200 Subject: [PATCH 065/298] move validation tests --- .../module-postgres}/test/src/validation.test.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {packages/service-core => modules/module-postgres}/test/src/validation.test.ts (100%) diff --git a/packages/service-core/test/src/validation.test.ts b/modules/module-postgres/test/src/validation.test.ts similarity index 100% rename from packages/service-core/test/src/validation.test.ts rename to modules/module-postgres/test/src/validation.test.ts From 92b243367f65d9521ded302973423b25e8d35b90 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 11:07:40 +0200 Subject: [PATCH 066/298] wip: more test changes --- modules/module-postgres/test/src/validation.test.ts | 2 +- packages/service-core/test/src/sync.test.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/module-postgres/test/src/validation.test.ts b/modules/module-postgres/test/src/validation.test.ts index 6c404f2c1..2536b141e 100644 --- a/modules/module-postgres/test/src/validation.test.ts +++ b/modules/module-postgres/test/src/validation.test.ts @@ -1,6 +1,6 @@ import { WalConnection } from '@/replication/WalConnection.js'; +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; import { expect, test } from 'vitest'; -import { MONGO_STORAGE_FACTORY } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; // Not quite a walStreamTest, but it helps to manage the connection diff --git a/packages/service-core/test/src/sync.test.ts b/packages/service-core/test/src/sync.test.ts index bd66ea357..34ae2af0c 100644 --- a/packages/service-core/test/src/sync.test.ts +++ b/packages/service-core/test/src/sync.test.ts @@ -1,14 +1,19 @@ +import { ZERO_LSN } from '@/storage/storage-index.js'; import { RequestTracker } from '@/sync/RequestTracker.js'; +import { streamResponse } from '@/sync/sync.js'; import { StreamingSyncLine } from '@/util/protocol-types.js'; -import { lsnMakeComparable } from '@powersync/service-jpgwire'; import { JSONBig } from '@powersync/service-jsonbig'; import { RequestParameters } from '@powersync/service-sync-rules'; import * as timers from 'timers/promises'; import { describe, expect, test } from 'vitest'; -import { ZERO_LSN } from '../../src/replication/WalStream.js'; -import { streamResponse } from '../../src/sync/sync.js'; import { makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory } from './util.js'; +// FIXME: This should come from somewhere else +export function lsnMakeComparable(text: string) { + const [h, l] = text.split('/'); + return h.padStart(8, '0') + '/' + l.padStart(8, '0'); +} + describe('sync - mongodb', function () { defineTests(MONGO_STORAGE_FACTORY); }); From 3669475be978a1b3a1e026afe22bc04bbd7a9813 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 11:08:29 +0200 Subject: [PATCH 067/298] move slow tests --- .../module-postgres}/test/src/slow_tests.test.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {packages/service-core => modules/module-postgres}/test/src/slow_tests.test.ts (100%) diff --git a/packages/service-core/test/src/slow_tests.test.ts b/modules/module-postgres/test/src/slow_tests.test.ts similarity index 100% rename from packages/service-core/test/src/slow_tests.test.ts rename to modules/module-postgres/test/src/slow_tests.test.ts From 6e7b24b60e102b7ffbe8d4666e4e284364f6bc66 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 11:14:44 +0200 Subject: [PATCH 068/298] test cleanup --- modules/module-postgres/test/src/env.ts | 1 - modules/module-postgres/test/src/setup.ts | 2 +- packages/service-core/test/src/env.ts | 3 +-- packages/service-core/test/src/setup.ts | 2 +- packages/service-core/test/tsconfig.json | 1 + 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/module-postgres/test/src/env.ts b/modules/module-postgres/test/src/env.ts index 4e9e1694a..fa8f76ca1 100644 --- a/modules/module-postgres/test/src/env.ts +++ b/modules/module-postgres/test/src/env.ts @@ -1,7 +1,6 @@ import { utils } from '@powersync/lib-services-framework'; export const env = utils.collectEnvironmentVariables({ - MONGO_TEST_URL: utils.type.string.default('mongodb://localhost:27017/powersync_test'), PG_TEST_URL: utils.type.string.default('postgres://postgres:postgres@localhost:5432/powersync_test'), CI: utils.type.boolean.default('false'), SLOW_TESTS: utils.type.boolean.default('false') diff --git a/modules/module-postgres/test/src/setup.ts b/modules/module-postgres/test/src/setup.ts index 8e4ece4c9..b924cf736 100644 --- a/modules/module-postgres/test/src/setup.ts +++ b/modules/module-postgres/test/src/setup.ts @@ -2,6 +2,6 @@ import { container } from '@powersync/lib-services-framework'; import { beforeAll } from 'vitest'; beforeAll(() => { - // Your setup code here + // Executes for every test file container.registerDefaults(); }); diff --git a/packages/service-core/test/src/env.ts b/packages/service-core/test/src/env.ts index af0ef2eef..1c86eae37 100644 --- a/packages/service-core/test/src/env.ts +++ b/packages/service-core/test/src/env.ts @@ -2,6 +2,5 @@ import { utils } from '@powersync/lib-services-framework'; export const env = utils.collectEnvironmentVariables({ MONGO_TEST_URL: utils.type.string.default('mongodb://localhost:27017/powersync_test'), - CI: utils.type.boolean.default('false'), - SLOW_TESTS: utils.type.boolean.default('false') + CI: utils.type.boolean.default('false') }); diff --git a/packages/service-core/test/src/setup.ts b/packages/service-core/test/src/setup.ts index 8e4ece4c9..b924cf736 100644 --- a/packages/service-core/test/src/setup.ts +++ b/packages/service-core/test/src/setup.ts @@ -2,6 +2,6 @@ import { container } from '@powersync/lib-services-framework'; import { beforeAll } from 'vitest'; beforeAll(() => { - // Your setup code here + // Executes for every test file container.registerDefaults(); }); diff --git a/packages/service-core/test/tsconfig.json b/packages/service-core/test/tsconfig.json index 3f02f71a0..124a1cbe5 100644 --- a/packages/service-core/test/tsconfig.json +++ b/packages/service-core/test/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "rootDir": "src", "baseUrl": "./", + "outDir": "dist", "esModuleInterop": true, "skipLibCheck": true, "sourceMap": true, From 57e88f7daedea70d4974c13830559aa97ece84ef Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 11:45:47 +0200 Subject: [PATCH 069/298] fix bugs for running routes --- .../src/module/PostgresModule.ts | 5 ++-- .../src/replication/core/ReplicationModule.ts | 5 +++- .../src/storage/mongo/MongoStorageProvider.ts | 2 +- packages/service-core/src/util/utils.ts | 2 +- packages/types/src/config/PowerSyncConfig.ts | 26 ++++++++++--------- service/src/runners/server.ts | 1 - service/tsconfig.json | 9 ++++--- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 84a1869f8..47f51cf4b 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -26,6 +26,7 @@ export class PostgresModule extends replication.ReplicationModule { async register(context: system.ServiceContext): Promise {} async initialize(context: system.ServiceContext): Promise { + await super.initialize(context); // Record replicated bytes using global jpgwire metrics. jpgwire.setMetricsRecorder({ addBytesRead(bytes) { @@ -42,7 +43,7 @@ export class PostgresModule extends replication.ReplicationModule { try { return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); } catch (ex) { - logger.warn('Failed to decode configuration in Postgres module initialization.'); + logger.warn('Failed to decode configuration in Postgres module initialization.', ex); } }) .filter((c) => !!c) @@ -57,7 +58,7 @@ export class PostgresModule extends replication.ReplicationModule { } protected createSyncAPIAdapter(config: types.PostgresConnectionConfig): api.RouteAPI { - throw new PostgresSyncAPIAdapter(this.resolveConfig(config)); + return new PostgresSyncAPIAdapter(this.resolveConfig(config)); } protected createReplicationAdapter(config: types.PostgresConnectionConfig): PostgresReplicationAdapter { diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 4b4d11219..4e9f5fe45 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -2,6 +2,7 @@ import { logger, schema } from '@powersync/lib-services-framework'; import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSyncConfig.js'; import * as t from 'ts-codec'; +import * as types from '@powersync/service-types'; import * as api from '../../api/api-index.js'; import * as modules from '../../modules/modules-index.js'; import * as system from '../../system/system-index.js'; @@ -81,7 +82,9 @@ export abstract class ReplicationModule extends modules.AbstractModule { private validateConfig(config: DataSourceConfig): void { const validator = schema - .parseJSONSchema(t.generateJSONSchema(this.configSchema(), { allowAdditional: true })) + .parseJSONSchema( + t.generateJSONSchema(this.configSchema(), { allowAdditional: true, parsers: [types.configFile.portParser] }) + ) .validator(); const valid = validator.validate(config); diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts index 5ba9ebad8..81a237126 100644 --- a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -9,7 +9,7 @@ import { PowerSyncMongo } from './db.js'; export type MongoStorageConfig = configFile.StorageConfig & BaseStorageConfig; export class MongoStorageProvider implements StorageProvider { get type() { - return 'mongo'; + return 'mongodb'; } async generate(config: MongoStorageConfig): Promise { diff --git a/packages/service-core/src/util/utils.ts b/packages/service-core/src/util/utils.ts index a555a6d33..18ea87e08 100644 --- a/packages/service-core/src/util/utils.ts +++ b/packages/service-core/src/util/utils.ts @@ -1,5 +1,5 @@ import * as sync_rules from '@powersync/service-sync-rules'; -import bson from 'bson'; +import * as bson from 'bson'; import crypto from 'crypto'; import * as uuid from 'uuid'; import { BucketChecksum, OpId } from './protocol-types.js'; diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index f1c1292c5..87607842c 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -19,18 +19,20 @@ export const portParser = { }) }; -export const dataSourceConfig = t.object({ - // Unique string identifier for the data source - type: t.string, - /** Unique identifier for the connection - optional when a single connection is present. */ - id: t.string.optional(), - /** Additional meta tag for connection */ - tag: t.string.optional(), - /** - * Allows for debug query execution - */ - debug_enabled: t.boolean.optional() -}); +export const dataSourceConfig = t + .object({ + // Unique string identifier for the data source + type: t.string, + /** Unique identifier for the connection - optional when a single connection is present. */ + id: t.string.optional(), + /** Additional meta tag for connection */ + tag: t.string.optional(), + /** + * Allows for debug query execution + */ + debug_enabled: t.boolean.optional() + }) + .and(t.record(t.any)); // This essentially allows any extra fields on this type export type DataSourceConfig = t.Decoded; diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 1e0ef05d2..ec6b0724b 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -14,7 +14,6 @@ export async function startServer(serviceContext: core.system.ServiceContext) { logger.info('Starting service'); - // TODO cleanup the initialization of metrics await serviceContext.start(); serviceContext.metrics.configureApiMetrics(); diff --git a/service/tsconfig.json b/service/tsconfig.json index 22d065e42..363c72bdb 100644 --- a/service/tsconfig.json +++ b/service/tsconfig.json @@ -21,14 +21,17 @@ { "path": "../packages/service-core" }, - { - "path": "../libs/lib-services" - }, { "path": "../packages/sync-rules" }, { "path": "../packages/types" + }, + { + "path": "../libs/lib-services" + }, + { + "path": "../modules/module-postgres" } ] } From 26a7882115da00651a96a19e6ef1e14d7544e89b Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 13:10:27 +0200 Subject: [PATCH 070/298] update supabase key collector to only register if configured --- libs/lib-services/package.json | 2 + libs/lib-services/src/utils/BaseObserver.ts | 29 ++++++++++ libs/lib-services/src/utils/utils-index.ts | 1 + .../src/module/PostgresModule.ts | 55 +++++++++++-------- packages/service-core/src/routes/auth.ts | 7 +-- .../src/routes/configure-rsocket.ts | 2 +- .../util/config/compound-config-collector.ts | 30 ++++++++-- pnpm-lock.yaml | 6 ++ 8 files changed, 99 insertions(+), 33 deletions(-) create mode 100644 libs/lib-services/src/utils/BaseObserver.ts diff --git a/libs/lib-services/package.json b/libs/lib-services/package.json index 77e175aa3..3797df49f 100644 --- a/libs/lib-services/package.json +++ b/libs/lib-services/package.json @@ -26,11 +26,13 @@ "dotenv": "^16.4.5", "lodash": "^4.17.21", "ts-codec": "^1.2.2", + "uuid": "^9.0.1", "winston": "^3.13.0", "zod": "^3.23.8" }, "devDependencies": { "@types/lodash": "^4.17.5", + "@types/uuid": "^9.0.4", "vitest": "^0.34.6" } } diff --git a/libs/lib-services/src/utils/BaseObserver.ts b/libs/lib-services/src/utils/BaseObserver.ts new file mode 100644 index 000000000..4c5b689ce --- /dev/null +++ b/libs/lib-services/src/utils/BaseObserver.ts @@ -0,0 +1,29 @@ +import { v4 as uuid } from 'uuid'; + +export class BaseObserver { + protected listeners: { [id: string]: Partial }; + + constructor() { + this.listeners = {}; + } + + registerListener(listener: Partial): () => void { + const id = uuid(); + this.listeners[id] = listener; + return () => { + delete this.listeners[id]; + }; + } + + iterateListeners(cb: (listener: Partial) => any) { + for (let i in this.listeners) { + cb(this.listeners[i]); + } + } + + async iterateAsyncListeners(cb: (listener: Partial) => Promise) { + for (let i in this.listeners) { + await cb(this.listeners[i]); + } + } +} diff --git a/libs/lib-services/src/utils/utils-index.ts b/libs/lib-services/src/utils/utils-index.ts index 17384042b..ee42d4057 100644 --- a/libs/lib-services/src/utils/utils-index.ts +++ b/libs/lib-services/src/utils/utils-index.ts @@ -1 +1,2 @@ +export * from './BaseObserver.js'; export * from './environment-variables.js'; diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 47f51cf4b..ba3aef913 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -23,7 +23,38 @@ export class PostgresModule extends replication.ReplicationModule { return types.PostgresConnectionConfig; } - async register(context: system.ServiceContext): Promise {} + async register(context: system.ServiceContext): Promise { + // Add the Supabase Ket collector to the resolved config whenever it is collected + context.configCollector.registerListener({ + configCollected: async (event) => { + if (!event.baseConfig.client_auth?.supabase) { + return; + } + + // Register the Supabase key collector(s) + event.resolvedConfig.connections + ?.map((baseConfig) => { + if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { + return; + } + try { + return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); + } catch (ex) { + logger.warn('Failed to decode configuration in Postgres module initialization.', ex); + } + }) + .filter((c) => !!c) + .forEach((config) => { + const keyCollector = new SupabaseKeyCollector(config!); + context.withLifecycle(keyCollector, { + // Close the internal pool + stop: (collector) => collector.shutdown() + }); + event.resolvedConfig.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); + }); + } + }); + } async initialize(context: system.ServiceContext): Promise { await super.initialize(context); @@ -33,28 +64,6 @@ export class PostgresModule extends replication.ReplicationModule { context.metrics.data_replicated_bytes.add(bytes); } }); - - // Register the Supabase key collector(s) - context.configuration.connections - ?.map((baseConfig) => { - if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { - return; - } - try { - return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); - } catch (ex) { - logger.warn('Failed to decode configuration in Postgres module initialization.', ex); - } - }) - .filter((c) => !!c) - .forEach((config) => { - const keyCollector = new SupabaseKeyCollector(config!); - context.withLifecycle(keyCollector, { - // Close the internal pool - stop: (collector) => collector.shutdown() - }); - context.configuration.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); - }); } protected createSyncAPIAdapter(config: types.PostgresConnectionConfig): api.RouteAPI { diff --git a/packages/service-core/src/routes/auth.ts b/packages/service-core/src/routes/auth.ts index 30de07d6f..f6a384b65 100644 --- a/packages/service-core/src/routes/auth.ts +++ b/packages/service-core/src/routes/auth.ts @@ -1,6 +1,5 @@ import * as jose from 'jose'; -import { container } from '@powersync/lib-services-framework'; import * as auth from '../auth/auth-index.js'; import { ServiceContext } from '../system/ServiceContext.js'; import * as util from '../util/util-index.js'; @@ -109,7 +108,7 @@ export async function authorizeUser(context: Context, authHeader: string = '') { }; } - const { context: tokenContext, errors } = await generateContext(token); + const { context: tokenContext, errors } = await generateContext(context.service_context, token); if (!tokenContext) { return { @@ -122,8 +121,8 @@ export async function authorizeUser(context: Context, authHeader: string = '') { return { authorized: true }; } -export async function generateContext(token: string) { - const { configuration } = container.getImplementation(ServiceContext); +export async function generateContext(serviceContext: ServiceContext, token: string) { + const { configuration } = serviceContext; let tokenPayload: auth.JwtPayload; try { diff --git a/packages/service-core/src/routes/configure-rsocket.ts b/packages/service-core/src/routes/configure-rsocket.ts index 3ed65cd48..67a4e9ad0 100644 --- a/packages/service-core/src/routes/configure-rsocket.ts +++ b/packages/service-core/src/routes/configure-rsocket.ts @@ -32,7 +32,7 @@ export function configureRSocket(router: ReactiveSocketRouter, options: try { const extracted_token = getTokenFromHeader(token); if (extracted_token != null) { - const { context, errors: token_errors } = await generateContext(extracted_token); + const { context, errors: token_errors } = await generateContext(options.service_context, extracted_token); if (context?.token_payload == null) { throw new errors.AuthorizationError(token_errors ?? 'Authentication required'); } diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index e01918a0d..24ba39a0d 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -1,4 +1,4 @@ -import { logger } from '@powersync/lib-services-framework'; +import { logger, utils } from '@powersync/lib-services-framework'; import { configFile } from '@powersync/service-types'; import * as auth from '../../auth/auth-index.js'; import { ConfigCollector } from './collectors/config-collector.js'; @@ -11,8 +11,6 @@ import { InlineSyncRulesCollector } from './sync-rules/impl/inline-sync-rules-co import { SyncRulesCollector } from './sync-rules/sync-collector.js'; import { ResolvedPowerSyncConfig, RunnerConfig, SyncRulesConfig } from './types.js'; -const POWERSYNC_DEV_KID = 'powersync-dev'; - export type CompoundConfigCollectorOptions = { /** * Collectors for PowerSync configuration content. @@ -28,6 +26,17 @@ export type CompoundConfigCollectorOptions = { syncRulesCollectors: SyncRulesCollector[]; }; +export type ConfigCollectedEvent = { + baseConfig: configFile.PowerSyncConfig; + resolvedConfig: ResolvedPowerSyncConfig; +}; + +export type ConfigCollectorListener = { + configCollected?: (event: ConfigCollectedEvent) => Promise; +}; + +const POWERSYNC_DEV_KID = 'powersync-dev'; + const DEFAULT_COLLECTOR_OPTIONS: CompoundConfigCollectorOptions = { configCollectors: [new Base64ConfigCollector(), new FileSystemConfigCollector(), new FallbackConfigCollector()], syncRulesCollectors: [ @@ -37,8 +46,10 @@ const DEFAULT_COLLECTOR_OPTIONS: CompoundConfigCollectorOptions = { ] }; -export class CompoundConfigCollector { - constructor(protected options: CompoundConfigCollectorOptions = DEFAULT_COLLECTOR_OPTIONS) {} +export class CompoundConfigCollector extends utils.BaseObserver { + constructor(protected options: CompoundConfigCollectorOptions = DEFAULT_COLLECTOR_OPTIONS) { + super(); + } /** * Collects and resolves base config @@ -114,6 +125,15 @@ export class CompoundConfigCollector { // slot_name_prefix: connections[0]?.slot_name_prefix ?? 'powersync_' slot_name_prefix: 'powersync_' }; + + // Allow listeners to add to the config + await this.iterateAsyncListeners(async (l) => + l.configCollected?.({ + baseConfig, + resolvedConfig: config + }) + ); + return config; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a732e4ee9..bd0ec2222 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: ts-codec: specifier: ^1.2.2 version: 1.2.2 + uuid: + specifier: ^9.0.1 + version: 9.0.1 winston: specifier: ^3.13.0 version: 3.13.1 @@ -84,6 +87,9 @@ importers: '@types/lodash': specifier: ^4.17.5 version: 4.17.6 + '@types/uuid': + specifier: ^9.0.4 + version: 9.0.8 vitest: specifier: ^0.34.6 version: 0.34.6 From 391d8dc145cc2c50c91cd0e2caa4288dd2a14806 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 13:22:56 +0200 Subject: [PATCH 071/298] minor cleanup --- packages/service-core/test/src/checksum_cache.test.ts | 6 +++--- packages/service-core/test/src/sync.test.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/service-core/test/src/checksum_cache.test.ts b/packages/service-core/test/src/checksum_cache.test.ts index 2ce56fd19..2aebc7691 100644 --- a/packages/service-core/test/src/checksum_cache.test.ts +++ b/packages/service-core/test/src/checksum_cache.test.ts @@ -1,8 +1,8 @@ -import { describe, expect, it } from 'vitest'; +import { ChecksumCache, FetchChecksums, FetchPartialBucketChecksum } from '@/storage/ChecksumCache.js'; import { BucketChecksum, OpId } from '@/util/protocol-types.js'; -import * as crypto from 'node:crypto'; import { addBucketChecksums } from '@/util/util-index.js'; -import { ChecksumCache, FetchChecksums, FetchPartialBucketChecksum } from '@/storage/ChecksumCache.js'; +import * as crypto from 'node:crypto'; +import { describe, expect, it } from 'vitest'; /** * Create a deterministic BucketChecksum based on the bucket name and checkpoint for testing purposes. diff --git a/packages/service-core/test/src/sync.test.ts b/packages/service-core/test/src/sync.test.ts index 34ae2af0c..496837e1e 100644 --- a/packages/service-core/test/src/sync.test.ts +++ b/packages/service-core/test/src/sync.test.ts @@ -1,3 +1,4 @@ +// FIXME: This needs to come from a better place import { ZERO_LSN } from '@/storage/storage-index.js'; import { RequestTracker } from '@/sync/RequestTracker.js'; import { streamResponse } from '@/sync/sync.js'; From fb43aaadde23a20ee9ceeeed3d4ac8e813584ea9 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 13:27:32 +0200 Subject: [PATCH 072/298] update dockerfile and temporarily remove tests --- modules/module-postgres/package.json | 2 -- service/Dockerfile | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index 3346ddfd5..1fefad9f7 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -11,8 +11,6 @@ "type": "module", "scripts": { "build": "tsc -b", - "build:tests": "tsc -b test/tsconfig.json", - "test": "vitest --no-threads", "clean": "rm -rf ./lib && tsc -b --clean" }, "dependencies": { diff --git a/service/Dockerfile b/service/Dockerfile index 7351662af..369abcc59 100644 --- a/service/Dockerfile +++ b/service/Dockerfile @@ -17,6 +17,8 @@ COPY packages/types/package.json packages/types/tsconfig.json packages/types/ COPY libs/lib-services/package.json libs/lib-services/tsconfig.json libs/lib-services/ +COPY modules/module-postgres/package.json modules/module-postgres/tsconfig.json modules/module-postgres/ + RUN pnpm install --frozen-lockfile COPY service/src service/src/ @@ -31,6 +33,8 @@ COPY packages/types/src packages/types/src/ COPY libs/lib-services/src libs/lib-services/src/ +COPY modules/module-postgres/src modules/module-postgres/src/ + RUN pnpm build:production && \ rm -rf node_modules **/node_modules && \ pnpm install --frozen-lockfile --prod --ignore-scripts From 6842aecae4187c451d8705aebd9e7335b8c9bb93 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 13:38:19 +0200 Subject: [PATCH 073/298] split tests --- .github/workflows/test.yml | 57 ++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c2b757c2..0c02dd753 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,14 +30,59 @@ jobs: platforms: linux/amd64 push: false file: ./service/Dockerfile - # TODO remove this when removing Journey Micro - build-args: | - GITHUB_TOKEN=${{secrets.RESTRICTED_PACKAGES_TOKEN}} - run-tests: - name: Test + run-core-tests: + name: Core Test runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Start MongoDB + uses: supercharge/mongodb-github-action@1.8.0 + with: + mongodb-version: '6.0' + mongodb-replica-set: test-rs + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + version: 9 + run_install: false + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install + + - name: Build + shell: bash + run: pnpm build + + - name: Test + run: pnpm test --filter !./modules + + run-postgres-tests: + name: Postgres Test + runs-on: ubuntu-latest + needs: run-core-tests + strategy: fail-fast: false matrix: @@ -97,4 +142,4 @@ jobs: run: pnpm build - name: Test - run: pnpm test + run: pnpm test --filter ./modules/module-postgres From f2df73966b9c6d7e6d2be82eec99a4315210a7f1 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 13:42:38 +0200 Subject: [PATCH 074/298] fix tests --- modules/module-postgres/src/replication/PgRelation.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/module-postgres/src/replication/PgRelation.ts b/modules/module-postgres/src/replication/PgRelation.ts index 6dd95547f..f2dfdc17a 100644 --- a/modules/module-postgres/src/replication/PgRelation.ts +++ b/modules/module-postgres/src/replication/PgRelation.ts @@ -1,9 +1,9 @@ +import { storage } from '@powersync/service-core'; import { PgoutputRelation } from '@powersync/service-jpgwire'; -import { ColumnDescriptor, SourceEntityDescriptor } from '../storage/SourceEntity.js'; export type ReplicationIdentity = 'default' | 'nothing' | 'full' | 'index'; -export function getReplicaIdColumns(relation: PgoutputRelation): ColumnDescriptor[] { +export function getReplicaIdColumns(relation: PgoutputRelation): storage.ColumnDescriptor[] { if (relation.replicaIdentity == 'nothing') { return []; } else { @@ -21,7 +21,7 @@ export function getRelId(source: PgoutputRelation): number { return relId; } -export function getPgOutputRelation(source: PgoutputRelation): SourceEntityDescriptor { +export function getPgOutputRelation(source: PgoutputRelation): storage.SourceEntityDescriptor { return { name: source.name, schema: source.schema, From da07f2b584f4b2005e01ed6ef981ab91f2b961a7 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 17:24:30 +0200 Subject: [PATCH 075/298] cleanup socket routes --- modules/module-postgres/test/src/validation.test.ts | 2 +- packages/service-core/src/routes/RouterEngine.ts | 8 ++++---- packages/service-core/src/routes/configure-rsocket.ts | 6 +++--- service/src/runners/server.ts | 6 +++++- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/module-postgres/test/src/validation.test.ts b/modules/module-postgres/test/src/validation.test.ts index 2536b141e..e55c207a8 100644 --- a/modules/module-postgres/test/src/validation.test.ts +++ b/modules/module-postgres/test/src/validation.test.ts @@ -1,5 +1,5 @@ -import { WalConnection } from '@/replication/WalConnection.js'; import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; +import { WalConnection } from '@module/replication/WalConnection.js'; import { expect, test } from 'vitest'; import { walStreamTest } from './wal_stream_utils.js'; diff --git a/packages/service-core/src/routes/RouterEngine.ts b/packages/service-core/src/routes/RouterEngine.ts index 231955c14..9fe06b346 100644 --- a/packages/service-core/src/routes/RouterEngine.ts +++ b/packages/service-core/src/routes/RouterEngine.ts @@ -4,8 +4,10 @@ import * as api from '../api/api-index.js'; import { ADMIN_ROUTES } from './endpoints/admin.js'; import { CHECKPOINT_ROUTES } from './endpoints/checkpointing.js'; +import { syncStreamReactive } from './endpoints/socket-route.js'; import { SYNC_RULES_ROUTES } from './endpoints/sync-rules.js'; import { SYNC_STREAM_ROUTES } from './endpoints/sync-stream.js'; +import { SocketRouteGenerator } from './router-socket.js'; import { RouteDefinition } from './router.js'; export type RouterSetupResponse = { @@ -15,7 +17,7 @@ export type RouterSetupResponse = { export type RouterEngineRoutes = { api_routes: RouteDefinition[]; stream_routes: RouteDefinition[]; - socket_routes: RouteDefinition[]; + socket_routes: SocketRouteGenerator[]; }; export type RouterSetup = (routes: RouterEngineRoutes) => Promise; @@ -47,9 +49,7 @@ export class RouterEngine { this.routes = { api_routes: [...ADMIN_ROUTES, ...CHECKPOINT_ROUTES, ...SYNC_RULES_ROUTES], stream_routes: [...SYNC_STREAM_ROUTES], - socket_routes: [ - // TODO - ] + socket_routes: [syncStreamReactive] }; } diff --git a/packages/service-core/src/routes/configure-rsocket.ts b/packages/service-core/src/routes/configure-rsocket.ts index 67a4e9ad0..b2fd6b332 100644 --- a/packages/service-core/src/routes/configure-rsocket.ts +++ b/packages/service-core/src/routes/configure-rsocket.ts @@ -13,13 +13,13 @@ import { Context } from './router.js'; export type RSockerRouterConfig = { service_context: ServiceContext; server: http.Server; - routeGenerators?: SocketRouteGenerator[]; + route_generators?: SocketRouteGenerator[]; }; export const DEFAULT_SOCKET_ROUTES = [syncStreamReactive]; export function configureRSocket(router: ReactiveSocketRouter, options: RSockerRouterConfig) { - const { routeGenerators = DEFAULT_SOCKET_ROUTES, server, service_context } = options; + const { route_generators = DEFAULT_SOCKET_ROUTES, server, service_context } = options; router.applyWebSocketEndpoints(server, { contextProvider: async (data: Buffer) => { @@ -50,7 +50,7 @@ export function configureRSocket(router: ReactiveSocketRouter, options: throw ex; } }, - endpoints: routeGenerators.map((generator) => generator(router)), + endpoints: route_generators.map((generator) => generator(router)), metaDecoder: async (meta: Buffer) => { return RSocketRequestMeta.decode(deserialize(meta) as any); }, diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index ec6b0724b..d988a3ef1 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -34,7 +34,11 @@ export async function startServer(serviceContext: core.system.ServiceContext) { routes: { api: { routes: routes.api_routes }, sync_stream: { routes: routes.stream_routes } } }); - core.routes.configureRSocket(SocketRouter, { server: server.server, service_context: serviceContext }); + core.routes.configureRSocket(SocketRouter, { + server: server.server, + service_context: serviceContext, + route_generators: routes.socket_routes + }); const { port } = serviceContext.configuration; From 927ccb562df37c3646007e40147b85e8affc5016 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 31 Jul 2024 17:41:38 +0200 Subject: [PATCH 076/298] cleanup --- modules/module-postgres/src/module/PostgresModule.ts | 6 +++--- .../src/util/config/compound-config-collector.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index ba3aef913..5397bd90a 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -27,12 +27,12 @@ export class PostgresModule extends replication.ReplicationModule { // Add the Supabase Ket collector to the resolved config whenever it is collected context.configCollector.registerListener({ configCollected: async (event) => { - if (!event.baseConfig.client_auth?.supabase) { + if (!event.base_config.client_auth?.supabase) { return; } // Register the Supabase key collector(s) - event.resolvedConfig.connections + event.resolved_config.connections ?.map((baseConfig) => { if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { return; @@ -50,7 +50,7 @@ export class PostgresModule extends replication.ReplicationModule { // Close the internal pool stop: (collector) => collector.shutdown() }); - event.resolvedConfig.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); + event.resolved_config.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); }); } }); diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 24ba39a0d..0dc5218a8 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -27,8 +27,8 @@ export type CompoundConfigCollectorOptions = { }; export type ConfigCollectedEvent = { - baseConfig: configFile.PowerSyncConfig; - resolvedConfig: ResolvedPowerSyncConfig; + base_config: configFile.PowerSyncConfig; + resolved_config: ResolvedPowerSyncConfig; }; export type ConfigCollectorListener = { @@ -129,8 +129,8 @@ export class CompoundConfigCollector extends utils.BaseObserver l.configCollected?.({ - baseConfig, - resolvedConfig: config + base_config: baseConfig, + resolved_config: config }) ); From a607226145aa9edcf7a31b1bba9fcbd3e4f2c53e Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 1 Aug 2024 12:15:27 +0200 Subject: [PATCH 077/298] Added ReplicationJob --- .../replication/core/ReplicationAdapter.ts | 19 +- .../src/replication/core/ReplicationJob.ts | 220 ++++++++++++++++++ 2 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 packages/service-core/src/replication/core/ReplicationJob.ts diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index dfb84e3ff..703bd00da 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -1,6 +1,6 @@ import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; import * as storage from '../../storage/storage-index.js'; -import { PgRelation } from '../PgRelation.js'; +import { SourceEntityDescriptor } from '../../storage/SourceEntity.js'; /** * The ReplicationAdapter describes all the methods that are required by the @@ -79,26 +79,27 @@ export enum UpdateType { DELETE = 'DELETE', TRUNCATE = 'TRUNCATE', SCHEMA_CHANGE = 'SCHEMA_CHANGE', - COMMIT = 'COMMIT' + COMMIT = 'COMMIT', + KEEP_ALIVE = 'KEEP_ALIVE' } -type ReplicationUpdate = { +export interface ReplicationUpdate { type: UpdateType; /** - * Descriptor of the data source entity that was updated + * Descriptor of the data source entities that were updated. + * Usually only one entity is updated at a time, but for the truncate operation there could be multiple */ - entity: storage.SourceTable; + entities: storage.SourceTable[]; /** * Present when the update is an insert, update or delete. Contains the changed values for adding to the bucket storage */ entry?: storage.SaveOptions; /** * Present when the update is a schema change. Describes the new data source entity - * TODO: Redefine PgRelation */ - entityDescriptor?: PgRelation; + entityDescriptor?: SourceEntityDescriptor; /** - * Present when the update is a commit. Contains the LSN of the commit + * Present when the update is a commit or a keep alive. */ lsn?: string; -}; +} diff --git a/packages/service-core/src/replication/core/ReplicationJob.ts b/packages/service-core/src/replication/core/ReplicationJob.ts new file mode 100644 index 000000000..cac0829a5 --- /dev/null +++ b/packages/service-core/src/replication/core/ReplicationJob.ts @@ -0,0 +1,220 @@ +import { ReplicationAdapter, ReplicationUpdate, UpdateType } from './ReplicationAdapter.js'; +import { container, logger } from '@powersync/lib-services-framework'; +import { ErrorRateLimiter } from '../ErrorRateLimiter.js'; +import { Metrics } from '../../metrics/Metrics.js'; +import * as storage from '../../storage/storage-index.js'; +import { SourceEntityDescriptor } from '../../storage/SourceEntity.js'; + +export interface ReplicationJobOptions { + adapter: ReplicationAdapter; + storage: storage.SyncRulesBucketStorage; + lock: storage.ReplicationLock; + rateLimiter?: ErrorRateLimiter; +} + +export class ReplicationJob { + private readonly adapter: ReplicationAdapter; + private readonly storage: storage.SyncRulesBucketStorage; + + /** + * TODO: This will need to be properly sourced once we allow more than 1 connection + * @private + */ + private readonly connectionId = 1; + + private abortController = new AbortController(); + private isReplicatingPromise: Promise | undefined; + + constructor(private options: ReplicationJobOptions) { + this.adapter = options.adapter; + this.storage = options.storage; + } + + isStopped(): boolean { + return this.abortController.signal.aborted; + } + + async start() { + try { + this.isReplicatingPromise = this.replicateLoop(); + } catch (e) { + // Fatal exception + container.reporter.captureException(e, { + metadata: { + replicator: this.adapter.name() + // TODO We could allow extra metadata contributed from the adapter here + } + }); + logger.error(`Replication failed on ${this.adapter.name()}`, e); + } finally { + this.abortController.abort(); + } + await this.options.lock.release(); + } + + async replicateLoop() { + while (!this.isStopped) { + await this.replicate(); + + if (!this.isStopped) { + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + } + + async replicate() { + try { + await this.adapter.checkPrerequisites(); + const status = await this.storage.getStatus(); + const sourceEntities = await this.getSourceEntities(); + if (status.snapshot_done && status.checkpoint_lsn) { + await this.storage.clear(); + await this.storage.startBatch({}, async (batch) => { + await this.initializeData(sourceEntities, batch); + }); + } + await this.startReplication(sourceEntities); + } catch (e) { + await this.storage.reportError(e); + throw e; + } + } + + private async getSourceEntities(): Promise { + const resolvedSourceEntities: storage.SourceTable[] = []; + + for (const sourceEntity of this.storage.sync_rules.getSourceTables()) { + const resolved = await this.adapter.resolveReplicationEntities(sourceEntity); + resolvedSourceEntities.push(...resolved); + } + + return resolvedSourceEntities; + } + + async initializeData(sourceEntities: storage.SourceTable[], storageBatch: storage.BucketStorageBatch) { + let at = 0; + let replicatedCount = 0; + let currentEntity: storage.SourceTable | null = null; + let estimatedCount = 0; + + await this.adapter.initializeData({ + entities: sourceEntities, + abortSignal: this.abortController.signal, + entryConsumer: async (batch) => { + if (batch.entity != currentEntity) { + logger.info(`${this.adapter.name()}: Replicating: ${batch.entity.table}`); + currentEntity = batch.entity; + estimatedCount = await this.adapter.count(currentEntity); + } + + if (batch.entries.length > 0 && at - replicatedCount >= 5000) { + logger.info(`${this.adapter.name()}: Replicating: ${currentEntity.table} Progress: ${at}/${estimatedCount}`); + } + + for (const entry of batch.entries) { + await storageBatch.save({ tag: 'insert', sourceTable: currentEntity, before: undefined, after: entry }); + } + at += batch.entries.length; + Metrics.getInstance().rows_replicated_total.add(batch.entries.length); + + if (batch.isLast) { + await storageBatch.markSnapshotDone([batch.entity], batch.fromLSN); + await storageBatch.commit(batch.fromLSN); + at = 0; + replicatedCount = 0; + currentEntity = null; + estimatedCount = 0; + } + + await this.touch(); + } + }); + } + + async startReplication(sourceEntities: storage.SourceTable[]) { + await this.storage.autoActivate(); + + await this.storage.startBatch({}, async (batch) => { + await this.adapter.startReplication({ + entities: sourceEntities, + abortSignal: this.abortController.signal, + changeListener: (update) => this.handeReplicationUpdate(update, batch) + }); + }); + } + + private async handeReplicationUpdate(update: ReplicationUpdate, batch: storage.BucketStorageBatch): Promise { + switch (update.type) { + case UpdateType.INSERT: + case UpdateType.UPDATE: + case UpdateType.DELETE: + await batch.save(update.entry!); + Metrics.getInstance().rows_replicated_total.add(1); + return; + case UpdateType.TRUNCATE: + await batch.truncate(update.entities); + return; + case UpdateType.SCHEMA_CHANGE: + await this.handleSchemaChange(batch, update.entityDescriptor!, update.entities[0]); + break; + case UpdateType.COMMIT: + await batch.commit(update.lsn!); + Metrics.getInstance().transactions_replicated_total.add(1); + return; + case UpdateType.KEEP_ALIVE: + await batch.keepalive(update.lsn!); + return; + } + } + + async stop(): Promise { + logger.info(`Stopping ${this.adapter.name()} replication job: ${this.storage.group_id}`); + // End gracefully + this.abortController.abort(); + await this.isReplicatingPromise; + } + + /** + * Terminate this replication. Cleans up any config for the replication and deletes the replicated data + * + * Stops replication if needed. + */ + async terminate(): Promise { + logger.info(`Terminating ${this.adapter.name()} replication job: ${this.storage.group_id}`); + await this.stop(); + await this.adapter.cleanupReplication(this.storage.group_id); + await this.options.storage.terminate(); + } + + async handleSchemaChange( + storageBatch: storage.BucketStorageBatch, + entityDescriptor: SourceEntityDescriptor, + entity: storage.SourceTable + ): Promise { + if (!entityDescriptor.objectId) { + throw new Error('relationId expected'); + } + const result = await this.storage.resolveTable({ + group_id: this.storage.group_id, + connection_id: this.connectionId, + connection_tag: entity.connectionTag, + entity_descriptor: entityDescriptor, + sync_rules: this.storage.sync_rules + }); + + // Drop conflicting tables. This includes for example renamed tables. + await storageBatch.drop(result.dropTables); + + // Truncate this table, in case a previous snapshot was interrupted. + await storageBatch.truncate([result.table]); + + await this.initializeData([result.table], storageBatch); + } + + async touch(): Promise { + // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. + // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, + // or reduce PING_INTERVAL here. + return container.probes.touch(); + } +} From c7ac08efe5d1a27f9892fff075ed1a4290a6030e Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 1 Aug 2024 12:30:29 +0200 Subject: [PATCH 078/298] Cleaned up Replicator and ReplicationJob --- .../src/replication/core/ReplicationJob.ts | 1 + .../src/replication/core/Replicator.ts | 150 +++++++++++++++++- 2 files changed, 143 insertions(+), 8 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationJob.ts b/packages/service-core/src/replication/core/ReplicationJob.ts index cac0829a5..ea3a20a4e 100644 --- a/packages/service-core/src/replication/core/ReplicationJob.ts +++ b/packages/service-core/src/replication/core/ReplicationJob.ts @@ -178,6 +178,7 @@ export class ReplicationJob { * Terminate this replication. Cleans up any config for the replication and deletes the replicated data * * Stops replication if needed. + * TODO: Confirm if this is still needed at all. */ async terminate(): Promise { logger.info(`Terminating ${this.adapter.name()} replication job: ${this.storage.group_id}`); diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts index 047f9b688..94bf26e6e 100644 --- a/packages/service-core/src/replication/core/Replicator.ts +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -1,30 +1,164 @@ +import * as storage from '../../storage/storage-index.js'; + import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +import { container, logger } from '@powersync/lib-services-framework'; +import { SyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; +import { ReplicationJob } from './ReplicationJob.js'; export interface ReplicatorOptions { - adapter: ReplicationAdapter; + adapter: ReplicationAdapter; storage: BucketStorageFactory; + sync_rule_provider: SyncRulesProvider; } /** * A replicator manages the mechanics for replicating data from a data source to a storage bucket. - * This includes copying across the original data set and then keeping it in sync with the data source. - * TODO: Implement this. This will replace the current WallStreamManager + * This includes copying across the original data set and then keeping it in sync with the data source using ReplicationJobs. + * It also handles any changes to the sync rules. */ export class Replicator { - private readonly adapter: ReplicationAdapter; - private storage: BucketStorageFactory; + private readonly adapter: ReplicationAdapter; + private readonly storage: BucketStorageFactory; + + private replicationJobs = new Map(); + private stopped = false; - constructor(options: ReplicatorOptions) { + constructor(private options: ReplicatorOptions) { this.adapter = options.adapter; this.storage = options.storage; } public async start(): Promise { - // start the replication + this.runLoop().catch((e) => { + logger.error(`Data source fatal replication error: ${this.adapter.name()}`, e); + container.reporter.captureException(e); + setTimeout(() => { + process.exit(1); + }, 1000); + }); } public async stop(): Promise { - // stop the replication + this.stopped = true; + let promises: Promise[] = []; + for (const job of this.replicationJobs.values()) { + promises.push(job.stop()); + } + await Promise.all(promises); + } + + private async runLoop() { + const syncRules = await this.options.sync_rule_provider.get(); + let configuredLock: storage.ReplicationLock | undefined = undefined; + if (syncRules != null) { + logger.info('Loaded sync rules'); + try { + // Configure new sync rules, if they have changed. + // In that case, also immediately take out a lock, so that another process doesn't start replication on it. + const { lock } = await this.storage.configureSyncRules(syncRules, { + lock: true + }); + if (lock) { + configuredLock = lock; + } + } catch (e) { + // Log, but continue with previous sync rules + logger.error(`Failed to update sync rules from configuration`, e); + } + } else { + logger.info('No sync rules configured - configure via API'); + } + while (!this.stopped) { + await container.probes.touch(); + try { + await this.refresh({ configured_lock: configuredLock }); + // The lock is only valid on the first refresh. + configuredLock = undefined; + } catch (e) { + logger.error(`Failed to refresh replication jobs: ${this.adapter.name()}`, e); + } + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + + private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { + if (this.stopped) { + return; + } + + let configuredLock = options?.configured_lock; + + const existingJobs = new Map(this.replicationJobs.entries()); + const replicatingSyncRules = await this.storage.getReplicatingSyncRules(); + const newJobs = new Map(); + for (let syncRules of replicatingSyncRules) { + const existingJob = existingJobs.get(syncRules.id); + if (existingJob && !existingJob.isStopped()) { + // No change + existingJobs.delete(syncRules.id); + newJobs.set(syncRules.id, existingJob); + } else if (existingJob && existingJob.isStopped()) { + // Stopped (e.g. fatal error). + // Remove from the list. Next refresh call will restart the job. + existingJobs.delete(syncRules.id); + } else { + // New sync rules were found (or resume after restart) + try { + let lock: storage.ReplicationLock; + if (configuredLock?.sync_rules_id == syncRules.id) { + lock = configuredLock; + } else { + lock = await syncRules.lock(); + } + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + const newJob = new ReplicationJob({ + adapter: this.adapter, + storage: storage, + lock: lock + }); + newJobs.set(syncRules.id, newJob); + newJob.start(); + } catch (e) { + // Could be a sync rules parse error, + // for example from stricter validation that was added. + // This will be retried every couple of seconds. + // When new (valid) sync rules are deployed and processed, this one be disabled. + logger.error(`Failed to start replication for ${this.adapter.name()} with new sync rules`, e); + } + } + } + + this.replicationJobs = newJobs; + + // Terminate any orphaned jobs that no longer have sync rules + for (let job of existingJobs.values()) { + // Old - stop and clean up + try { + await job.terminate(); + } catch (e) { + // This will be retried + logger.warn(`Failed to terminate old ${this.adapter.name()} replication job}`, e); + } + } + + // Sync rules stopped previously or by a different process. + const stopped = await this.storage.getStoppedSyncRules(); + for (let syncRules of stopped) { + try { + const lock = await syncRules.lock(); + try { + await this.adapter.cleanupReplication(syncRules.id); + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + await storage.terminate(); + } finally { + await lock.release(); + } + } catch (e) { + logger.warn(`Failed to terminate ${syncRules.id}`, e); + } + } } } From 08771678acca20462e91b2e4fd37dd1d377714db Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 1 Aug 2024 12:35:17 +0200 Subject: [PATCH 079/298] ReplicationEngine cleanup after removing generic element of the ReplicationAdapter. --- .../src/replication/core/ReplicationEngine.ts | 16 +++++++++++++--- .../service-core/src/storage/SourceEntity.ts | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index abcbb346f..ab049a785 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -1,14 +1,17 @@ import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; import { Replicator } from './Replicator.js'; +import { ConfigurationFileSyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; +import { SyncRulesConfig } from '../../util/config/types.js'; export interface ReplicationEngineOptions { storage: BucketStorageFactory; + config: SyncRulesConfig; } export class ReplicationEngine { private readonly options: ReplicationEngineOptions; - private readonly replicators: Map, Replicator> = new Map(); + private readonly replicators: Map = new Map(); constructor(options: ReplicationEngineOptions) { this.options = options; @@ -20,11 +23,18 @@ export class ReplicationEngine { * * @param adapter */ - public register(adapter: ReplicationAdapter) { + public register(adapter: ReplicationAdapter) { if (this.replicators.has(adapter)) { throw new Error(`Replicator for type ${adapter.name} already registered`); } - this.replicators.set(adapter, new Replicator({ adapter: adapter, storage: this.options.storage })); + this.replicators.set( + adapter, + new Replicator({ + adapter: adapter, + storage: this.options.storage, + sync_rule_provider: new ConfigurationFileSyncRulesProvider(this.options.config) + }) + ); } /** diff --git a/packages/service-core/src/storage/SourceEntity.ts b/packages/service-core/src/storage/SourceEntity.ts index 501d5fd9d..f1bf90e68 100644 --- a/packages/service-core/src/storage/SourceEntity.ts +++ b/packages/service-core/src/storage/SourceEntity.ts @@ -10,6 +10,7 @@ export interface ColumnDescriptor { typeId?: number; } +// TODO: This needs to be consolidate with SourceTable into something new. export interface SourceEntityDescriptor { /** * The internal id of the data source structure in the database From 4c617eb209b5e9cd81ddee1945a2853b4924c357 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 1 Aug 2024 13:43:57 +0200 Subject: [PATCH 080/298] Missing sync rules provider --- .../config/sync-rules/sync-rules-provider.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 packages/service-core/src/util/config/sync-rules/sync-rules-provider.ts diff --git a/packages/service-core/src/util/config/sync-rules/sync-rules-provider.ts b/packages/service-core/src/util/config/sync-rules/sync-rules-provider.ts new file mode 100644 index 000000000..150a1cd1b --- /dev/null +++ b/packages/service-core/src/util/config/sync-rules/sync-rules-provider.ts @@ -0,0 +1,18 @@ +import { SyncRulesConfig } from '../types.js'; +import fs from 'fs/promises'; + +export interface SyncRulesProvider { + get(): Promise; +} + +export class ConfigurationFileSyncRulesProvider implements SyncRulesProvider { + constructor(private config: SyncRulesConfig) {} + + async get(): Promise { + if (this.config.content) { + return this.config.content; + } else if (this.config.path) { + return await fs.readFile(this.config.path, 'utf-8'); + } + } +} From b32e9da1ca1b8dfc48e095c9d5502602d2bd26cf Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 1 Aug 2024 16:18:05 +0200 Subject: [PATCH 081/298] fix bug in module registration. continue instead of returning --- packages/service-core/src/modules/ModuleManager.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index 64a039693..c5bbdf6c9 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -19,10 +19,9 @@ export class ModuleManager { for (const module of modules) { if (this.modules.has(module.name)) { logger.warn(`Module ${module.name} already registered, skipping...`); - return; - } else { - this.modules.set(module.name, module); + continue; } + this.modules.set(module.name, module); // Let the module register functionality on the service context module.register(this.serviceContext); } From ffd218d91bd0a3a7632f641ded4b3dd4ec15060b Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 1 Aug 2024 16:24:24 +0200 Subject: [PATCH 082/298] pass in api to getConectionsSchema --- packages/service-core/src/api/schema.ts | 7 ++----- packages/service-core/src/routes/endpoints/admin.ts | 8 +++++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index 155e293d1..30e737363 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -1,11 +1,8 @@ import { internal_routes } from '@powersync/service-types'; -import * as system from '../system/system-index.js'; +import * as api from '../api/api-index.js'; -export async function getConnectionsSchema( - serviceContext: system.ServiceContext -): Promise { - const api = serviceContext.routerEngine.getAPI(); +export async function getConnectionsSchema(api: api.RouteAPI | null): Promise { if (!api) { return { connections: [] diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index aa6e85f83..ae2273420 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -90,7 +90,9 @@ export const getSchema = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }), handler: async (payload) => { - return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(payload.context.service_context)); + return internal_routes.GetSchemaResponse.encode( + await api.getConnectionsSchema(payload.context.service_context.routerEngine.getAPI()) + ); } }); @@ -148,8 +150,9 @@ export const validate = routeDefinition({ context: { service_context } } = payload; const content = payload.params.sync_rules; + const apiHandler = service_context.routerEngine.getAPI(); - const schemaData = await api.getConnectionsSchema(service_context); + const schemaData = await api.getConnectionsSchema(apiHandler); const schema = new StaticSchema(schemaData.connections); const sync_rules: storage.PersistedSyncRulesContent = { @@ -169,7 +172,6 @@ export const validate = routeDefinition({ } }; - const apiHandler = service_context.routerEngine.getAPI(); const connectionStatus = await apiHandler?.getConnectionStatus(); if (!connectionStatus) { return internal_routes.ValidateResponse.encode({ From 3906951b3fe3f392e14c405651553cb078119de3 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 5 Aug 2024 12:09:43 +0200 Subject: [PATCH 083/298] rename getReplicationLag param --- modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts | 6 +++--- packages/service-core/src/api/RouteAPI.ts | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts index 5f41e8237..2892397a4 100644 --- a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts @@ -261,7 +261,7 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { }; } - async getReplicationLag(slotName: string): Promise { + async getReplicationLag(syncRulesId: string): Promise { const results = await pg_utils.retriedQuery(this.pool, { statement: `SELECT slot_name, @@ -269,14 +269,14 @@ export class PostgresSyncAPIAdapter implements api.RouteAPI { pg_current_wal_lsn(), (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, - params: [{ type: 'varchar', value: slotName }] + params: [{ type: 'varchar', value: syncRulesId }] }); const [row] = pgwire.pgwireRows(results); if (row) { return Number(row.lsn_distance); } - throw new Error(`Could not determine replication lag for slot ${slotName}`); + throw new Error(`Could not determine replication lag for slot ${syncRulesId}`); } async getReplicationHead(): Promise { diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index cfe926d59..559336715 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -40,8 +40,9 @@ export interface RouteAPI { /** * @returns The replication lag: that is the amount of data which has not been * replicated yet, in bytes. + * @param {string} syncRulesId An identifier representing which set of sync rules the lag is required for. */ - getReplicationLag(slotName: string): Promise; + getReplicationLag(syncRulesId: string): Promise; /** * Get the current LSN or equivalent replication HEAD position identifier From 58f5e66b0ff1e305d8dd8bac75412f5ce83d2823 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 6 Aug 2024 16:21:26 +0200 Subject: [PATCH 084/298] re-arrange module initialization --- .../src/system/LifeCycledSystem.ts | 2 +- .../src/module/PostgresModule.ts | 132 ++++++++--------- packages/service-core/src/entry/cli-entry.ts | 14 +- .../src/entry/commands/compact-action.ts | 9 +- .../src/entry/commands/migrate-action.ts | 7 +- .../src/entry/commands/start-action.ts | 11 +- .../db/migrations/1684951997326-init.ts | 12 +- .../1702295701188-sync-rule-state.ts | 10 +- .../1711543888062-write-checkpoint-index.ts | 10 +- .../service-core/src/migrations/migrations.ts | 33 +++-- .../src/modules/AbstractModule.ts | 9 +- .../service-core/src/modules/ModuleManager.ts | 20 +-- .../src/storage/StorageFactory.ts | 24 +++ .../src/storage/StorageProvider.ts | 12 +- .../src/storage/mongo/MongoStorageProvider.ts | 18 ++- .../service-core/src/system/ServiceContext.ts | 138 ++++-------------- packages/service-core/src/util/config.ts | 12 +- .../util/config/compound-config-collector.ts | 17 +-- .../service-core/src/util/config/types.ts | 7 +- service/src/entry.ts | 25 ++-- service/src/metrics.ts | 13 ++ service/src/runners/server.ts | 108 ++++++++------ service/src/runners/shared-runner.ts | 20 +++ service/src/runners/stream-worker.ts | 53 +++++-- service/src/runners/unified-runner.ts | 38 +++++ 25 files changed, 407 insertions(+), 347 deletions(-) create mode 100644 packages/service-core/src/storage/StorageFactory.ts create mode 100644 service/src/metrics.ts create mode 100644 service/src/runners/shared-runner.ts create mode 100644 service/src/runners/unified-runner.ts diff --git a/libs/lib-services/src/system/LifeCycledSystem.ts b/libs/lib-services/src/system/LifeCycledSystem.ts index 3cd77c938..bcbc911be 100644 --- a/libs/lib-services/src/system/LifeCycledSystem.ts +++ b/libs/lib-services/src/system/LifeCycledSystem.ts @@ -20,7 +20,7 @@ export type ComponentLifecycle = PartialLifecycle & { }; export type LifecycleHandler = () => ComponentLifecycle; -export abstract class LifeCycledSystem { +export class LifeCycledSystem { components: ComponentLifecycle[] = []; constructor() { diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 5397bd90a..1a684ceb9 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,4 +1,4 @@ -import { api, auth, db, replication, storage, system } from '@powersync/service-core'; +import { api, auth, replication, system } from '@powersync/service-core'; import * as jpgwire from '@powersync/service-jpgwire'; import * as t from 'ts-codec'; @@ -8,7 +8,6 @@ import * as types from '../types/types.js'; import { PostgresSyncAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; -import { terminateReplicators } from '../utils/teardown.js'; export class PostgresModule extends replication.ReplicationModule { constructor() { @@ -23,42 +22,37 @@ export class PostgresModule extends replication.ReplicationModule { return types.PostgresConnectionConfig; } - async register(context: system.ServiceContext): Promise { - // Add the Supabase Ket collector to the resolved config whenever it is collected - context.configCollector.registerListener({ - configCollected: async (event) => { - if (!event.base_config.client_auth?.supabase) { + async initialize(context: system.ServiceContextContainer): Promise { + await super.initialize(context); + + const { configuration } = context; + // Record replicated bytes using global jpgwire metrics. + if (!configuration.base_config.client_auth?.supabase) { + return; + } + + // Register the Supabase key collector(s) + configuration.connections + ?.map((baseConfig) => { + if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { return; } + try { + return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); + } catch (ex) { + logger.warn('Failed to decode configuration in Postgres module initialization.', ex); + } + }) + .filter((c) => !!c) + .forEach((config) => { + const keyCollector = new SupabaseKeyCollector(config!); + context.lifeCycleEngine.withLifecycle(keyCollector, { + // Close the internal pool + stop: (collector) => collector.shutdown() + }); + configuration.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); + }); - // Register the Supabase key collector(s) - event.resolved_config.connections - ?.map((baseConfig) => { - if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { - return; - } - try { - return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); - } catch (ex) { - logger.warn('Failed to decode configuration in Postgres module initialization.', ex); - } - }) - .filter((c) => !!c) - .forEach((config) => { - const keyCollector = new SupabaseKeyCollector(config!); - context.withLifecycle(keyCollector, { - // Close the internal pool - stop: (collector) => collector.shutdown() - }); - event.resolved_config.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); - }); - } - }); - } - - async initialize(context: system.ServiceContext): Promise { - await super.initialize(context); - // Record replicated bytes using global jpgwire metrics. jpgwire.setMetricsRecorder({ addBytesRead(bytes) { context.metrics.data_replicated_bytes.add(bytes); @@ -84,40 +78,38 @@ export class PostgresModule extends replication.ReplicationModule { }; } - async teardown(context: system.ServiceContext): Promise { - const mongoDB = storage.createPowerSyncMongo(context.configuration.storage); - try { - // TODO this should not be necessary since the service context - // has already been initialized. - // However we need a direct mongo connection for this. - // Maybe we can add termination methods to the storage. - // TODO improve this when other storage methods or connections are implemented - logger.info(`Waiting for auth`); - await db.mongo.waitForAuth(mongoDB.db); - - logger.info(`Terminating replication slots`); - const connections = (context.configuration.connections ?? []) - .filter((c) => c.type == 'postgresql') - .map((c) => types.PostgresConnectionConfig.decode(c as any)); - - for (const connection of connections) { - await terminateReplicators(context.storage, this.resolveConfig(connection)); - } - - const database = mongoDB.db; - logger.info(`Dropping database ${database.namespace}`); - await database.dropDatabase(); - logger.info(`Done`); - await mongoDB.client.close(); - - // If there was an error connecting to postgress, the process may stay open indefinitely. - // This forces an exit. - // We do not consider those errors a teardown failure. - process.exit(0); - } catch (e) { - logger.error(`Teardown failure`, e); - await mongoDB.client.close(); - process.exit(1); - } + async teardown(): Promise { + // TODO this needs the service context to operate. + // Should this keep a refference? + // const mongoDB = storage.createPowerSyncMongo(context.configuration.storage); + // try { + // // TODO this should not be necessary since the service context + // // has already been initialized. + // // However we need a direct mongo connection for this. + // // Maybe we can add termination methods to the storage. + // // TODO improve this when other storage methods or connections are implemented + // logger.info(`Waiting for auth`); + // await db.mongo.waitForAuth(mongoDB.db); + // logger.info(`Terminating replication slots`); + // const connections = (context.configuration.connections ?? []) + // .filter((c) => c.type == 'postgresql') + // .map((c) => types.PostgresConnectionConfig.decode(c as any)); + // for (const connection of connections) { + // await terminateReplicators(context.storage, this.resolveConfig(connection)); + // } + // const database = mongoDB.db; + // logger.info(`Dropping database ${database.namespace}`); + // await database.dropDatabase(); + // logger.info(`Done`); + // await mongoDB.client.close(); + // // If there was an error connecting to postgress, the process may stay open indefinitely. + // // This forces an exit. + // // We do not consider those errors a teardown failure. + // process.exit(0); + // } catch (e) { + // logger.error(`Teardown failure`, e); + // await mongoDB.client.close(); + // process.exit(1); + // } } } diff --git a/packages/service-core/src/entry/cli-entry.ts b/packages/service-core/src/entry/cli-entry.ts index f2d541dac..a9f693c77 100644 --- a/packages/service-core/src/entry/cli-entry.ts +++ b/packages/service-core/src/entry/cli-entry.ts @@ -1,7 +1,6 @@ import { Command } from 'commander'; import { logger } from '@powersync/lib-services-framework'; -import * as modules from '../modules/modules-index.js'; import * as utils from '../util/util-index.js'; import { registerCompactAction } from './commands/compact-action.js'; import { registerMigrationAction } from './commands/migrate-action.js'; @@ -14,19 +13,16 @@ import { registerTearDownAction } from './commands/teardown-action.js'; * This registers standard actions for teardown and migrations. * Optionally registers the start command handlers. */ -export function generateEntryProgram( - moduleManager: modules.ModuleManager, - startHandlers?: Record -) { +export function generateEntryProgram(startHandlers?: Record) { const entryProgram = new Command(); entryProgram.name('powersync-runner').description('CLI to initiate a PowerSync service runner'); - registerTearDownAction(entryProgram, moduleManager); - registerMigrationAction(entryProgram, moduleManager); - registerCompactAction(entryProgram, moduleManager); + registerTearDownAction(entryProgram); + registerMigrationAction(entryProgram); + registerCompactAction(entryProgram); if (startHandlers) { - registerStartAction(entryProgram, moduleManager, startHandlers); + registerStartAction(entryProgram, startHandlers); } return { diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index 47ea806f5..6016644e5 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -2,8 +2,8 @@ import { Command } from 'commander'; import { logger } from '@powersync/lib-services-framework'; import * as v8 from 'v8'; -import * as modules from '../../modules/modules-index.js'; import * as storage from '../../storage/storage-index.js'; +import * as utils from '../../util/util-index.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'compact'; @@ -20,17 +20,14 @@ const HEAP_LIMIT = v8.getHeapStatistics().heap_size_limit; */ const COMPACT_MEMORY_LIMIT_MB = Math.min(HEAP_LIMIT / 1024 / 1024 - 128, 1024); -export function registerCompactAction(program: Command, moduleManager: modules.ModuleManager) { +export function registerCompactAction(program: Command) { const compactCommand = program.command(COMMAND_NAME); wrapConfigCommand(compactCommand); return compactCommand.description('Compact storage').action(async (options) => { const runnerConfig = extractRunnerOptions(options); - - await moduleManager.initialize(runnerConfig); - - const { configuration } = moduleManager.serviceContext; + const configuration = await utils.loadConfig(runnerConfig); const { storage: storageConfig } = configuration; const psdb = storage.createPowerSyncMongo(storageConfig); const client = psdb.client; diff --git a/packages/service-core/src/entry/commands/migrate-action.ts b/packages/service-core/src/entry/commands/migrate-action.ts index 0196b8486..29ed5bbee 100644 --- a/packages/service-core/src/entry/commands/migrate-action.ts +++ b/packages/service-core/src/entry/commands/migrate-action.ts @@ -2,12 +2,11 @@ import { logger } from '@powersync/lib-services-framework'; import { Command } from 'commander'; import * as migrations from '../../migrations/migrations-index.js'; -import * as modules from '../../modules/modules-index.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'migrate'; -export function registerMigrationAction(program: Command, moduleManager: modules.ModuleManager) { +export function registerMigrationAction(program: Command) { const migrationCommand = program.command(COMMAND_NAME); wrapConfigCommand(migrationCommand); @@ -16,12 +15,10 @@ export function registerMigrationAction(program: Command, moduleManager: modules .description('Run migrations') .argument('', 'Migration direction. `up` or `down`') .action(async (direction: migrations.Direction, options) => { - await moduleManager.initialize(extractRunnerOptions(options)); - try { await migrations.migrate({ direction, - service_context: moduleManager.serviceContext + runner_config: extractRunnerOptions(options) }); process.exit(0); diff --git a/packages/service-core/src/entry/commands/start-action.ts b/packages/service-core/src/entry/commands/start-action.ts index db40c55b6..9b1cceb23 100644 --- a/packages/service-core/src/entry/commands/start-action.ts +++ b/packages/service-core/src/entry/commands/start-action.ts @@ -1,16 +1,11 @@ import { Command } from 'commander'; -import * as modules from '../../modules/modules-index.js'; import * as utils from '../../util/util-index.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'start'; -export function registerStartAction( - program: Command, - moduleManager: modules.ModuleManager, - handlers: Record -) { +export function registerStartAction(program: Command, handlers: Record) { const startCommand = program.command(COMMAND_NAME); wrapConfigCommand(startCommand); @@ -23,9 +18,7 @@ export function registerStartAction( utils.env.PS_RUNNER_TYPE ) .action(async (options) => { - await moduleManager.initialize(extractRunnerOptions(options)); - const runner = handlers[options.runnerType as utils.ServiceRunner]; - await runner(moduleManager.serviceContext); + await runner(extractRunnerOptions(options)); }); } diff --git a/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts b/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts index 4f1fff58a..9042f0b55 100644 --- a/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts +++ b/packages/service-core/src/migrations/db/migrations/1684951997326-init.ts @@ -3,9 +3,11 @@ import * as storage from '../../../storage/storage-index.js'; import * as utils from '../../../util/util-index.js'; export const up = async (context: utils.MigrationContext) => { - const { service_context } = context; + const { runner_config } = context; - const database = storage.createPowerSyncMongo(service_context.configuration.storage); + const config = await utils.loadConfig(runner_config); + + const database = storage.createPowerSyncMongo(config.storage); await mongo.waitForAuth(database.db); try { await database.bucket_parameters.createIndex( @@ -22,8 +24,10 @@ export const up = async (context: utils.MigrationContext) => { }; export const down = async (context: utils.MigrationContext) => { - const { service_context } = context; - const database = storage.createPowerSyncMongo(service_context.configuration.storage); + const { runner_config } = context; + const config = await utils.loadConfig(runner_config); + + const database = storage.createPowerSyncMongo(config.storage); try { if (await database.bucket_parameters.indexExists('lookup')) { await database.bucket_parameters.dropIndex('lookup1'); diff --git a/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts b/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts index a23b47e68..d0782ce5a 100644 --- a/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +++ b/packages/service-core/src/migrations/db/migrations/1702295701188-sync-rule-state.ts @@ -24,8 +24,9 @@ interface LegacySyncRulesDocument extends storage.SyncRuleDocument { } export const up = async (context: utils.MigrationContext) => { - const { service_context } = context; - const db = storage.createPowerSyncMongo(service_context.configuration.storage); + const { runner_config } = context; + const config = await utils.loadConfig(runner_config); + const db = storage.createPowerSyncMongo(config.storage); await mongo.waitForAuth(db.db); try { @@ -70,9 +71,10 @@ export const up = async (context: utils.MigrationContext) => { }; export const down = async (context: utils.MigrationContext) => { - const { service_context } = context; + const { runner_config } = context; + const config = await utils.loadConfig(runner_config); - const db = storage.createPowerSyncMongo(service_context.configuration.storage); + const db = storage.createPowerSyncMongo(config.storage); try { await db.sync_rules.updateMany( { diff --git a/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts b/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts index 30ecbbcc3..be568408a 100644 --- a/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +++ b/packages/service-core/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts @@ -2,8 +2,9 @@ import * as storage from '../../../storage/storage-index.js'; import * as utils from '../../../util/util-index.js'; export const up = async (context: utils.MigrationContext) => { - const { service_context } = context; - const db = storage.createPowerSyncMongo(service_context.configuration.storage); + const { runner_config } = context; + const config = await utils.loadConfig(runner_config); + const db = storage.createPowerSyncMongo(config.storage); try { await db.write_checkpoints.createIndex( @@ -18,9 +19,10 @@ export const up = async (context: utils.MigrationContext) => { }; export const down = async (context: utils.MigrationContext) => { - const { service_context } = context; + const { runner_config } = context; + const config = await utils.loadConfig(runner_config); - const db = storage.createPowerSyncMongo(service_context.configuration.storage); + const db = storage.createPowerSyncMongo(config.storage); try { if (await db.write_checkpoints.indexExists('user_id')) { diff --git a/packages/service-core/src/migrations/migrations.ts b/packages/service-core/src/migrations/migrations.ts index 7dc45939a..d2abdc6ea 100644 --- a/packages/service-core/src/migrations/migrations.ts +++ b/packages/service-core/src/migrations/migrations.ts @@ -5,7 +5,6 @@ import { fileURLToPath } from 'url'; import { logger } from '@powersync/lib-services-framework'; import * as db from '../db/db-index.js'; import * as locks from '../locks/locks-index.js'; -import * as system from '../system/system-index.js'; import * as util from '../util/util-index.js'; import { Direction } from './definitions.js'; import { execute, writeLogsToStore } from './executor.js'; @@ -21,21 +20,26 @@ const MIGRATIONS_DIR = path.join(__dirname, '/db/migrations'); export type MigrationOptions = { direction: Direction; - service_context: system.ServiceContext; + runner_config: util.RunnerConfig; +}; + +export type AutomaticMigrationParams = { + config: util.ResolvedPowerSyncConfig; + runner_config: util.RunnerConfig; }; /** * Loads migrations and injects a custom context for loading the specified * runner configuration. */ -const loadMigrations = async (dir: string, serviceContext: system.ServiceContext) => { +const loadMigrations = async (dir: string, runnerConfig: util.RunnerConfig) => { const files = await fs.readdir(dir); const migrations = files.filter((file) => { return path.extname(file) === '.js'; }); const context: util.MigrationContext = { - service_context: serviceContext + runner_config: runnerConfig }; return await Promise.all( @@ -54,16 +58,15 @@ const loadMigrations = async (dir: string, serviceContext: system.ServiceContext * Runs migration scripts exclusively using Mongo locks */ export const migrate = async (options: MigrationOptions) => { - const { direction, service_context } = options; + const { direction, runner_config } = options; + const config = await util.loadConfig(runner_config); + const { storage } = config; /** * Try and get Mongo from config file. * But this might not be available in Journey Micro as we use the standard Mongo. */ - const { configuration } = service_context; - const { storage } = configuration; - const client = db.mongo.createMongoClient(storage); logger.info('Connecting to MongoDB'); await client.connect(); @@ -97,7 +100,7 @@ export const migrate = async (options: MigrationOptions) => { try { logger.info('Loading migrations'); - const migrations = await loadMigrations(MIGRATIONS_DIR, service_context); + const migrations = await loadMigrations(MIGRATIONS_DIR, runner_config); // Use the provided config to connect to Mongo const store = createMongoMigrationStore(clientDB); @@ -125,3 +128,15 @@ export const migrate = async (options: MigrationOptions) => { logger.info('Done with migrations'); } }; + +/** + * Ensures automatic migrations are executed + */ +export const ensureAutomaticMigrations = async (params: AutomaticMigrationParams) => { + if (!params.config.migrations?.disable_auto_migration) { + await migrate({ + direction: Direction.Up, + runner_config: params.runner_config + }); + } +}; diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index 02b906b8d..d1262b1e0 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -1,4 +1,4 @@ -import { ServiceContext } from '../system/ServiceContext.js'; +import { ServiceContextContainer } from '../system/ServiceContext.js'; export interface AbstractModuleOptions { name: string; @@ -14,16 +14,15 @@ export abstract class AbstractModule { } /** - * Register any functionality on the {@link ServiceContext}. + * Register any functionality on the {@link ServiceContextContainer}. * Note this will be executed before the ServiceContext has been initialized. * This can be used to register storage providers which will be created during initialization. */ - public abstract register(context: ServiceContext): Promise; /** * Initialize the module using any required services from the ServiceContext */ - public abstract initialize(context: ServiceContext): Promise; + public abstract initialize(context: ServiceContextContainer): Promise; /** * Finish processing any requests and gracefully shut down any resources managed by the module @@ -33,5 +32,5 @@ export abstract class AbstractModule { /** * Terminate and clean up any resources managed by the module right away */ - public abstract teardown(context: ServiceContext): Promise; + public abstract teardown(): Promise; } diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index c5bbdf6c9..1aba2eecc 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -1,19 +1,13 @@ import { logger } from '@powersync/lib-services-framework'; -import { ServiceContext } from '../system/ServiceContext.js'; -import { RunnerConfig } from '../util/util-index.js'; +import * as system from '../system/system-index.js'; import { AbstractModule } from './AbstractModule.js'; - /** * The module manager is responsible for managing the lifecycle of all modules in the system. */ export class ModuleManager { - readonly serviceContext: ServiceContext; - private readonly modules: Map = new Map(); - constructor(serviceContext?: ServiceContext) { - this.serviceContext = serviceContext ?? new ServiceContext(); - } + constructor() {} public register(modules: AbstractModule[]) { for (const module of modules) { @@ -22,16 +16,12 @@ export class ModuleManager { continue; } this.modules.set(module.name, module); - // Let the module register functionality on the service context - module.register(this.serviceContext); } } - async initialize(entryConfig: RunnerConfig) { - await this.serviceContext.initialize(entryConfig); - + async initialize(serviceContext: system.ServiceContextContainer) { for (const module of this.modules.values()) { - await module.initialize(this.serviceContext); + await module.initialize(serviceContext); } } @@ -43,7 +33,7 @@ export class ModuleManager { async tearDown() { for (const module of this.modules.values()) { - await module.teardown(this.serviceContext); + await module.teardown(); } } } diff --git a/packages/service-core/src/storage/StorageFactory.ts b/packages/service-core/src/storage/StorageFactory.ts new file mode 100644 index 000000000..18af2c403 --- /dev/null +++ b/packages/service-core/src/storage/StorageFactory.ts @@ -0,0 +1,24 @@ +import { ResolvedPowerSyncConfig } from '../util/util-index.js'; +import { GeneratedStorage, StorageProvider } from './StorageProvider.js'; + +export class StorageFactory { + protected storageProviders: Map; + + constructor() { + this.storageProviders = new Map(); + } + + /** + * Register a provider which generates a {@link BucketStorageFactory} + * given the matching config specified in the loaded {@link ResolvedPowerSyncConfig} + */ + registerProvider(provider: StorageProvider) { + this.storageProviders.set(provider.type, provider); + } + + generateStorage(config: ResolvedPowerSyncConfig): Promise { + return this.storageProviders.get(config.storage.type)!.generate({ + resolved_config: config + }); + } +} diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts index 1728c6d17..581a29df1 100644 --- a/packages/service-core/src/storage/StorageProvider.ts +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -1,17 +1,19 @@ import { BucketStorageFactory } from '../storage/BucketStorage.js'; +import * as util from '../util/util-index.js'; + export type GeneratedStorage = { storage: BucketStorageFactory; disposer: () => Promise; }; -export type BaseStorageConfig = { - slot_name_prefix: string; +export type StorageGenerationParams = { + resolved_config: util.ResolvedPowerSyncConfig; }; -export interface StorageProvider { - // The storage type +export interface StorageProvider { + // The storage type which should match the `type` field in the config type: string; - generate(config: StorageConfig): Promise; + generate(storageConfig: StorageGenerationParams): Promise; } diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts index 81a237126..8c854c57a 100644 --- a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -3,23 +3,27 @@ import { configFile } from '@powersync/service-types'; import * as db from '../../db/db-index.js'; import { MongoBucketStorage } from '../MongoBucketStorage.js'; -import { BaseStorageConfig, GeneratedStorage, StorageProvider } from '../StorageProvider.js'; +import { GeneratedStorage, StorageGenerationParams, StorageProvider } from '../StorageProvider.js'; import { PowerSyncMongo } from './db.js'; -export type MongoStorageConfig = configFile.StorageConfig & BaseStorageConfig; -export class MongoStorageProvider implements StorageProvider { +export type MongoStorageConfig = configFile.StorageConfig; + +export class MongoStorageProvider implements StorageProvider { get type() { return 'mongodb'; } - async generate(config: MongoStorageConfig): Promise { - const client = db.mongo.createMongoClient(config); + async generate(params: StorageGenerationParams): Promise { + const { resolved_config } = params; + const { storage } = resolved_config; + const client = db.mongo.createMongoClient(resolved_config.storage); - const database = new PowerSyncMongo(client, { database: config.database }); + const database = new PowerSyncMongo(client, { database: storage.database }); return { storage: new MongoBucketStorage(database, { - slot_name_prefix: config.slot_name_prefix + // TODO currently need the entire resolved config for this + slot_name_prefix: params.resolved_config.slot_name_prefix }), disposer: () => client.close() }; diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index b7556e634..d45d6a226 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -3,74 +3,55 @@ import { Metrics } from '../metrics/Metrics.js'; import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; import { RouterEngine } from '../routes/RouterEngine.js'; import { BucketStorageFactory } from '../storage/BucketStorage.js'; -import { StorageProvider } from '../storage/StorageProvider.js'; +import { StorageFactory } from '../storage/StorageFactory.js'; import { MongoStorageProvider } from '../storage/mongo/MongoStorageProvider.js'; -import { ResolvedPowerSyncConfig, RunnerConfig } from '../util/config/types.js'; -import { CompoundConfigCollector } from '../util/util-index.js'; +import { ResolvedPowerSyncConfig } from '../util/config/types.js'; + +export interface ServiceContext { + configuration: ResolvedPowerSyncConfig; + lifeCycleEngine: LifeCycledSystem; + metrics: Metrics; + replicationEngine: ReplicationEngine; + routerEngine: RouterEngine; + storage: BucketStorageFactory; +} + +export enum ServiceIdentifiers { + // TODO a better identifier + STORAGE = 'storage' +} /** * Context which allows for registering and getting implementations * of various service engines. * This controls registering, initializing and the lifecycle of various services. */ -export class ServiceContext extends LifeCycledSystem { - private _replicationEngine: ReplicationEngine | null; - private _storage: BucketStorageFactory | null; - private _configuration: ResolvedPowerSyncConfig | null; - private _metrics: Metrics | null; - - protected storageProviders: Map; +export class ServiceContextContainer implements ServiceContext { + lifeCycleEngine: LifeCycledSystem; + storageFactory: StorageFactory; - routerEngine: RouterEngine; - configCollector: CompoundConfigCollector; + constructor(public configuration: ResolvedPowerSyncConfig) { + this.lifeCycleEngine = new LifeCycledSystem(); + this.storageFactory = new StorageFactory(); - get configuration(): ResolvedPowerSyncConfig { - if (!this._configuration) { - throw new Error(`Attempt to use configuration before it has been collected`); - } - return this._configuration; + // Mongo storage is available as an option by default + this.storageFactory.registerProvider(new MongoStorageProvider()); } get replicationEngine(): ReplicationEngine { - if (!this._replicationEngine) { - throw new Error(`Attempt to use replication engine before [initialize] has been called`); - } - return this._replicationEngine; + return this.get(ReplicationEngine); } - get storage(): BucketStorageFactory { - if (!this._storage) { - throw new Error(`Attempt to use storage before [initialize] has been called`); - } - return this._storage; + get routerEngine(): RouterEngine { + return this.get(RouterEngine); } - get metrics(): Metrics { - if (!this._metrics) { - throw new Error(`Attempt to use metrics before [initialize] has been called`); - } - return this._metrics; + get storage(): BucketStorageFactory { + return this.get(ServiceIdentifiers.STORAGE); } - constructor() { - super(); - - // These will only be set once `initialize` has been called - this._replicationEngine = null; - this._storage = null; - this._configuration = null; - this._metrics = null; - - this.storageProviders = new Map(); - // Mongo storage is available as an option by default - this.registerStorageProvider(new MongoStorageProvider()); - - this.configCollector = new CompoundConfigCollector(); - - this.routerEngine = new RouterEngine(); - this.withLifecycle(this.routerEngine, { - stop: (routerEngine) => routerEngine.shutdown() - }); + get metrics(): Metrics { + return this.get(Metrics); } /** @@ -87,61 +68,4 @@ export class ServiceContext extends LifeCycledSystem { get(identifier: ServiceIdentifier) { return container.getImplementation(identifier); } - - /** - * Register a provider which generates a {@link BucketStorageFactory} - * given the matching config specified in the loaded {@link ResolvedPowerSyncConfig} - */ - registerStorageProvider(provider: StorageProvider) { - this.storageProviders.set(provider.type, provider); - } - - async initialize(entryConfig: RunnerConfig) { - // Collect the config - this._configuration = await this.configCollector.collectConfig(entryConfig); - - const { storage: storageConfig } = this.configuration; - const { type } = storageConfig; - const provider = this.storageProviders.get(type); - - if (!provider) { - throw new Error(`No storage provider registered for type: ${type}`); - } - - const { storage, disposer } = await provider.generate({ - ...storageConfig, - slot_name_prefix: this.configuration.slot_name_prefix - }); - - this._storage = storage; - this.withLifecycle(storage, { - stop: () => disposer() - }); - - // Metrics go here for now - this._metrics = await this.initializeMetrics(); - this.withLifecycle(this.metrics, { - stop: () => this.metrics.shutdown() - }); - - this._replicationEngine = this.withLifecycle( - new ReplicationEngine({ - config: this.configuration.sync_rules, - storage - }), - { - stop: (engine) => engine.stop() - } - ); - } - - protected async initializeMetrics() { - const instanceId = await this.storage.getPowerSyncInstanceId(); - await Metrics.initialise({ - powersync_instance_id: instanceId, - disable_telemetry_sharing: this.configuration.telemetry.disable_telemetry_sharing, - internal_metrics_endpoint: this.configuration.telemetry.internal_service_endpoint - }); - return Metrics.getInstance(); - } } diff --git a/packages/service-core/src/util/config.ts b/packages/service-core/src/util/config.ts index 45f3aee85..58c189d89 100644 --- a/packages/service-core/src/util/config.ts +++ b/packages/service-core/src/util/config.ts @@ -1,6 +1,16 @@ import * as fs from 'fs/promises'; -import { ResolvedPowerSyncConfig } from './config/types.js'; +import { container } from '@powersync/lib-services-framework'; +import { ResolvedPowerSyncConfig, RunnerConfig } from './config/types.js'; +import { CompoundConfigCollector } from './util-index.js'; + +/** + * Loads the resolved config using the registered config collector + */ +export async function loadConfig(runnerConfig: RunnerConfig) { + const collector = container.getImplementation(CompoundConfigCollector); + return collector.collectConfig(runnerConfig); +} export async function loadSyncRules(config: ResolvedPowerSyncConfig): Promise { const sync_rules = config.sync_rules; diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 0dc5218a8..78e8500cb 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -1,4 +1,4 @@ -import { logger, utils } from '@powersync/lib-services-framework'; +import { logger } from '@powersync/lib-services-framework'; import { configFile } from '@powersync/service-types'; import * as auth from '../../auth/auth-index.js'; import { ConfigCollector } from './collectors/config-collector.js'; @@ -46,10 +46,8 @@ const DEFAULT_COLLECTOR_OPTIONS: CompoundConfigCollectorOptions = { ] }; -export class CompoundConfigCollector extends utils.BaseObserver { - constructor(protected options: CompoundConfigCollectorOptions = DEFAULT_COLLECTOR_OPTIONS) { - super(); - } +export class CompoundConfigCollector { + constructor(protected options: CompoundConfigCollectorOptions = DEFAULT_COLLECTOR_OPTIONS) {} /** * Collects and resolves base config @@ -95,6 +93,7 @@ export class CompoundConfigCollector extends utils.BaseObserver - l.configCollected?.({ - base_config: baseConfig, - resolved_config: config - }) - ); - return config; } diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index 84963d533..e5f461e49 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -1,8 +1,8 @@ import { configFile } from '@powersync/service-types'; +import { PowerSyncConfig } from '@powersync/service-types/src/config/PowerSyncConfig.js'; import { CompoundKeyCollector } from '../../auth/CompoundKeyCollector.js'; import { KeySpec } from '../../auth/KeySpec.js'; import { KeyStore } from '../../auth/KeyStore.js'; -import { ServiceContext } from '../../system/ServiceContext.js'; export enum ServiceRunner { UNIFIED = 'unified', @@ -17,10 +17,10 @@ export type RunnerConfig = { }; export type MigrationContext = { - service_context: ServiceContext; + runner_config: RunnerConfig; }; -export type Runner = (serviceContext: ServiceContext) => Promise; +export type Runner = (config: RunnerConfig) => Promise; export type SyncRulesConfig = { present: boolean; @@ -29,6 +29,7 @@ export type SyncRulesConfig = { }; export type ResolvedPowerSyncConfig = { + base_config: PowerSyncConfig; connections?: configFile.DataSourceConfig[]; storage: configFile.StorageConfig; dev: { diff --git a/service/src/entry.ts b/service/src/entry.ts index 7a6f810b6..d817f46fe 100644 --- a/service/src/entry.ts +++ b/service/src/entry.ts @@ -1,25 +1,30 @@ import { container, ContainerImplementation } from '@powersync/lib-services-framework'; -import { entry, modules, utils } from '@powersync/service-core'; +import * as core from '@powersync/service-core'; import PostgresModule from '@powersync/service-module-postgres'; + import { startServer } from './runners/server.js'; -import { startStreamWorker } from './runners/stream-worker.js'; +import { startStreamRunner } from './runners/stream-worker.js'; +import { startUnifiedRunner } from './runners/unified-runner.js'; import { createSentryReporter } from './util/alerting.js'; // Initialize framework components container.registerDefaults(); container.register(ContainerImplementation.REPORTER, createSentryReporter()); -const moduleManager = new modules.ModuleManager(); +const moduleManager = new core.modules.ModuleManager(); moduleManager.register([PostgresModule]); +// This is a bit of a hack. Commands such as the teardown command or even migrations might +// want access to the ModuleManager in order to use modules +container.register(core.ModuleManager, moduleManager); + +// This is nice to have to avoid passing it around +container.register(core.utils.CompoundConfigCollector, new core.utils.CompoundConfigCollector()); // Generate Commander CLI entry point program -const { execute } = entry.generateEntryProgram(moduleManager, { - [utils.ServiceRunner.API]: startServer, - [utils.ServiceRunner.SYNC]: startStreamWorker, - [utils.ServiceRunner.UNIFIED]: async (serviceContext) => { - await startServer(serviceContext); - await startStreamWorker(serviceContext); - } +const { execute } = core.entry.generateEntryProgram({ + [core.utils.ServiceRunner.API]: startServer, + [core.utils.ServiceRunner.SYNC]: startStreamRunner, + [core.utils.ServiceRunner.UNIFIED]: startUnifiedRunner }); /** diff --git a/service/src/metrics.ts b/service/src/metrics.ts new file mode 100644 index 000000000..e6a7d30db --- /dev/null +++ b/service/src/metrics.ts @@ -0,0 +1,13 @@ +import * as core from '@powersync/service-core'; + +export async function registerMetrics(serviceContext: core.system.ServiceContextContainer) { + const instanceId = await serviceContext.storage.getPowerSyncInstanceId(); + await core.metrics.Metrics.initialise({ + powersync_instance_id: instanceId, + disable_telemetry_sharing: serviceContext.configuration.telemetry.disable_telemetry_sharing, + internal_metrics_endpoint: serviceContext.configuration.telemetry.internal_service_endpoint + }); + + // TODO remove singleton + serviceContext.register(core.metrics.Metrics, core.Metrics.getInstance()); +} diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index d988a3ef1..ad3516d00 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -5,63 +5,81 @@ import { container, logger } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; import { SocketRouter } from '../routes/router.js'; +import { registerSharedRunnerServices } from './shared-runner.js'; + +/** + * Configures the server portion on a {@link ServiceContext} + */ +export const registerServerServices = (serviceContext: core.system.ServiceContextContainer) => { + serviceContext.metrics.configureApiMetrics(); + serviceContext.register(core.routes.RouterEngine, new core.routes.RouterEngine()); + serviceContext.lifeCycleEngine.withLifecycle(serviceContext.routerEngine, { + start: async (routerEngine) => { + await routerEngine.start(async (routes) => { + const server = fastify.fastify(); + + server.register(cors, { + origin: '*', + allowedHeaders: ['Content-Type', 'Authorization'], + exposedHeaders: ['Content-Type'], + // Cache time for preflight response + maxAge: 3600 + }); + + core.routes.configureFastifyServer(server, { + service_context: serviceContext, + routes: { api: { routes: routes.api_routes }, sync_stream: { routes: routes.stream_routes } } + }); + + core.routes.configureRSocket(SocketRouter, { + server: server.server, + service_context: serviceContext, + route_generators: routes.socket_routes + }); + + const { port } = serviceContext.configuration; + + await server.listen({ + host: '0.0.0.0', + port + }); + + logger.info(`Running on port ${port}`); + + return { + onShutdown: async () => { + logger.info('Shutting down HTTP server...'); + await server.close(); + logger.info('HTTP server stopped'); + } + }; + }); + }, + stop: (routerEngine) => routerEngine.shutdown() + }); +}; /** * Starts an API server */ -export async function startServer(serviceContext: core.system.ServiceContext) { +export const startServer = async (runnerConfig: core.utils.RunnerConfig) => { logger.info('Booting'); - logger.info('Starting service'); + const config = await core.utils.loadConfig(runnerConfig); + const serviceContext = new core.system.ServiceContextContainer(config); - await serviceContext.start(); + await registerSharedRunnerServices(serviceContext); + registerServerServices(serviceContext); - serviceContext.metrics.configureApiMetrics(); - - await serviceContext.routerEngine.start(async (routes) => { - const server = fastify.fastify(); - - server.register(cors, { - origin: '*', - allowedHeaders: ['Content-Type', 'Authorization'], - exposedHeaders: ['Content-Type'], - // Cache time for preflight response - maxAge: 3600 - }); - - core.routes.configureFastifyServer(server, { - service_context: serviceContext, - routes: { api: { routes: routes.api_routes }, sync_stream: { routes: routes.stream_routes } } - }); - - core.routes.configureRSocket(SocketRouter, { - server: server.server, - service_context: serviceContext, - route_generators: routes.socket_routes - }); - - const { port } = serviceContext.configuration; - - await server.listen({ - host: '0.0.0.0', - port - }); - - logger.info(`Running on port ${port}`); - - return { - onShutdown: async () => { - logger.info('Shutting down HTTP server...'); - await server.close(); - logger.info('HTTP server stopped'); - } - }; - }); + const moduleManager = container.getImplementation(core.modules.ModuleManager); + await moduleManager.initialize(serviceContext); + logger.info('Starting service'); + await serviceContext.lifeCycleEngine.start(); logger.info('service started'); await container.probes.ready(); // Enable in development to track memory usage: // trackMemoryUsage(); -} +}; diff --git a/service/src/runners/shared-runner.ts b/service/src/runners/shared-runner.ts new file mode 100644 index 000000000..c41437012 --- /dev/null +++ b/service/src/runners/shared-runner.ts @@ -0,0 +1,20 @@ +import * as core from '@powersync/service-core'; +import { registerMetrics } from '../metrics.js'; + +/** + * Configures the server portion on a {@link serviceContext} + */ +export const registerSharedRunnerServices = async (serviceContext: core.system.ServiceContextContainer) => { + const { storage, disposer: storageDisposer } = await serviceContext.storageFactory.generateStorage( + serviceContext.configuration + ); + + // TODO better identifier + serviceContext.register(core.system.ServiceIdentifiers.STORAGE, storage); + serviceContext.lifeCycleEngine.withLifecycle(storage, { + stop: () => storageDisposer() + }); + + // Needs to be done after storage + await registerMetrics(serviceContext); +}; diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index 6f2539aff..5937960d4 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -1,25 +1,48 @@ import { container, logger } from '@powersync/lib-services-framework'; -import { migrations, system } from '@powersync/service-core'; +import * as core from '@powersync/service-core'; +import { registerSharedRunnerServices } from './shared-runner.js'; -export async function startStreamWorker(serviceContext: system.ServiceContext) { +/** + * Configures the replication portion on a {@link serviceContext} + */ +export const registerReplicationServices = (serviceContext: core.system.ServiceContextContainer) => { + // Needs to be executed after shared registrations + const replication = new core.replication.ReplicationEngine({ + config: serviceContext.configuration.sync_rules, + storage: serviceContext.storage + }); + + serviceContext.register(core.replication.ReplicationEngine, replication); + serviceContext.lifeCycleEngine.withLifecycle(replication, { + start: (replication) => replication.start(), + stop: (replication) => replication.stop() + }); + serviceContext.metrics.configureReplicationMetrics(serviceContext); +}; + +export const startStreamRunner = async (runnerConfig: core.utils.RunnerConfig) => { logger.info('Booting'); + const config = await core.utils.loadConfig(runnerConfig); + + await core.migrations.ensureAutomaticMigrations({ + config, + runner_config: runnerConfig + }); + // Self hosted version allows for automatic migrations - if (!serviceContext.configuration.migrations?.disable_auto_migration) { - await migrations.migrate({ - direction: migrations.Direction.Up, - service_context: serviceContext - }); - } - logger.info('Starting system'); - await serviceContext.start(); - logger.info('System started'); + const serviceContext = new core.system.ServiceContextContainer(config); - // Start the replication engine - await serviceContext.replicationEngine.start(); + await registerSharedRunnerServices(serviceContext); + registerReplicationServices(serviceContext); - serviceContext.metrics.configureReplicationMetrics(serviceContext); + const moduleManager = container.getImplementation(core.modules.ModuleManager); + await moduleManager.initialize(serviceContext); + + logger.info('Starting system'); + await serviceContext.lifeCycleEngine.start(); + logger.info('System started'); await container.probes.ready(); -} +}; diff --git a/service/src/runners/unified-runner.ts b/service/src/runners/unified-runner.ts new file mode 100644 index 000000000..963d193d9 --- /dev/null +++ b/service/src/runners/unified-runner.ts @@ -0,0 +1,38 @@ +import { container, logger } from '@powersync/lib-services-framework'; +import * as core from '@powersync/service-core'; + +import { registerServerServices } from './server.js'; +import { registerSharedRunnerServices } from './shared-runner.js'; +import { registerReplicationServices } from './stream-worker.js'; + +/** + * Starts an API server + */ +export const startUnifiedRunner = async (runnerConfig: core.utils.RunnerConfig) => { + logger.info('Booting'); + + const config = await core.utils.loadConfig(runnerConfig); + + await core.migrations.ensureAutomaticMigrations({ + config, + runner_config: runnerConfig + }); + + const serviceContext = new core.system.ServiceContextContainer(config); + + await registerSharedRunnerServices(serviceContext); + registerServerServices(serviceContext); + registerReplicationServices(serviceContext); + + const moduleManager = container.getImplementation(core.modules.ModuleManager); + await moduleManager.initialize(serviceContext); + + logger.info('Starting service'); + await serviceContext.lifeCycleEngine.start(); + logger.info('service started'); + + await container.probes.ready(); + + // Enable in development to track memory usage: + // trackMemoryUsage(); +}; From e3d949127cbefd4f438639110d287f5dd7b8d080 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 6 Aug 2024 16:26:06 +0200 Subject: [PATCH 085/298] wip: very basic teardown --- .../src/entry/commands/teardown-action.ts | 6 ++---- packages/service-core/src/runner/teardown.ts | 14 +++++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/service-core/src/entry/commands/teardown-action.ts b/packages/service-core/src/entry/commands/teardown-action.ts index cf27f58e7..fb983fb79 100644 --- a/packages/service-core/src/entry/commands/teardown-action.ts +++ b/packages/service-core/src/entry/commands/teardown-action.ts @@ -1,12 +1,11 @@ import { Command } from 'commander'; -import * as modules from '../../modules/modules-index.js'; import { teardown } from '../../runner/teardown.js'; import { extractRunnerOptions, wrapConfigCommand } from './config-command.js'; const COMMAND_NAME = 'teardown'; -export function registerTearDownAction(program: Command, moduleManager: modules.ModuleManager) { +export function registerTearDownAction(program: Command) { const teardownCommand = program.command(COMMAND_NAME); wrapConfigCommand(teardownCommand); @@ -19,7 +18,6 @@ export function registerTearDownAction(program: Command, moduleManager: modules. throw new Error('TEARDOWN was not acknowledged.'); } - await moduleManager.initialize(extractRunnerOptions(options)); - await teardown(moduleManager); + await teardown(extractRunnerOptions(options)); }); } diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index f2b148038..c4c449414 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -3,12 +3,20 @@ // 1. The replication slots on the source postgres instance (if available). // 2. The mongo database. -import { logger } from '@powersync/lib-services-framework'; +import { container, logger } from '@powersync/lib-services-framework'; import * as modules from '../modules/modules-index.js'; +import * as system from '../system/system-index.js'; +import * as utils from '../util/util-index.js'; -export async function teardown(moduleManager: modules.ModuleManager) { +export async function teardown(runnerConfig: utils.RunnerConfig) { try { - // teardown all modules + const config = await utils.loadConfig(runnerConfig); + const serviceContext = new system.ServiceContextContainer(config); + + // TODO what should be registered on the teardown command here? + + const moduleManager = container.getImplementation(modules.ModuleManager); + await moduleManager.initialize(serviceContext); await moduleManager.tearDown(); process.exit(0); } catch (e) { From 4aab47232b4715901b3a0c69daa0177e0bb0adcb Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 7 Aug 2024 11:33:39 +0200 Subject: [PATCH 086/298] implement storagefactoryprovider --- packages/service-core/src/api/diagnostics.ts | 6 +- packages/service-core/src/metrics/Metrics.ts | 5 +- .../src/replication/core/ReplicationEngine.ts | 12 ++-- .../src/replication/core/Replicator.ts | 26 +++++---- .../src/routes/endpoints/admin.ts | 18 +++--- .../src/routes/endpoints/checkpointing.ts | 8 ++- .../src/routes/endpoints/socket-route.ts | 8 ++- .../src/routes/endpoints/sync-rules.ts | 18 +++--- .../src/routes/endpoints/sync-stream.ts | 4 +- .../src/storage/StorageFactory.ts | 24 -------- .../src/storage/StorageFactoryProvider.ts | 47 ++++++++++++++++ .../src/storage/StorageProvider.ts | 2 +- .../src/storage/mongo/MongoStorageProvider.ts | 4 +- .../service-core/src/storage/storage-index.ts | 2 + .../service-core/src/system/ServiceContext.ts | 55 ++++++++----------- packages/service-core/src/util/util-index.ts | 1 + service/src/metrics.ts | 46 ++++++++++++---- service/src/runners/server.ts | 8 ++- service/src/runners/shared-runner.ts | 20 ------- service/src/runners/stream-worker.ts | 10 ++-- service/src/runners/unified-runner.ts | 8 ++- 21 files changed, 191 insertions(+), 141 deletions(-) delete mode 100644 packages/service-core/src/storage/StorageFactory.ts create mode 100644 packages/service-core/src/storage/StorageFactoryProvider.ts delete mode 100644 service/src/runners/shared-runner.ts diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index de382681e..9c4df90c6 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -52,10 +52,12 @@ export async function getSyncRulesStatus( }; } - const { storage } = serviceContext; + const { + storage: { bucketStorage } + } = serviceContext; const api = serviceContext.routerEngine.getAPI(); - const systemStorage = live_status ? storage.getInstance(persisted) : undefined; + const systemStorage = live_status ? bucketStorage.getInstance(persisted) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; diff --git a/packages/service-core/src/metrics/Metrics.ts b/packages/service-core/src/metrics/Metrics.ts index edf39888e..98888f0db 100644 --- a/packages/service-core/src/metrics/Metrics.ts +++ b/packages/service-core/src/metrics/Metrics.ts @@ -5,7 +5,6 @@ import { Resource } from '@opentelemetry/resources'; import { MeterProvider, MetricReader, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'; import { logger } from '@powersync/lib-services-framework'; import * as storage from '../storage/storage-index.js'; -import * as system from '../system/system-index.js'; import * as util from '../util/util-index.js'; export interface MetricsOptions { @@ -201,7 +200,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable this.concurrent_connections.add(0); } - public configureReplicationMetrics(serviceContext: system.ServiceContext) { + public configureReplicationMetrics(bucketStorage: storage.BucketStorageFactory) { // Rate limit collection of these stats, since it may be an expensive query const MINIMUM_INTERVAL = 60_000; @@ -210,7 +209,7 @@ Anonymous telemetry is currently: ${options.disable_telemetry_sharing ? 'disable function getMetrics() { if (cachedRequest == null || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) { - cachedRequest = serviceContext.storage.getStorageMetrics().catch((e) => { + cachedRequest = bucketStorage.getStorageMetrics().catch((e) => { logger.error(`Failed to get storage metrics`, e); return null; }); diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index 47ee907e3..499ca82b2 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -1,12 +1,12 @@ -import { BucketStorageFactory } from '../../storage/BucketStorage.js'; import { ReplicationAdapter } from './ReplicationAdapter.js'; import { Replicator } from './Replicator.js'; -import { ConfigurationFileSyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; -import { SyncRulesConfig } from '../../util/config/types.js'; + +import * as storage from '../../storage/storage-index.js'; +import * as utils from '.././../util/util-index.js'; export interface ReplicationEngineOptions { - storage: BucketStorageFactory; - config: SyncRulesConfig; + storage: storage.StorageFactoryProvider; + config: utils.SyncRulesConfig; } export class ReplicationEngine { @@ -32,7 +32,7 @@ export class ReplicationEngine { new Replicator({ adapter: adapter, storage: this.options.storage, - sync_rule_provider: new ConfigurationFileSyncRulesProvider(this.options.config) + sync_rule_provider: new utils.ConfigurationFileSyncRulesProvider(this.options.config) }) ); } diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts index cbcd11578..20494abba 100644 --- a/packages/service-core/src/replication/core/Replicator.ts +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -1,15 +1,13 @@ -import * as storage from '../../storage/storage-index.js'; - import { container, logger } from '@powersync/lib-services-framework'; -import { BucketStorageFactory } from '../../storage/BucketStorage.js'; -import { SyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; import { ReplicationAdapter } from './ReplicationAdapter.js'; import { ReplicationJob } from './ReplicationJob.js'; +import * as storage from '../../storage/storage-index.js'; +import * as util from '../../util/util-index.js'; export interface ReplicatorOptions { adapter: ReplicationAdapter; - storage: BucketStorageFactory; - sync_rule_provider: SyncRulesProvider; + storage: storage.StorageFactoryProvider; + sync_rule_provider: util.SyncRulesProvider; } /** @@ -19,7 +17,7 @@ export interface ReplicatorOptions { */ export class Replicator { private readonly adapter: ReplicationAdapter; - private readonly storage: BucketStorageFactory; + private readonly storage: storage.StorageFactoryProvider; private replicationJobs = new Map(); private stopped = false; @@ -29,6 +27,10 @@ export class Replicator { this.storage = options.storage; } + get bucketStorage() { + return this.storage.bucketStorage; + } + public async start(): Promise { this.runLoop().catch((e) => { logger.error(`Data source fatal replication error: ${this.adapter.name()}`, e); @@ -56,7 +58,7 @@ export class Replicator { try { // Configure new sync rules, if they have changed. // In that case, also immediately take out a lock, so that another process doesn't start replication on it. - const { lock } = await this.storage.configureSyncRules(syncRules, { + const { lock } = await this.bucketStorage.configureSyncRules(syncRules, { lock: true }); if (lock) { @@ -90,7 +92,7 @@ export class Replicator { let configuredLock = options?.configured_lock; const existingJobs = new Map(this.replicationJobs.entries()); - const replicatingSyncRules = await this.storage.getReplicatingSyncRules(); + const replicatingSyncRules = await this.bucketStorage.getReplicatingSyncRules(); const newJobs = new Map(); for (let syncRules of replicatingSyncRules) { const existingJob = existingJobs.get(syncRules.id); @@ -112,7 +114,7 @@ export class Replicator { lock = await syncRules.lock(); } const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); + const storage = this.bucketStorage.getInstance(parsed); const newJob = new ReplicationJob({ adapter: this.adapter, storage: storage, @@ -144,14 +146,14 @@ export class Replicator { } // Sync rules stopped previously or by a different process. - const stopped = await this.storage.getStoppedSyncRules(); + const stopped = await this.bucketStorage.getStoppedSyncRules(); for (let syncRules of stopped) { try { const lock = await syncRules.lock(); try { await this.adapter.cleanupReplication(syncRules.id); const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); + const storage = this.bucketStorage.getInstance(parsed); await storage.terminate(); } finally { await lock.release(); diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index ae2273420..55af1dd07 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -54,9 +54,11 @@ export const diagnostics = routeDefinition({ }); } - const { storage } = service_context; - const active = await storage.getActiveSyncRulesContent(); - const next = await storage.getNextSyncRulesContent(); + const { + storage: { bucketStorage } + } = service_context; + const active = await bucketStorage.getActiveSyncRulesContent(); + const next = await bucketStorage.getNextSyncRulesContent(); const active_status = await api.getSyncRulesStatus(service_context, active, { include_content, @@ -105,13 +107,15 @@ export const reprocess = routeDefinition({ const { context: { service_context } } = payload; - const storage = service_context.storage; - const next = await storage.getNextSyncRules(); + const { + storage: { bucketStorage } + } = service_context; + const next = await bucketStorage.getNextSyncRules(); if (next != null) { throw new Error(`Busy processing sync rules - cannot reprocess`); } - const active = await storage.getActiveSyncRules(); + const active = await bucketStorage.getActiveSyncRules(); if (active == null) { throw new errors.JourneyError({ status: 422, @@ -120,7 +124,7 @@ export const reprocess = routeDefinition({ }); } - const new_rules = await storage.updateSyncRules({ + const new_rules = await bucketStorage.updateSyncRules({ content: active.sync_rules.content }); diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index 36cbb7d5b..e4737471f 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -30,7 +30,7 @@ export const writeCheckpoint = routeDefinition({ logger.info(`Waiting for LSN checkpoint: ${head}`); while (Date.now() - start < timeout) { - const cp = await service_context.storage.getActiveCheckpoint(); + const cp = await service_context.storage.bucketStorage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { throw new Error('No sync rules available'); } @@ -60,9 +60,11 @@ export const writeCheckpoint2 = routeDefinition({ // Might want to call this something link replicationHead or something else const currentCheckpoint = await api.getReplicationHead(); - const { storage } = service_context; + const { + storage: { bucketStorage } + } = service_context; - const id = await storage.createWriteCheckpoint(user_id!, { '1': currentCheckpoint }); + const id = await bucketStorage.createWriteCheckpoint(user_id!, { '1': currentCheckpoint }); logger.info(`Write checkpoint 2: ${JSON.stringify({ currentCheckpoint, id: String(id) })}`); return { diff --git a/packages/service-core/src/routes/endpoints/socket-route.ts b/packages/service-core/src/routes/endpoints/socket-route.ts index 954a06ea7..0871980f6 100644 --- a/packages/service-core/src/routes/endpoints/socket-route.ts +++ b/packages/service-core/src/routes/endpoints/socket-route.ts @@ -30,9 +30,11 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => const syncParams = new RequestParameters(context.token_payload!, params.parameters ?? {}); - const storage = service_context.storage; + const { + storage: { bucketStorage } + } = service_context; // Sanity check before we start the stream - const cp = await storage.getActiveCheckpoint(); + const cp = await bucketStorage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { responder.onError( new errors.JourneyError({ @@ -63,7 +65,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => const tracker = new sync.RequestTracker(); try { for await (const data of sync.streamResponse({ - storage, + storage: bucketStorage, params: { ...params, binary_data: true // always true for web sockets diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 2282a765a..8efd6bab2 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -63,7 +63,7 @@ export const deploySyncRules = routeDefinition({ }); } - const sync_rules = await storage.updateSyncRules({ + const sync_rules = await storage.bucketStorage.updateSyncRules({ content: content }); @@ -99,8 +99,10 @@ export const currentSyncRules = routeDefinition({ authorize: authApi, handler: async (payload) => { const { service_context } = payload.context; - const { storage } = service_context; - const sync_rules = await storage.getActiveSyncRulesContent(); + const { + storage: { bucketStorage } + } = service_context; + const sync_rules = await bucketStorage.getActiveSyncRulesContent(); if (!sync_rules) { throw new errors.JourneyError({ status: 422, @@ -109,7 +111,7 @@ export const currentSyncRules = routeDefinition({ }); } const info = await debugSyncRules(service_context, sync_rules.sync_rules_content); - const next = await storage.getNextSyncRulesContent(); + const next = await bucketStorage.getNextSyncRulesContent(); const next_info = next ? await debugSyncRules(service_context, next.sync_rules_content) : null; @@ -141,8 +143,10 @@ export const reprocessSyncRules = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(ReprocessSyncRulesRequest), handler: async (payload) => { - const { storage } = payload.context.service_context; - const sync_rules = await storage.getActiveSyncRules(); + const { + storage: { bucketStorage } + } = payload.context.service_context; + const sync_rules = await bucketStorage.getActiveSyncRules(); if (sync_rules == null) { throw new errors.JourneyError({ status: 422, @@ -151,7 +155,7 @@ export const reprocessSyncRules = routeDefinition({ }); } - const new_rules = await storage.updateSyncRules({ + const new_rules = await bucketStorage.updateSyncRules({ content: sync_rules.sync_rules.content }); return { diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index c1e117407..ddff705cb 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -34,7 +34,7 @@ export const syncStreamed = routeDefinition({ const syncParams = new RequestParameters(payload.context.token_payload!, payload.params.parameters ?? {}); // Sanity check before we start the stream - const cp = await storage.getActiveCheckpoint(); + const cp = await storage.bucketStorage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { throw new errors.JourneyError({ status: 500, @@ -50,7 +50,7 @@ export const syncStreamed = routeDefinition({ sync.transformToBytesTracked( sync.ndjson( sync.streamResponse({ - storage, + storage: storage.bucketStorage, params, syncParams, token: payload.context.token_payload!, diff --git a/packages/service-core/src/storage/StorageFactory.ts b/packages/service-core/src/storage/StorageFactory.ts deleted file mode 100644 index 18af2c403..000000000 --- a/packages/service-core/src/storage/StorageFactory.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ResolvedPowerSyncConfig } from '../util/util-index.js'; -import { GeneratedStorage, StorageProvider } from './StorageProvider.js'; - -export class StorageFactory { - protected storageProviders: Map; - - constructor() { - this.storageProviders = new Map(); - } - - /** - * Register a provider which generates a {@link BucketStorageFactory} - * given the matching config specified in the loaded {@link ResolvedPowerSyncConfig} - */ - registerProvider(provider: StorageProvider) { - this.storageProviders.set(provider.type, provider); - } - - generateStorage(config: ResolvedPowerSyncConfig): Promise { - return this.storageProviders.get(config.storage.type)!.generate({ - resolved_config: config - }); - } -} diff --git a/packages/service-core/src/storage/StorageFactoryProvider.ts b/packages/service-core/src/storage/StorageFactoryProvider.ts new file mode 100644 index 000000000..c944f7ea9 --- /dev/null +++ b/packages/service-core/src/storage/StorageFactoryProvider.ts @@ -0,0 +1,47 @@ +import { LifeCycledSystem } from '@powersync/lib-services-framework'; +import { ResolvedPowerSyncConfig } from '../util/util-index.js'; +import { BucketStorageFactory } from './BucketStorage.js'; +import { BucketStorageProvider, GeneratedStorage } from './StorageProvider.js'; + +export type StorageFactoryProviderOptions = { + configuration: ResolvedPowerSyncConfig; + lifecycle_engine: LifeCycledSystem; +}; + +export class StorageFactoryProvider { + protected storageProviders: Map; + protected generatedStorage: GeneratedStorage | null; + + constructor(options: StorageFactoryProviderOptions) { + this.storageProviders = new Map(); + this.generatedStorage = null; + + // This will create the relevant storage provider when the system is started. + options.lifecycle_engine.withLifecycle(null, { + start: async () => { + const { configuration } = options; + const generatedStorage = await this.storageProviders.get(configuration.storage.type)!.generate({ + resolved_config: configuration + }); + + this.generatedStorage = generatedStorage; + }, + stop: () => this.generatedStorage?.disposer() + }); + } + + get bucketStorage(): BucketStorageFactory { + if (!this.generatedStorage) { + throw new Error(`storage has not been initialized yet.`); + } + return this.generatedStorage.storage; + } + + /** + * Register a provider which generates a {@link BucketStorageFactory} + * given the matching config specified in the loaded {@link ResolvedPowerSyncConfig} + */ + registerProvider(provider: BucketStorageProvider) { + this.storageProviders.set(provider.type, provider); + } +} diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts index 581a29df1..122af4bd0 100644 --- a/packages/service-core/src/storage/StorageProvider.ts +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -11,7 +11,7 @@ export type StorageGenerationParams = { resolved_config: util.ResolvedPowerSyncConfig; }; -export interface StorageProvider { +export interface BucketStorageProvider { // The storage type which should match the `type` field in the config type: string; diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts index 8c854c57a..40e6d6a5c 100644 --- a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -3,12 +3,12 @@ import { configFile } from '@powersync/service-types'; import * as db from '../../db/db-index.js'; import { MongoBucketStorage } from '../MongoBucketStorage.js'; -import { GeneratedStorage, StorageGenerationParams, StorageProvider } from '../StorageProvider.js'; +import { BucketStorageProvider, GeneratedStorage, StorageGenerationParams } from '../StorageProvider.js'; import { PowerSyncMongo } from './db.js'; export type MongoStorageConfig = configFile.StorageConfig; -export class MongoStorageProvider implements StorageProvider { +export class MongoStorageProvider implements BucketStorageProvider { get type() { return 'mongodb'; } diff --git a/packages/service-core/src/storage/storage-index.ts b/packages/service-core/src/storage/storage-index.ts index 876eb95b2..3917b87e5 100644 --- a/packages/service-core/src/storage/storage-index.ts +++ b/packages/service-core/src/storage/storage-index.ts @@ -2,6 +2,7 @@ export * from './BucketStorage.js'; export * from './MongoBucketStorage.js'; export * from './SourceEntity.js'; export * from './SourceTable.js'; +export * from './StorageFactoryProvider.js'; export * from './mongo/db.js'; export * from './mongo/models.js'; @@ -9,6 +10,7 @@ export * from './mongo/MongoBucketBatch.js'; export * from './mongo/MongoIdSequence.js'; export * from './mongo/MongoPersistedSyncRules.js'; export * from './mongo/MongoPersistedSyncRulesContent.js'; +export * from './mongo/MongoStorageProvider.js'; export * from './mongo/MongoSyncBucketStorage.js'; export * from './mongo/MongoSyncRulesLock.js'; export * from './mongo/OperationBatch.js'; diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index d45d6a226..9fd7f2362 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -1,24 +1,18 @@ import { LifeCycledSystem, ServiceIdentifier, container } from '@powersync/lib-services-framework'; -import { Metrics } from '../metrics/Metrics.js'; -import { ReplicationEngine } from '../replication/core/ReplicationEngine.js'; -import { RouterEngine } from '../routes/RouterEngine.js'; -import { BucketStorageFactory } from '../storage/BucketStorage.js'; -import { StorageFactory } from '../storage/StorageFactory.js'; -import { MongoStorageProvider } from '../storage/mongo/MongoStorageProvider.js'; -import { ResolvedPowerSyncConfig } from '../util/config/types.js'; + +import * as metrics from '../metrics/Metrics.js'; +import * as replication from '../replication/replication-index.js'; +import * as routes from '../routes/routes-index.js'; +import * as storage from '../storage/storage-index.js'; +import * as utils from '../util/util-index.js'; export interface ServiceContext { - configuration: ResolvedPowerSyncConfig; + configuration: utils.ResolvedPowerSyncConfig; lifeCycleEngine: LifeCycledSystem; - metrics: Metrics; - replicationEngine: ReplicationEngine; - routerEngine: RouterEngine; - storage: BucketStorageFactory; -} - -export enum ServiceIdentifiers { - // TODO a better identifier - STORAGE = 'storage' + metrics: metrics.Metrics; + replicationEngine: replication.ReplicationEngine; + routerEngine: routes.RouterEngine; + storage: storage.StorageFactoryProvider; } /** @@ -28,30 +22,29 @@ export enum ServiceIdentifiers { */ export class ServiceContextContainer implements ServiceContext { lifeCycleEngine: LifeCycledSystem; - storageFactory: StorageFactory; + storage: storage.StorageFactoryProvider; - constructor(public configuration: ResolvedPowerSyncConfig) { + constructor(public configuration: utils.ResolvedPowerSyncConfig) { this.lifeCycleEngine = new LifeCycledSystem(); - this.storageFactory = new StorageFactory(); + this.storage = new storage.StorageFactoryProvider({ + configuration, + lifecycle_engine: this.lifeCycleEngine + }); // Mongo storage is available as an option by default - this.storageFactory.registerProvider(new MongoStorageProvider()); - } - - get replicationEngine(): ReplicationEngine { - return this.get(ReplicationEngine); + this.storage.registerProvider(new storage.MongoStorageProvider()); } - get routerEngine(): RouterEngine { - return this.get(RouterEngine); + get replicationEngine(): replication.ReplicationEngine { + return this.get(replication.ReplicationEngine); } - get storage(): BucketStorageFactory { - return this.get(ServiceIdentifiers.STORAGE); + get routerEngine(): routes.RouterEngine { + return this.get(routes.RouterEngine); } - get metrics(): Metrics { - return this.get(Metrics); + get metrics(): metrics.Metrics { + return this.get(metrics.Metrics); } /** diff --git a/packages/service-core/src/util/util-index.ts b/packages/service-core/src/util/util-index.ts index 5581284e6..50ad85dc7 100644 --- a/packages/service-core/src/util/util-index.ts +++ b/packages/service-core/src/util/util-index.ts @@ -19,3 +19,4 @@ export * from './config/sync-rules/impl/base64-sync-rules-collector.js'; export * from './config/sync-rules/impl/filesystem-sync-rules-collector.js'; export * from './config/sync-rules/impl/inline-sync-rules-collector.js'; export * from './config/sync-rules/sync-collector.js'; +export * from './config/sync-rules/sync-rules-provider.js'; diff --git a/service/src/metrics.ts b/service/src/metrics.ts index e6a7d30db..a2479b0c3 100644 --- a/service/src/metrics.ts +++ b/service/src/metrics.ts @@ -1,13 +1,39 @@ import * as core from '@powersync/service-core'; -export async function registerMetrics(serviceContext: core.system.ServiceContextContainer) { - const instanceId = await serviceContext.storage.getPowerSyncInstanceId(); - await core.metrics.Metrics.initialise({ - powersync_instance_id: instanceId, - disable_telemetry_sharing: serviceContext.configuration.telemetry.disable_telemetry_sharing, - internal_metrics_endpoint: serviceContext.configuration.telemetry.internal_service_endpoint - }); - - // TODO remove singleton - serviceContext.register(core.metrics.Metrics, core.Metrics.getInstance()); +export enum MetricModes { + API = 'api', + REPLICATION = 'replication' } + +export type MetricsRegistrationOptions = { + service_context: core.system.ServiceContextContainer; + modes: MetricModes[]; +}; + +export const registerMetrics = async (options: MetricsRegistrationOptions) => { + const { service_context, modes } = options; + + // This requires an instantiated bucket storage, which is only created when the lifecycle starts + service_context.lifeCycleEngine.withLifecycle(null, { + start: async () => { + const instanceId = await service_context.storage.bucketStorage.getPowerSyncInstanceId(); + await core.metrics.Metrics.initialise({ + powersync_instance_id: instanceId, + disable_telemetry_sharing: service_context.configuration.telemetry.disable_telemetry_sharing, + internal_metrics_endpoint: service_context.configuration.telemetry.internal_service_endpoint + }); + + // TODO remove singleton + const instance = core.Metrics.getInstance(); + service_context.register(core.metrics.Metrics, instance); + + if (modes.includes(MetricModes.API)) { + instance.configureApiMetrics(); + } + + if (modes.includes(MetricModes.REPLICATION)) { + instance.configureReplicationMetrics(service_context.storage.bucketStorage); + } + } + }); +}; diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index ad3516d00..6515eb721 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -4,8 +4,8 @@ import fastify from 'fastify'; import { container, logger } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; +import { MetricModes, registerMetrics } from '../metrics.js'; import { SocketRouter } from '../routes/router.js'; -import { registerSharedRunnerServices } from './shared-runner.js'; /** * Configures the server portion on a {@link ServiceContext} @@ -68,9 +68,13 @@ export const startServer = async (runnerConfig: core.utils.RunnerConfig) => { const config = await core.utils.loadConfig(runnerConfig); const serviceContext = new core.system.ServiceContextContainer(config); - await registerSharedRunnerServices(serviceContext); registerServerServices(serviceContext); + await registerMetrics({ + service_context: serviceContext, + modes: [MetricModes.API] + }); + const moduleManager = container.getImplementation(core.modules.ModuleManager); await moduleManager.initialize(serviceContext); diff --git a/service/src/runners/shared-runner.ts b/service/src/runners/shared-runner.ts deleted file mode 100644 index c41437012..000000000 --- a/service/src/runners/shared-runner.ts +++ /dev/null @@ -1,20 +0,0 @@ -import * as core from '@powersync/service-core'; -import { registerMetrics } from '../metrics.js'; - -/** - * Configures the server portion on a {@link serviceContext} - */ -export const registerSharedRunnerServices = async (serviceContext: core.system.ServiceContextContainer) => { - const { storage, disposer: storageDisposer } = await serviceContext.storageFactory.generateStorage( - serviceContext.configuration - ); - - // TODO better identifier - serviceContext.register(core.system.ServiceIdentifiers.STORAGE, storage); - serviceContext.lifeCycleEngine.withLifecycle(storage, { - stop: () => storageDisposer() - }); - - // Needs to be done after storage - await registerMetrics(serviceContext); -}; diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index 5937960d4..f61cb39ae 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -1,6 +1,6 @@ import { container, logger } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; -import { registerSharedRunnerServices } from './shared-runner.js'; +import { MetricModes, registerMetrics } from '../metrics.js'; /** * Configures the replication portion on a {@link serviceContext} @@ -17,7 +17,6 @@ export const registerReplicationServices = (serviceContext: core.system.ServiceC start: (replication) => replication.start(), stop: (replication) => replication.stop() }); - serviceContext.metrics.configureReplicationMetrics(serviceContext); }; export const startStreamRunner = async (runnerConfig: core.utils.RunnerConfig) => { @@ -31,12 +30,15 @@ export const startStreamRunner = async (runnerConfig: core.utils.RunnerConfig) = }); // Self hosted version allows for automatic migrations - const serviceContext = new core.system.ServiceContextContainer(config); - await registerSharedRunnerServices(serviceContext); registerReplicationServices(serviceContext); + await registerMetrics({ + service_context: serviceContext, + modes: [MetricModes.REPLICATION] + }); + const moduleManager = container.getImplementation(core.modules.ModuleManager); await moduleManager.initialize(serviceContext); diff --git a/service/src/runners/unified-runner.ts b/service/src/runners/unified-runner.ts index 963d193d9..687e8f38c 100644 --- a/service/src/runners/unified-runner.ts +++ b/service/src/runners/unified-runner.ts @@ -1,8 +1,8 @@ import { container, logger } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; +import { MetricModes, registerMetrics } from '../metrics.js'; import { registerServerServices } from './server.js'; -import { registerSharedRunnerServices } from './shared-runner.js'; import { registerReplicationServices } from './stream-worker.js'; /** @@ -20,10 +20,14 @@ export const startUnifiedRunner = async (runnerConfig: core.utils.RunnerConfig) const serviceContext = new core.system.ServiceContextContainer(config); - await registerSharedRunnerServices(serviceContext); registerServerServices(serviceContext); registerReplicationServices(serviceContext); + await registerMetrics({ + service_context: serviceContext, + modes: [MetricModes.API, MetricModes.REPLICATION] + }); + const moduleManager = container.getImplementation(core.modules.ModuleManager); await moduleManager.initialize(serviceContext); From d923c7811380d2870077cc8a5a0de3070a054f84 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 7 Aug 2024 11:43:49 +0200 Subject: [PATCH 087/298] remove duplicate metrics api config --- service/src/runners/server.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 6515eb721..d20482d93 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -11,7 +11,6 @@ import { SocketRouter } from '../routes/router.js'; * Configures the server portion on a {@link ServiceContext} */ export const registerServerServices = (serviceContext: core.system.ServiceContextContainer) => { - serviceContext.metrics.configureApiMetrics(); serviceContext.register(core.routes.RouterEngine, new core.routes.RouterEngine()); serviceContext.lifeCycleEngine.withLifecycle(serviceContext.routerEngine, { start: async (routerEngine) => { From e5d947dbd4f384149bae27d51071cee85c96decd Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 7 Aug 2024 14:50:31 +0200 Subject: [PATCH 088/298] cleanup --- .../src/module/PostgresModule.ts | 52 ++++++++++--------- .../src/modules/AbstractModule.ts | 5 -- .../service-core/src/modules/ModuleManager.ts | 8 --- .../src/replication/core/ReplicationModule.ts | 2 - service/src/metrics.ts | 3 +- 5 files changed, 29 insertions(+), 41 deletions(-) diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 1a684ceb9..f154f05ca 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -25,34 +25,11 @@ export class PostgresModule extends replication.ReplicationModule { async initialize(context: system.ServiceContextContainer): Promise { await super.initialize(context); - const { configuration } = context; // Record replicated bytes using global jpgwire metrics. - if (!configuration.base_config.client_auth?.supabase) { - return; + if (context.configuration.base_config.client_auth?.supabase) { + this.registerSupabaseAuth(context); } - // Register the Supabase key collector(s) - configuration.connections - ?.map((baseConfig) => { - if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { - return; - } - try { - return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); - } catch (ex) { - logger.warn('Failed to decode configuration in Postgres module initialization.', ex); - } - }) - .filter((c) => !!c) - .forEach((config) => { - const keyCollector = new SupabaseKeyCollector(config!); - context.lifeCycleEngine.withLifecycle(keyCollector, { - // Close the internal pool - stop: (collector) => collector.shutdown() - }); - configuration.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); - }); - jpgwire.setMetricsRecorder({ addBytesRead(bytes) { context.metrics.data_replicated_bytes.add(bytes); @@ -112,4 +89,29 @@ export class PostgresModule extends replication.ReplicationModule { // process.exit(1); // } } + + protected registerSupabaseAuth(context: system.ServiceContextContainer) { + const { configuration } = context; + // Register the Supabase key collector(s) + configuration.connections + ?.map((baseConfig) => { + if (baseConfig.type != types.POSTGRES_CONNECTION_TYPE) { + return; + } + try { + return this.resolveConfig(types.PostgresConnectionConfig.decode(baseConfig as any)); + } catch (ex) { + logger.warn('Failed to decode configuration in Postgres module initialization.', ex); + } + }) + .filter((c) => !!c) + .forEach((config) => { + const keyCollector = new SupabaseKeyCollector(config!); + context.lifeCycleEngine.withLifecycle(keyCollector, { + // Close the internal pool + stop: (collector) => collector.shutdown() + }); + configuration.client_keystore.collector.add(new auth.CachedKeyCollector(keyCollector)); + }); + } } diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index d1262b1e0..6a419d12b 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -24,11 +24,6 @@ export abstract class AbstractModule { */ public abstract initialize(context: ServiceContextContainer): Promise; - /** - * Finish processing any requests and gracefully shut down any resources managed by the module - */ - public abstract shutdown(): Promise; - /** * Terminate and clean up any resources managed by the module right away */ diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index 1aba2eecc..4df30c926 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -7,8 +7,6 @@ import { AbstractModule } from './AbstractModule.js'; export class ModuleManager { private readonly modules: Map = new Map(); - constructor() {} - public register(modules: AbstractModule[]) { for (const module of modules) { if (this.modules.has(module.name)) { @@ -25,12 +23,6 @@ export class ModuleManager { } } - async shutDown() { - for (const module of this.modules.values()) { - await module.shutdown(); - } - } - async tearDown() { for (const module of this.modules.values()) { await module.teardown(); diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 3bbf7e919..6c9742c8c 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -93,6 +93,4 @@ export abstract class ReplicationModule extends modules.AbstractModule { throw new Error(`Failed to validate Module ${this.name} configuration: ${valid.errors.join(', ')}`); } } - - public async shutdown(): Promise {} } diff --git a/service/src/metrics.ts b/service/src/metrics.ts index a2479b0c3..adf608dc5 100644 --- a/service/src/metrics.ts +++ b/service/src/metrics.ts @@ -34,6 +34,7 @@ export const registerMetrics = async (options: MetricsRegistrationOptions) => { if (modes.includes(MetricModes.REPLICATION)) { instance.configureReplicationMetrics(service_context.storage.bucketStorage); } - } + }, + stop: () => service_context.metrics.shutdown() }); }; From 01216ab5e712530b7f6c323c0f516f51f7bbcd36 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 7 Aug 2024 17:49:13 +0200 Subject: [PATCH 089/298] validation errors --- .../service-core/src/replication/core/ReplicationModule.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 6c9742c8c..5ba212850 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -66,9 +66,9 @@ export abstract class ReplicationModule extends modules.AbstractModule { try { const baseMatchingConfig = matchingConfig[0]; - const decodedConfig = this.configSchema().decode(baseMatchingConfig); // If validation fails, log the error and continue, no replication will happen for this data source - this.validateConfig(matchingConfig[0]); + this.validateConfig(baseMatchingConfig); + const decodedConfig = this.configSchema().decode(baseMatchingConfig); const replicationAdapter = this.createReplicationAdapter(decodedConfig); this.replicationAdapters.add(replicationAdapter); context.replicationEngine.register(replicationAdapter); @@ -83,6 +83,7 @@ export abstract class ReplicationModule extends modules.AbstractModule { private validateConfig(config: DataSourceConfig): void { const validator = schema .parseJSONSchema( + // This generates a schema for the encoded form of the codec t.generateJSONSchema(this.configSchema(), { allowAdditional: true, parsers: [types.configFile.portParser] }) ) .validator(); From d35e3472c7a9f353383f86c6da06de1d63727a79 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 8 Aug 2024 16:04:12 +0200 Subject: [PATCH 090/298] add export for types --- modules/module-postgres/package.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index 1fefad9f7..f83742c66 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -13,6 +13,18 @@ "build": "tsc -b", "clean": "rm -rf ./lib && tsc -b --clean" }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.js", + "default": "./dist/index.js" + }, + "./types": { + "import": "./dist/types/types.js", + "require": "./dist/types/types.js", + "default": "./dist/types/types.js" + } + }, "dependencies": { "@powersync/lib-services-framework": "workspace:*", "@powersync/service-core": "workspace:*", From a85140d935e1b0fe1adde51cd2cc67e8ee212ab6 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 12 Aug 2024 13:15:51 +0200 Subject: [PATCH 091/298] rename route api --- modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts | 2 +- modules/module-postgres/src/module/PostgresModule.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts index 2892397a4..7294ac1a4 100644 --- a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts @@ -7,7 +7,7 @@ import * as replication_utils from '../replication/replication-utils.js'; import * as types from '../types/types.js'; import * as pg_utils from '../utils/pgwire_utils.js'; -export class PostgresSyncAPIAdapter implements api.RouteAPI { +export class PostgresRouteAPIAdapter implements api.RouteAPI { protected pool: pgwire.PgClient; connectionTag: string; diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index f154f05ca..1be4dac20 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -5,7 +5,7 @@ import * as t from 'ts-codec'; import { logger } from '@powersync/lib-services-framework'; import * as types from '../types/types.js'; -import { PostgresSyncAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; +import { PostgresRouteAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; @@ -38,7 +38,7 @@ export class PostgresModule extends replication.ReplicationModule { } protected createSyncAPIAdapter(config: types.PostgresConnectionConfig): api.RouteAPI { - return new PostgresSyncAPIAdapter(this.resolveConfig(config)); + return new PostgresRouteAPIAdapter(this.resolveConfig(config)); } protected createReplicationAdapter(config: types.PostgresConnectionConfig): PostgresReplicationAdapter { From d144190f9216f3aa61a7fe012e5635099ff0002a Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 12 Aug 2024 13:28:31 +0200 Subject: [PATCH 092/298] rename PostgresAPI adapter --- .../{PostgresSyncAPIAdapter.ts => PostgresRouteAPIAdapter.ts} | 0 modules/module-postgres/src/module/PostgresModule.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename modules/module-postgres/src/api/{PostgresSyncAPIAdapter.ts => PostgresRouteAPIAdapter.ts} (100%) diff --git a/modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts similarity index 100% rename from modules/module-postgres/src/api/PostgresSyncAPIAdapter.ts rename to modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 1be4dac20..cce6d0401 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -5,7 +5,7 @@ import * as t from 'ts-codec'; import { logger } from '@powersync/lib-services-framework'; import * as types from '../types/types.js'; -import { PostgresRouteAPIAdapter } from '../api/PostgresSyncAPIAdapter.js'; +import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; From ebabbbde17a86d543108488fb27cbf4cfc6349b4 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 12 Aug 2024 16:20:33 +0200 Subject: [PATCH 093/298] add scaffolding for mysql module --- modules/module-mysql/LICENSE | 67 ++++ modules/module-mysql/README.md | 3 + modules/module-mysql/package.json | 41 +++ .../module-mysql/src/api/MySQLAPIAdapter.ts | 340 ++++++++++++++++++ modules/module-mysql/src/index.ts | 5 + .../module-mysql/src/module/MySQLModule.ts | 83 +++++ .../replication/MSSQLReplicationAdapter.ts | 44 +++ modules/module-mysql/src/types/types.ts | 110 ++++++ modules/module-mysql/tsconfig.json | 31 ++ .../src/replication/core/ReplicationModule.ts | 4 + pnpm-lock.yaml | 43 +++ service/package.json | 1 + service/src/entry.ts | 3 +- 13 files changed, 774 insertions(+), 1 deletion(-) create mode 100644 modules/module-mysql/LICENSE create mode 100644 modules/module-mysql/README.md create mode 100644 modules/module-mysql/package.json create mode 100644 modules/module-mysql/src/api/MySQLAPIAdapter.ts create mode 100644 modules/module-mysql/src/index.ts create mode 100644 modules/module-mysql/src/module/MySQLModule.ts create mode 100644 modules/module-mysql/src/replication/MSSQLReplicationAdapter.ts create mode 100644 modules/module-mysql/src/types/types.ts create mode 100644 modules/module-mysql/tsconfig.json diff --git a/modules/module-mysql/LICENSE b/modules/module-mysql/LICENSE new file mode 100644 index 000000000..c8efd46cc --- /dev/null +++ b/modules/module-mysql/LICENSE @@ -0,0 +1,67 @@ +# Functional Source License, Version 1.1, Apache 2.0 Future License + +## Abbreviation + +FSL-1.1-Apache-2.0 + +## Notice + +Copyright 2023-2024 Journey Mobile, Inc. + +## Terms and Conditions + +### Licensor ("We") + +The party offering the Software under these Terms and Conditions. + +### The Software + +The "Software" is each version of the software that we make available under these Terms and Conditions, as indicated by our inclusion of these Terms and Conditions with the Software. + +### License Grant + +Subject to your compliance with this License Grant and the Patents, Redistribution and Trademark clauses below, we hereby grant you the right to use, copy, modify, create derivative works, publicly perform, publicly display and redistribute the Software for any Permitted Purpose identified below. + +### Permitted Purpose + +A Permitted Purpose is any purpose other than a Competing Use. A Competing Use means making the Software available to others in a commercial product or service that: + +1. substitutes for the Software; +2. substitutes for any other product or service we offer using the Software that exists as of the date we make the Software available; or +3. offers the same or substantially similar functionality as the Software. + +Permitted Purposes specifically include using the Software: + +1. for your internal use and access; +2. for non-commercial education; +3. for non-commercial research; and +4. in connection with professional services that you provide to a licensee using the Software in accordance with these Terms and Conditions. + +### Patents + +To the extent your use for a Permitted Purpose would necessarily infringe our patents, the license grant above includes a license under our patents. If you make a claim against any party that the Software infringes or contributes to the infringement of any patent, then your patent license to the Software ends immediately. + +### Redistribution + +The Terms and Conditions apply to all copies, modifications and derivatives of the Software. +If you redistribute any copies, modifications or derivatives of the Software, you must include a copy of or a link to these Terms and Conditions and not remove any copyright notices provided in or with the Software. + +### Disclaimer + +THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT. +IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE. + +### Trademarks + +Except for displaying the License Details and identifying us as the origin of the Software, you have no right under these Terms and Conditions to use our trademarks, trade names, service marks or product names. + +## Grant of Future License + +We hereby irrevocably grant you an additional license to use the Software under the Apache License, Version 2.0 that is effective on the second anniversary of the date we make the Software available. On or after that date, you may use the Software under the Apache License, Version 2.0, in which case the following will apply: + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/modules/module-mysql/README.md b/modules/module-mysql/README.md new file mode 100644 index 000000000..f17e382c9 --- /dev/null +++ b/modules/module-mysql/README.md @@ -0,0 +1,3 @@ +# PowerSync MSSQL Module + +This is a module which provides MSSQL replication to PowerSync diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json new file mode 100644 index 000000000..9b0680df6 --- /dev/null +++ b/modules/module-mysql/package.json @@ -0,0 +1,41 @@ +{ + "name": "@powersync/service-module-mysql", + "repository": "https://github.com/powersync-ja/powersync-service", + "types": "dist/index.d.ts", + "version": "0.0.1", + "license": "FSL-1.1-Apache-2.0", + "main": "dist/index.js", + "type": "module", + "scripts": { + "build": "tsc -b", + "clean": "rm -rf ./lib && tsc -b --clean" + }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.js", + "default": "./dist/index.js" + }, + "./types": { + "import": "./dist/types/types.js", + "require": "./dist/types/types.js", + "default": "./dist/types/types.js" + } + }, + "dependencies": { + "@powersync/lib-services-framework": "workspace:*", + "@powersync/service-core": "workspace:*", + "@powersync/service-jsonbig": "workspace:*", + "@powersync/service-sync-rules": "workspace:*", + "@powersync/service-types": "workspace:*", + "ts-codec": "^1.2.2", + "uuid": "^9.0.1", + "uri-js": "^4.4.1" + }, + "devDependencies": { + "@types/uuid": "^9.0.4", + "typescript": "^5.2.2", + "vitest": "^0.34.6", + "vite-tsconfig-paths": "^4.3.2" + } +} diff --git a/modules/module-mysql/src/api/MySQLAPIAdapter.ts b/modules/module-mysql/src/api/MySQLAPIAdapter.ts new file mode 100644 index 000000000..82afcad9d --- /dev/null +++ b/modules/module-mysql/src/api/MySQLAPIAdapter.ts @@ -0,0 +1,340 @@ +import { api } from '@powersync/service-core'; + +import * as sync_rules from '@powersync/service-sync-rules'; +import * as service_types from '@powersync/service-types'; +import * as types from '../types/types.js'; + +export class MySQLAPIAdapter implements api.RouteAPI { + constructor(protected config: types.ResolvedConnectionConfig) {} + + async shutdown(): Promise {} + + async getSourceConfig(): Promise { + return this.config; + } + + async getConnectionStatus(): Promise { + throw new Error('not implemented'); + // const base = { + // id: this.config.id, + // uri: types.baseUri(this.config) + // }; + // try { + // await pg_utils.retriedQuery(this.pool, `SELECT 'PowerSync connection test'`); + // } catch (e) { + // return { + // ...base, + // connected: false, + // errors: [{ level: 'fatal', message: e.message }] + // }; + // } + // try { + // await replication_utils.checkSourceConfiguration(this.pool); + // } catch (e) { + // return { + // ...base, + // connected: true, + // errors: [{ level: 'fatal', message: e.message }] + // }; + // } + // return { + // ...base, + // connected: true, + // errors: [] + // }; + } + + async executeQuery(query: string, params: any[]): Promise { + throw new Error('not implemented'); + + // if (!this.config.debug_api) { + // return service_types.internal_routes.ExecuteSqlResponse.encode({ + // results: { + // columns: [], + // rows: [] + // }, + // success: false, + // error: 'SQL querying is not enabled' + // }); + // } + // try { + // const result = await this.pool.query({ + // statement: query, + // params: params.map(pg_utils.autoParameter) + // }); + // return service_types.internal_routes.ExecuteSqlResponse.encode({ + // success: true, + // results: { + // columns: result.columns.map((c) => c.name), + // rows: result.rows.map((row) => { + // return row.map((value) => { + // const sqlValue = sync_rules.toSyncRulesValue(value); + // if (typeof sqlValue == 'bigint') { + // return Number(value); + // } else if (sync_rules.isJsonValue(sqlValue)) { + // return sqlValue; + // } else { + // return null; + // } + // }); + // }) + // } + // }); + // } catch (e) { + // return service_types.internal_routes.ExecuteSqlResponse.encode({ + // results: { + // columns: [], + // rows: [] + // }, + // success: false, + // error: e.message + // }); + // } + } + + async getDebugTablesInfo( + tablePatterns: sync_rules.TablePattern[], + sqlSyncRules: sync_rules.SqlSyncRules + ): Promise { + throw new Error('not implemented'); + + // let result: api.PatternResult[] = []; + // for (let tablePattern of tablePatterns) { + // const schema = tablePattern.schema; + // let patternResult: api.PatternResult = { + // schema: schema, + // pattern: tablePattern.tablePattern, + // wildcard: tablePattern.isWildcard + // }; + // result.push(patternResult); + // if (tablePattern.isWildcard) { + // patternResult.tables = []; + // const prefix = tablePattern.tablePrefix; + // const results = await pg_utils.retriedQuery(this.pool, { + // statement: `SELECT c.oid AS relid, c.relname AS table_name + // FROM pg_class c + // JOIN pg_namespace n ON n.oid = c.relnamespace + // WHERE n.nspname = $1 + // AND c.relkind = 'r' + // AND c.relname LIKE $2`, + // params: [ + // { type: 'varchar', value: schema }, + // { type: 'varchar', value: tablePattern.tablePattern } + // ] + // }); + // for (let row of pgwire.pgwireRows(results)) { + // const name = row.table_name as string; + // const relationId = row.relid as number; + // if (!name.startsWith(prefix)) { + // continue; + // } + // const details = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules); + // patternResult.tables.push(details); + // } + // } else { + // const results = await pg_utils.retriedQuery(this.pool, { + // statement: `SELECT c.oid AS relid, c.relname AS table_name + // FROM pg_class c + // JOIN pg_namespace n ON n.oid = c.relnamespace + // WHERE n.nspname = $1 + // AND c.relkind = 'r' + // AND c.relname = $2`, + // params: [ + // { type: 'varchar', value: schema }, + // { type: 'varchar', value: tablePattern.tablePattern } + // ] + // }); + // if (results.rows.length == 0) { + // // Table not found + // const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null, sqlSyncRules); + // patternResult.table = details; + // } else { + // const row = pgwire.pgwireRows(results)[0]; + // const name = row.table_name as string; + // const relationId = row.relid as number; + // patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules); + // } + // } + // } + // return result; + } + + protected async getDebugTableInfo( + tablePattern: sync_rules.TablePattern, + name: string, + relationId: number | null, + syncRules: sync_rules.SqlSyncRules + ): Promise { + throw new Error('not implemented'); + + // const schema = tablePattern.schema; + // let id_columns_result: replication_utils.ReplicaIdentityResult | undefined = undefined; + // let id_columns_error = null; + // if (relationId != null) { + // try { + // id_columns_result = await replication_utils.getReplicationIdentityColumns(this.pool, relationId); + // } catch (e) { + // id_columns_error = { level: 'fatal', message: e.message }; + // } + // } + // const id_columns = id_columns_result?.replicationColumns ?? []; + // const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); + // const syncData = syncRules.tableSyncsData(sourceTable); + // const syncParameters = syncRules.tableSyncsParameters(sourceTable); + // if (relationId == null) { + // return { + // schema: schema, + // name: name, + // pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + // replication_id: [], + // data_queries: syncData, + // parameter_queries: syncParameters, + // // Also + // errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] + // }; + // } + // if (id_columns.length == 0 && id_columns_error == null) { + // let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; + // if (id_columns_result?.replicationIdentity == 'default') { + // message += ' Configure a primary key on the table.'; + // } + // id_columns_error = { level: 'fatal', message }; + // } + // let selectError = null; + // try { + // await pg_utils.retriedQuery(this.pool, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); + // } catch (e) { + // selectError = { level: 'fatal', message: e.message }; + // } + // let replicateError = null; + // const publications = await pg_utils.retriedQuery(this.pool, { + // statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, + // params: [ + // { type: 'varchar', value: this.publication_name }, + // { type: 'varchar', value: tablePattern.schema }, + // { type: 'varchar', value: name } + // ] + // }); + // if (publications.rows.length == 0) { + // replicateError = { + // level: 'fatal', + // message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` + // }; + // } + // return { + // schema: schema, + // name: name, + // pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + // replication_id: id_columns.map((c) => c.name), + // data_queries: syncData, + // parameter_queries: syncParameters, + // errors: [id_columns_error, selectError, replicateError].filter( + // (error) => error != null + // ) as service_types.ReplicationError[] + // }; + } + + async getReplicationLag(syncRulesId: string): Promise { + throw new Error('not implemented'); + + // const results = await pg_utils.retriedQuery(this.pool, { + // statement: `SELECT + // slot_name, + // confirmed_flush_lsn, + // pg_current_wal_lsn(), + // (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance + // FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, + // params: [{ type: 'varchar', value: syncRulesId }] + // }); + // const [row] = pgwire.pgwireRows(results); + // if (row) { + // return Number(row.lsn_distance); + // } + + throw new Error(`Could not determine replication lag for slot ${syncRulesId}`); + } + + async getReplicationHead(): Promise { + throw new Error('not implemented'); + + // const [{ lsn }] = pgwire.pgwireRows( + // await pg_utils.retriedQuery(this.pool, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) + // ); + // return String(lsn); + } + + async getConnectionSchema(): Promise { + throw new Error('not implemented'); + + // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts + // const results = await pg_utils.retriedQuery( + // this.pool, + // `SELECT + // tbl.schemaname, + // tbl.tablename, + // tbl.quoted_name, + // json_agg(a ORDER BY attnum) as columns + // FROM + // ( + // SELECT + // n.nspname as schemaname, + // c.relname as tablename, + // (quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as quoted_name + // FROM + // pg_catalog.pg_class c + // JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace + // WHERE + // c.relkind = 'r' + // AND n.nspname not in ('information_schema', 'pg_catalog', 'pg_toast') + // AND n.nspname not like 'pg_temp_%' + // AND n.nspname not like 'pg_toast_temp_%' + // AND c.relnatts > 0 + // AND has_schema_privilege(n.oid, 'USAGE') = true + // AND has_table_privilege(quote_ident(n.nspname) || '.' || quote_ident(c.relname), 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') = true + // ) as tbl + // LEFT JOIN ( + // SELECT + // attrelid, + // attname, + // format_type(atttypid, atttypmod) as data_type, + // (SELECT typname FROM pg_catalog.pg_type WHERE oid = atttypid) as pg_type, + // attnum, + // attisdropped + // FROM + // pg_attribute + // ) as a ON ( + // a.attrelid = tbl.quoted_name::regclass + // AND a.attnum > 0 + // AND NOT a.attisdropped + // AND has_column_privilege(tbl.quoted_name, a.attname, 'SELECT, INSERT, UPDATE, REFERENCES') + // ) + // GROUP BY schemaname, tablename, quoted_name` + // ); + // const rows = pgwire.pgwireRows(results); + // let schemas: Record = {}; + // for (let row of rows) { + // const schema = (schemas[row.schemaname] ??= { + // name: row.schemaname, + // tables: [] + // }); + // const table = { + // name: row.tablename, + // columns: [] as any[] + // }; + // schema.tables.push(table); + // const columnInfo = JSON.parse(row.columns); + // for (let column of columnInfo) { + // let pg_type = column.pg_type as string; + // if (pg_type.startsWith('_')) { + // pg_type = `${pg_type.substring(1)}[]`; + // } + // table.columns.push({ + // name: column.attname, + // type: column.data_type, + // pg_type: pg_type + // }); + // } + // } + // return Object.values(schemas); + } +} diff --git a/modules/module-mysql/src/index.ts b/modules/module-mysql/src/index.ts new file mode 100644 index 000000000..a6426f95a --- /dev/null +++ b/modules/module-mysql/src/index.ts @@ -0,0 +1,5 @@ +import { MySQLModule } from './module/MySQLModule.js'; + +export const module = new MySQLModule(); + +export default module; diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts new file mode 100644 index 000000000..da5da7591 --- /dev/null +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -0,0 +1,83 @@ +import { api, replication, system } from '@powersync/service-core'; +import * as t from 'ts-codec'; + +import { MySQLAPIAdapter } from '../api/MySQLAPIAdapter.js'; +import { MSSQLReplicationAdapter } from '../replication/MSSQLReplicationAdapter.js'; +import * as types from '../types/types.js'; + +export class MySQLModule extends replication.ReplicationModule { + constructor() { + super({ + name: 'MySQL', + type: types.MSSQL_CONNECTION_TYPE + }); + } + + protected configSchema(): t.AnyCodec { + // Intersection types have some limitations in codec typing + return types.MSSQLConnectionConfig; + } + + async initialize(context: system.ServiceContextContainer): Promise { + await super.initialize(context); + + // jpgwire.setMetricsRecorder({ + // addBytesRead(bytes) { + // context.metrics.data_replicated_bytes.add(bytes); + // } + // }); + } + + protected createSyncAPIAdapter(config: types.MSSQLConnectionConfig): api.RouteAPI { + return new MySQLAPIAdapter(this.resolveConfig(config)); + } + + protected createReplicationAdapter(config: types.MSSQLConnectionConfig): MSSQLReplicationAdapter { + return new MSSQLReplicationAdapter(this.resolveConfig(config)); + } + + /** + * Combines base config with normalized connection settings + */ + private resolveConfig(config: types.MSSQLConnectionConfig): types.ResolvedConnectionConfig { + return { + ...config, + ...types.normalizeConnectionConfig(config) + }; + } + + async teardown(): Promise { + // TODO this needs the service context to operate. + // Should this keep a refference? + // const mongoDB = storage.createPowerSyncMongo(context.configuration.storage); + // try { + // // TODO this should not be necessary since the service context + // // has already been initialized. + // // However we need a direct mongo connection for this. + // // Maybe we can add termination methods to the storage. + // // TODO improve this when other storage methods or connections are implemented + // logger.info(`Waiting for auth`); + // await db.mongo.waitForAuth(mongoDB.db); + // logger.info(`Terminating replication slots`); + // const connections = (context.configuration.connections ?? []) + // .filter((c) => c.type == 'postgresql') + // .map((c) => types.PostgresConnectionConfig.decode(c as any)); + // for (const connection of connections) { + // await terminateReplicators(context.storage, this.resolveConfig(connection)); + // } + // const database = mongoDB.db; + // logger.info(`Dropping database ${database.namespace}`); + // await database.dropDatabase(); + // logger.info(`Done`); + // await mongoDB.client.close(); + // // If there was an error connecting to postgress, the process may stay open indefinitely. + // // This forces an exit. + // // We do not consider those errors a teardown failure. + // process.exit(0); + // } catch (e) { + // logger.error(`Teardown failure`, e); + // await mongoDB.client.close(); + // process.exit(1); + // } + } +} diff --git a/modules/module-mysql/src/replication/MSSQLReplicationAdapter.ts b/modules/module-mysql/src/replication/MSSQLReplicationAdapter.ts new file mode 100644 index 000000000..5535de795 --- /dev/null +++ b/modules/module-mysql/src/replication/MSSQLReplicationAdapter.ts @@ -0,0 +1,44 @@ +import { replication, SourceTable } from '@powersync/service-core'; +import * as sync_rules from '@powersync/service-sync-rules'; + +import { ResolvedConnectionConfig } from '../types/types.js'; + +export class MSSQLReplicationAdapter implements replication.ReplicationAdapter { + constructor(protected config: ResolvedConnectionConfig) {} + resolveReplicationEntities(pattern: sync_rules.TablePattern): Promise { + throw new Error('Method not implemented.'); + } + count(entity: SourceTable): Promise { + throw new Error('Method not implemented.'); + } + initializeData(options: replication.InitializeDataOptions): Promise { + throw new Error('Method not implemented.'); + } + + name(): string { + return 'postgres'; + } + + shutdown(): Promise { + throw new Error('Method not implemented.'); + } + + checkPrerequisites(): Promise { + throw new Error('Method not implemented.'); + } + + toReplicationEntities(pattern: sync_rules.TablePattern): Promise { + throw new Error('Method not implemented.'); + } + + startReplication(options: replication.StartReplicationOptions): Promise { + throw new Error('Method not implemented.'); + } + cleanupReplication(syncRuleId: number): Promise { + throw new Error('Method not implemented.'); + } + + terminateReplication(): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/modules/module-mysql/src/types/types.ts b/modules/module-mysql/src/types/types.ts new file mode 100644 index 000000000..03bc23540 --- /dev/null +++ b/modules/module-mysql/src/types/types.ts @@ -0,0 +1,110 @@ +import * as service_types from '@powersync/service-types'; +import * as t from 'ts-codec'; +import * as urijs from 'uri-js'; + +export const MSSQL_CONNECTION_TYPE = 'mssql' as const; + +export interface NormalizedMSSQLConnectionConfig { + id: string; + tag: string; + + hostname: string; + port: number; + database: string; + + username: string; + password: string; + + // TODO other fields +} + +export const MSSQLConnectionConfig = service_types.configFile.dataSourceConfig.and( + t.object({ + type: t.literal(MSSQL_CONNECTION_TYPE), + /** Unique identifier for the connection - optional when a single connection is present. */ + id: t.string.optional(), + /** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */ + tag: t.string.optional(), + uri: t.string.optional(), + hostname: t.string.optional(), + port: service_types.configFile.portCodec.optional(), + username: t.string.optional(), + password: t.string.optional(), + database: t.string.optional() + }) +); + +/** + * Config input specified when starting services + */ +export type MSSQLConnectionConfig = t.Decoded; + +/** + * Resolved version of {@link PostgresConnectionConfig} + */ +export type ResolvedConnectionConfig = MSSQLConnectionConfig & NormalizedMSSQLConnectionConfig; + +/** + * Validate and normalize connection options. + * + * Returns destructured options. + */ +export function normalizeConnectionConfig(options: MSSQLConnectionConfig): NormalizedMSSQLConnectionConfig { + // TOOD + let uri: urijs.URIComponents; + if (options.uri) { + uri = urijs.parse(options.uri); + if (uri.scheme != 'postgresql' && uri.scheme != 'postgres') { + `Invalid URI - protocol must be postgresql, got ${uri.scheme}`; + } else if (uri.scheme != 'postgresql') { + uri.scheme = 'postgresql'; + } + } else { + uri = urijs.parse('postgresql:///'); + } + + const hostname = options.hostname ?? uri.host ?? ''; + const port = Number(options.port ?? uri.port ?? 5432); + + const database = options.database ?? uri.path?.substring(1) ?? ''; + + const [uri_username, uri_password] = (uri.userinfo ?? '').split(':'); + + const username = options.username ?? uri_username ?? ''; + const password = options.password ?? uri_password ?? ''; + + const sslmode = options.sslmode ?? 'verify-full'; // Configuration not supported via URI + const cacert = options.cacert; + + if (sslmode == 'verify-ca' && cacert == null) { + throw new Error('Explicit cacert is required for sslmode=verify-ca'); + } + + if (hostname == '') { + throw new Error(`hostname required`); + } + + if (username == '') { + throw new Error(`username required`); + } + + if (password == '') { + throw new Error(`password required`); + } + + if (database == '') { + throw new Error(`database required`); + } + + return { + id: options.id ?? 'default', + tag: options.tag ?? 'default', + + hostname, + port, + database, + + username, + password + }; +} diff --git a/modules/module-mysql/tsconfig.json b/modules/module-mysql/tsconfig.json new file mode 100644 index 000000000..9ceadec40 --- /dev/null +++ b/modules/module-mysql/tsconfig.json @@ -0,0 +1,31 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "esModuleInterop": true, + "skipLibCheck": true, + "sourceMap": true + }, + "include": ["src"], + "references": [ + { + "path": "../../packages/types" + }, + { + "path": "../../packages/jsonbig" + }, + { + "path": "../../packages/jpgwire" + }, + { + "path": "../../packages/sync-rules" + }, + { + "path": "../../packages/service-core" + }, + { + "path": "../../libs/lib-services" + } + ] +} diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 5ba212850..600e506a3 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -58,6 +58,10 @@ export abstract class ReplicationModule extends modules.AbstractModule { const matchingConfig = context.configuration.connections.filter((dataSource) => dataSource.type === this.type); + if (!matchingConfig.length) { + return; + } + if (matchingConfig.length > 1) { logger.warning( `Multiple data sources of type ${this.type} found in the configuration. Only the first will be used.` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd0ec2222..ec9a466f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,6 +94,46 @@ importers: specifier: ^0.34.6 version: 0.34.6 + modules/module-mysql: + dependencies: + '@powersync/lib-services-framework': + specifier: workspace:* + version: link:../../libs/lib-services + '@powersync/service-core': + specifier: workspace:* + version: link:../../packages/service-core + '@powersync/service-jsonbig': + specifier: workspace:* + version: link:../../packages/jsonbig + '@powersync/service-sync-rules': + specifier: workspace:* + version: link:../../packages/sync-rules + '@powersync/service-types': + specifier: workspace:* + version: link:../../packages/types + ts-codec: + specifier: ^1.2.2 + version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + devDependencies: + '@types/uuid': + specifier: ^9.0.4 + version: 9.0.8 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) + vitest: + specifier: ^0.34.6 + version: 0.34.6 + modules/module-postgres: dependencies: '@powersync/lib-services-framework': @@ -373,6 +413,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:* version: link:../packages/jsonbig + '@powersync/service-module-mysql': + specifier: workspace:* + version: link:../modules/module-mysql '@powersync/service-module-postgres': specifier: workspace:* version: link:../modules/module-postgres diff --git a/service/package.json b/service/package.json index daf5f882d..cd6b44490 100644 --- a/service/package.json +++ b/service/package.json @@ -17,6 +17,7 @@ "@powersync/service-core": "workspace:*", "@powersync/lib-services-framework": "workspace:*", "@powersync/service-module-postgres": "workspace:*", + "@powersync/service-module-mysql": "workspace:*", "@powersync/service-jpgwire": "workspace:*", "@powersync/service-jsonbig": "workspace:*", "@powersync/service-rsocket-router": "workspace:*", diff --git a/service/src/entry.ts b/service/src/entry.ts index d817f46fe..92435dbcc 100644 --- a/service/src/entry.ts +++ b/service/src/entry.ts @@ -1,5 +1,6 @@ import { container, ContainerImplementation } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; +import MySQLModule from '@powersync/service-module-mysql'; import PostgresModule from '@powersync/service-module-postgres'; import { startServer } from './runners/server.js'; @@ -12,7 +13,7 @@ container.registerDefaults(); container.register(ContainerImplementation.REPORTER, createSentryReporter()); const moduleManager = new core.modules.ModuleManager(); -moduleManager.register([PostgresModule]); +moduleManager.register([PostgresModule, MySQLModule]); // This is a bit of a hack. Commands such as the teardown command or even migrations might // want access to the ModuleManager in order to use modules container.register(core.ModuleManager, moduleManager); From 527d911bf3a55c25556d81c3c82428b1887c515c Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 12 Aug 2024 16:22:06 +0200 Subject: [PATCH 094/298] dont load modules which arent present --- .../service-core/src/replication/core/ReplicationModule.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 5ba212850..9e47d01de 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -57,6 +57,10 @@ export abstract class ReplicationModule extends modules.AbstractModule { } const matchingConfig = context.configuration.connections.filter((dataSource) => dataSource.type === this.type); + if (!matchingConfig.length) { + // This module is needed given the config + return; + } if (matchingConfig.length > 1) { logger.warning( From 7ee096e8a7d6794652763f56517c06607046fc29 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 12 Aug 2024 16:54:39 +0200 Subject: [PATCH 095/298] add more mysql functions --- modules/module-mysql/package.json | 9 ++- .../module-mysql/src/api/MySQLAPIAdapter.ts | 16 +++- .../module-mysql/src/module/MySQLModule.ts | 10 +-- modules/module-mysql/src/types/types.ts | 38 +++------ modules/module-mysql/src/utils/mysql_utils.ts | 54 +++++++++++++ pnpm-lock.yaml | 78 ++++++++++++++++++- 6 files changed, 164 insertions(+), 41 deletions(-) create mode 100644 modules/module-mysql/src/utils/mysql_utils.ts diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 9b0680df6..59e120b6d 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -28,14 +28,15 @@ "@powersync/service-jsonbig": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", + "mysql2": "^3.11.0", "ts-codec": "^1.2.2", - "uuid": "^9.0.1", - "uri-js": "^4.4.1" + "uri-js": "^4.4.1", + "uuid": "^9.0.1" }, "devDependencies": { "@types/uuid": "^9.0.4", "typescript": "^5.2.2", - "vitest": "^0.34.6", - "vite-tsconfig-paths": "^4.3.2" + "vite-tsconfig-paths": "^4.3.2", + "vitest": "^0.34.6" } } diff --git a/modules/module-mysql/src/api/MySQLAPIAdapter.ts b/modules/module-mysql/src/api/MySQLAPIAdapter.ts index 82afcad9d..4fae30cc1 100644 --- a/modules/module-mysql/src/api/MySQLAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLAPIAdapter.ts @@ -2,12 +2,24 @@ import { api } from '@powersync/service-core'; import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; +import mysql from 'mysql2/promise'; import * as types from '../types/types.js'; export class MySQLAPIAdapter implements api.RouteAPI { - constructor(protected config: types.ResolvedConnectionConfig) {} + protected pool: mysql.Pool; - async shutdown(): Promise {} + constructor(protected config: types.ResolvedConnectionConfig) { + this.pool = mysql.createPool({ + host: config.hostname, + user: config.username, + password: config.password, + database: config.database + }); + } + + async shutdown(): Promise { + return this.pool.end(); + } async getSourceConfig(): Promise { return this.config; diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts index da5da7591..7a6427337 100644 --- a/modules/module-mysql/src/module/MySQLModule.ts +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -9,13 +9,13 @@ export class MySQLModule extends replication.ReplicationModule { constructor() { super({ name: 'MySQL', - type: types.MSSQL_CONNECTION_TYPE + type: types.MYSQL_CONNECTION_TYPE }); } protected configSchema(): t.AnyCodec { // Intersection types have some limitations in codec typing - return types.MSSQLConnectionConfig; + return types.MySQLConnectionConfig; } async initialize(context: system.ServiceContextContainer): Promise { @@ -28,18 +28,18 @@ export class MySQLModule extends replication.ReplicationModule { // }); } - protected createSyncAPIAdapter(config: types.MSSQLConnectionConfig): api.RouteAPI { + protected createSyncAPIAdapter(config: types.MySQLConnectionConfig): api.RouteAPI { return new MySQLAPIAdapter(this.resolveConfig(config)); } - protected createReplicationAdapter(config: types.MSSQLConnectionConfig): MSSQLReplicationAdapter { + protected createReplicationAdapter(config: types.MySQLConnectionConfig): MSSQLReplicationAdapter { return new MSSQLReplicationAdapter(this.resolveConfig(config)); } /** * Combines base config with normalized connection settings */ - private resolveConfig(config: types.MSSQLConnectionConfig): types.ResolvedConnectionConfig { + private resolveConfig(config: types.MySQLConnectionConfig): types.ResolvedConnectionConfig { return { ...config, ...types.normalizeConnectionConfig(config) diff --git a/modules/module-mysql/src/types/types.ts b/modules/module-mysql/src/types/types.ts index 03bc23540..d6016a9f7 100644 --- a/modules/module-mysql/src/types/types.ts +++ b/modules/module-mysql/src/types/types.ts @@ -2,9 +2,9 @@ import * as service_types from '@powersync/service-types'; import * as t from 'ts-codec'; import * as urijs from 'uri-js'; -export const MSSQL_CONNECTION_TYPE = 'mssql' as const; +export const MYSQL_CONNECTION_TYPE = 'mysql' as const; -export interface NormalizedMSSQLConnectionConfig { +export interface NormalizedMySQLConnectionConfig { id: string; tag: string; @@ -14,17 +14,11 @@ export interface NormalizedMSSQLConnectionConfig { username: string; password: string; - - // TODO other fields } -export const MSSQLConnectionConfig = service_types.configFile.dataSourceConfig.and( +export const MySQLConnectionConfig = service_types.configFile.dataSourceConfig.and( t.object({ - type: t.literal(MSSQL_CONNECTION_TYPE), - /** Unique identifier for the connection - optional when a single connection is present. */ - id: t.string.optional(), - /** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */ - tag: t.string.optional(), + type: t.literal(MYSQL_CONNECTION_TYPE), uri: t.string.optional(), hostname: t.string.optional(), port: service_types.configFile.portCodec.optional(), @@ -37,34 +31,31 @@ export const MSSQLConnectionConfig = service_types.configFile.dataSourceConfig.a /** * Config input specified when starting services */ -export type MSSQLConnectionConfig = t.Decoded; +export type MySQLConnectionConfig = t.Decoded; /** - * Resolved version of {@link PostgresConnectionConfig} + * Resolved version of {@link MySQLConnectionConfig} */ -export type ResolvedConnectionConfig = MSSQLConnectionConfig & NormalizedMSSQLConnectionConfig; +export type ResolvedConnectionConfig = MySQLConnectionConfig & NormalizedMySQLConnectionConfig; /** * Validate and normalize connection options. * * Returns destructured options. */ -export function normalizeConnectionConfig(options: MSSQLConnectionConfig): NormalizedMSSQLConnectionConfig { - // TOOD +export function normalizeConnectionConfig(options: MySQLConnectionConfig): NormalizedMySQLConnectionConfig { let uri: urijs.URIComponents; if (options.uri) { uri = urijs.parse(options.uri); - if (uri.scheme != 'postgresql' && uri.scheme != 'postgres') { - `Invalid URI - protocol must be postgresql, got ${uri.scheme}`; - } else if (uri.scheme != 'postgresql') { - uri.scheme = 'postgresql'; + if (uri.scheme != 'mysql') { + throw new Error(`Invalid URI - protocol must be mysql, got ${uri.scheme}`); } } else { uri = urijs.parse('postgresql:///'); } const hostname = options.hostname ?? uri.host ?? ''; - const port = Number(options.port ?? uri.port ?? 5432); + const port = Number(options.port ?? uri.port ?? 3306); const database = options.database ?? uri.path?.substring(1) ?? ''; @@ -73,13 +64,6 @@ export function normalizeConnectionConfig(options: MSSQLConnectionConfig): Norma const username = options.username ?? uri_username ?? ''; const password = options.password ?? uri_password ?? ''; - const sslmode = options.sslmode ?? 'verify-full'; // Configuration not supported via URI - const cacert = options.cacert; - - if (sslmode == 'verify-ca' && cacert == null) { - throw new Error('Explicit cacert is required for sslmode=verify-ca'); - } - if (hostname == '') { throw new Error(`hostname required`); } diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts new file mode 100644 index 000000000..2773bc881 --- /dev/null +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -0,0 +1,54 @@ +import { logger } from '@powersync/lib-services-framework'; +import mysql from 'mysql2/promise'; + +export type RetiredMySQLQueryOptions = { + db: mysql.Pool; + query: string; + params?: any[]; + retries?: number; +}; + +/** + * Retry a simple query - up to 2 attempts total. + */ +export async function retriedMysqlQuery(options: RetiredMySQLQueryOptions) { + const { db, query, params = [], retries = 2 } = options; + for (let tries = retries; ; tries--) { + try { + logger.debug(`Executing query: ${query}`); + return db.query(query, params); + } catch (e) { + if (tries == 1) { + throw e; + } + logger.warn('Query error, retrying', e); + } + } +} + +/** + * Transforms a GTID into a comparable string format, ensuring lexicographical + * order aligns with the GTID's relative age. This assumes that all GTIDs + * have the same server ID. + * + * @param gtid - The GTID string in the format 'server_id:transaction_ranges' + * @returns A comparable string in the format 'padded_end_transaction|original_gtid' + */ +export function gtidMakeComparable(gtid: string): string { + const [serverId, transactionRanges] = gtid.split(':'); + + let maxTransactionId = 0; + + for (const range of transactionRanges.split(',')) { + const [start, end] = range.split('-'); + maxTransactionId = Math.max(maxTransactionId, parseInt(start, 10), parseInt(end || start, 10)); + } + + const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); + return `${paddedTransactionId}|${gtid}`; +} + +export async function readMasterComparableGtid(db: mysql.Pool): Promise { + const results = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); + return gtidMakeComparable(results[0].length > 0 ? `${results[0][0]['GTID']}` : '0:0'); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec9a466f6..5dc1c8c9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,6 +111,9 @@ importers: '@powersync/service-types': specifier: workspace:* version: link:../../packages/types + mysql2: + specifier: ^3.11.0 + version: 3.11.0 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -1482,6 +1485,10 @@ packages: avvio@8.3.2: resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + aws-ssl-profiles@1.1.1: + resolution: {integrity: sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ==} + engines: {node: '>= 6.0.0'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1757,6 +1764,10 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -1981,6 +1992,9 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2213,6 +2227,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -2360,6 +2377,9 @@ packages: resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} engines: {node: '>= 12.0.0'} + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + lossless-json@2.0.11: resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} @@ -2380,6 +2400,10 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lru-cache@8.0.5: + resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==} + engines: {node: '>=16.14'} + magic-string@0.30.10: resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} @@ -2538,6 +2562,14 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mysql2@3.11.0: + resolution: {integrity: sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3094,6 +3126,9 @@ packages: engines: {node: '>=10'} hasBin: true + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -3215,6 +3250,10 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + ssri@10.0.6: resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -4565,7 +4604,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4573,7 +4612,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -4848,6 +4887,8 @@ snapshots: '@fastify/error': 3.4.1 fastq: 1.17.1 + aws-ssl-profiles@1.1.1: {} + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -5179,6 +5220,8 @@ snapshots: delegates@1.0.0: {} + denque@2.1.0: {} + detect-indent@6.1.0: {} diff-sequences@29.6.3: {} @@ -5431,6 +5474,10 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + get-caller-file@2.0.5: {} get-func-name@2.0.2: {} @@ -5557,7 +5604,6 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - optional: true ieee754@1.2.1: {} @@ -5668,6 +5714,8 @@ snapshots: is-path-inside@3.0.3: {} + is-property@1.0.2: {} + is-stream@2.0.1: {} is-subdir@1.2.0: @@ -5799,6 +5847,8 @@ snapshots: safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 + long@5.2.3: {} + lossless-json@2.0.11: {} loupe@2.3.7: @@ -5816,6 +5866,8 @@ snapshots: lru-cache@7.18.3: {} + lru-cache@8.0.5: {} + magic-string@0.30.10: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -5983,6 +6035,22 @@ snapshots: mute-stream@1.0.0: {} + mysql2@3.11.0: + dependencies: + aws-ssl-profiles: 1.1.1 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.2.3 + lru-cache: 8.0.5 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + nanoid@3.3.7: {} nearley@2.20.1: @@ -6619,6 +6687,8 @@ snapshots: semver@7.6.2: {} + seq-queue@0.0.5: {} + set-blocking@2.0.0: {} set-cookie-parser@2.6.0: {} @@ -6734,6 +6804,8 @@ snapshots: sprintf-js@1.1.3: {} + sqlstring@2.3.3: {} + ssri@10.0.6: dependencies: minipass: 7.1.2 From 729013d07bedcea74cfb5ae986e1a2678eb615a2 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 12 Aug 2024 17:42:05 +0200 Subject: [PATCH 096/298] add execute sql api --- .../module-mysql/src/api/MySQLAPIAdapter.ts | 92 ++++++++++--------- tsconfig.json | 6 ++ 2 files changed, 53 insertions(+), 45 deletions(-) diff --git a/modules/module-mysql/src/api/MySQLAPIAdapter.ts b/modules/module-mysql/src/api/MySQLAPIAdapter.ts index 4fae30cc1..a1fdd430f 100644 --- a/modules/module-mysql/src/api/MySQLAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLAPIAdapter.ts @@ -57,51 +57,53 @@ export class MySQLAPIAdapter implements api.RouteAPI { } async executeQuery(query: string, params: any[]): Promise { - throw new Error('not implemented'); - - // if (!this.config.debug_api) { - // return service_types.internal_routes.ExecuteSqlResponse.encode({ - // results: { - // columns: [], - // rows: [] - // }, - // success: false, - // error: 'SQL querying is not enabled' - // }); - // } - // try { - // const result = await this.pool.query({ - // statement: query, - // params: params.map(pg_utils.autoParameter) - // }); - // return service_types.internal_routes.ExecuteSqlResponse.encode({ - // success: true, - // results: { - // columns: result.columns.map((c) => c.name), - // rows: result.rows.map((row) => { - // return row.map((value) => { - // const sqlValue = sync_rules.toSyncRulesValue(value); - // if (typeof sqlValue == 'bigint') { - // return Number(value); - // } else if (sync_rules.isJsonValue(sqlValue)) { - // return sqlValue; - // } else { - // return null; - // } - // }); - // }) - // } - // }); - // } catch (e) { - // return service_types.internal_routes.ExecuteSqlResponse.encode({ - // results: { - // columns: [], - // rows: [] - // }, - // success: false, - // error: e.message - // }); - // } + if (!this.config.debug_enabled) { + return service_types.internal_routes.ExecuteSqlResponse.encode({ + results: { + columns: [], + rows: [] + }, + success: false, + error: 'SQL querying is not enabled' + }); + } + try { + const [results, fields] = await this.pool.query(query, params); + return service_types.internal_routes.ExecuteSqlResponse.encode({ + success: true, + results: { + columns: fields.map((c) => c.name), + rows: results.map((row) => { + /** + * Row will be in the format: + * @rows: [ { test: 2 } ] + */ + return fields.map((c) => { + const value = row[c.name]; + const sqlValue = sync_rules.toSyncRulesValue(value); + if (typeof sqlValue == 'bigint') { + return Number(value); + } else if (value instanceof Date) { + return value.toISOString(); + } else if (sync_rules.isJsonValue(sqlValue)) { + return sqlValue; + } else { + return null; + } + }); + }) + } + }); + } catch (e) { + return service_types.internal_routes.ExecuteSqlResponse.encode({ + results: { + columns: [], + rows: [] + }, + success: false, + error: e.message + }); + } } async getDebugTablesInfo( diff --git a/tsconfig.json b/tsconfig.json index bce764b1d..3c19076fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,12 @@ { "path": "./packages/sync-rules" }, + { + "path": "./modules/module-postgres" + }, + { + "path": "./modules/module-mysql" + }, { "path": "./libs/lib-services" }, From 1f007c3f8eac812a78b59931f7da2dcd7cdc1ff6 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 13 Aug 2024 17:31:57 +0200 Subject: [PATCH 097/298] fix conditional replication and router engines. Add more API functions --- .../module-mysql/src/api/MySQLAPIAdapter.ts | 166 ++++++++++++------ modules/module-mysql/src/utils/mysql_utils.ts | 41 ++++- packages/service-core/src/api/diagnostics.ts | 11 +- .../src/replication/core/ReplicationModule.ts | 4 +- .../src/routes/configure-fastify.ts | 33 ++-- .../src/routes/configure-rsocket.ts | 9 +- .../src/routes/endpoints/sync-rules.ts | 5 +- packages/service-core/src/routes/router.ts | 8 +- .../service-core/src/system/ServiceContext.ts | 12 +- service/src/runners/server.ts | 4 +- 10 files changed, 205 insertions(+), 88 deletions(-) diff --git a/modules/module-mysql/src/api/MySQLAPIAdapter.ts b/modules/module-mysql/src/api/MySQLAPIAdapter.ts index a1fdd430f..4cec0a143 100644 --- a/modules/module-mysql/src/api/MySQLAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLAPIAdapter.ts @@ -4,6 +4,7 @@ import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; import * as types from '../types/types.js'; +import { checkSourceConfiguration, readMasterComparableGtid, retriedQuery } from '../utils/mysql_utils.js'; export class MySQLAPIAdapter implements api.RouteAPI { protected pool: mysql.Pool; @@ -26,34 +27,43 @@ export class MySQLAPIAdapter implements api.RouteAPI { } async getConnectionStatus(): Promise { - throw new Error('not implemented'); - // const base = { - // id: this.config.id, - // uri: types.baseUri(this.config) - // }; - // try { - // await pg_utils.retriedQuery(this.pool, `SELECT 'PowerSync connection test'`); - // } catch (e) { - // return { - // ...base, - // connected: false, - // errors: [{ level: 'fatal', message: e.message }] - // }; - // } - // try { - // await replication_utils.checkSourceConfiguration(this.pool); - // } catch (e) { - // return { - // ...base, - // connected: true, - // errors: [{ level: 'fatal', message: e.message }] - // }; - // } - // return { - // ...base, - // connected: true, - // errors: [] - // }; + const base = { + id: this.config.id, + uri: `mysql://${this.config.hostname}:${this.config.port}/${this.config.database}` + }; + try { + await retriedQuery({ + db: this.pool, + query: `SELECT 'PowerSync connection test'` + }); + } catch (e) { + return { + ...base, + connected: false, + errors: [{ level: 'fatal', message: `${e.code} - message: ${e.message}` }] + }; + } + try { + const errors = await checkSourceConfiguration(this.pool); + if (errors.length) { + return { + ...base, + connected: true, + errors: errors.map((e) => ({ level: 'fatal', message: e })) + }; + } + } catch (e) { + return { + ...base, + connected: true, + errors: [{ level: 'fatal', message: e.message }] + }; + } + return { + ...base, + connected: true, + errors: [] + }; } async executeQuery(query: string, params: any[]): Promise { @@ -110,7 +120,62 @@ export class MySQLAPIAdapter implements api.RouteAPI { tablePatterns: sync_rules.TablePattern[], sqlSyncRules: sync_rules.SqlSyncRules ): Promise { - throw new Error('not implemented'); + let result: api.PatternResult[] = []; + + for (let tablePattern of tablePatterns) { + const schema = tablePattern.schema; + let patternResult: api.PatternResult = { + schema: schema, + pattern: tablePattern.tablePattern, + wildcard: tablePattern.isWildcard + }; + result.push(patternResult); + + if (tablePattern.isWildcard) { + patternResult.tables = []; + const prefix = tablePattern.tablePrefix; + + const [results] = await this.pool.query( + `SELECT TABLE_NAME AS table_name + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ? + AND TABLE_NAME LIKE ?`, + [schema, tablePattern.tablePattern] + ); + + for (let row of results) { + const name = row.table_name as string; + + if (!name.startsWith(prefix)) { + continue; + } + + // const details = await getDebugTableInfo(tablePattern, name, sqlSyncRules); + // patternResult.tables.push(details); + } + } else { + const [results] = await this.pool.query( + `SELECT TABLE_NAME AS table_name + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_SCHEMA = ? + AND TABLE_NAME = ?`, + [schema, tablePattern.tablePattern] + ); + + // if (results.length == 0) { + // // Table not found + // const details = await getDebugTableInfo(tablePattern, tablePattern.name, sqlSyncRules); + // patternResult.table = details; + // } else { + // const row = results[0]; + // const name = row.table_name as string; + + // patternResult.table = await getDebugTableInfo(tablePattern, name, sqlSyncRules); + // } + } + } + + return result; // let result: api.PatternResult[] = []; // for (let tablePattern of tablePatterns) { @@ -249,32 +314,35 @@ export class MySQLAPIAdapter implements api.RouteAPI { } async getReplicationLag(syncRulesId: string): Promise { - throw new Error('not implemented'); + const [binLogFiles] = await retriedQuery({ + db: this.pool, + query: `SHOW MASTER STATUS` + }); - // const results = await pg_utils.retriedQuery(this.pool, { - // statement: `SELECT - // slot_name, - // confirmed_flush_lsn, - // pg_current_wal_lsn(), - // (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance - // FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, - // params: [{ type: 'varchar', value: syncRulesId }] - // }); - // const [row] = pgwire.pgwireRows(results); - // if (row) { - // return Number(row.lsn_distance); - // } + /** + * The format will be something like: + * Array<{ + * File: 'binlog.000002', + * Position: 197, + * Binlog_Do_DB: '', + * Binlog_Ignore_DB: '', + * Executed_Gtid_Set: 'a7b95c22-5987-11ef-ac5e-0242ac190003:1-12' + * }> + * The file should be the syncRulesId. The Position should be the current head in bytes. + */ + + const matchingFile = binLogFiles.find((f) => f.File == syncRulesId); + if (!matchingFile) { + throw new Error(`Could not determine replication lag for file ${syncRulesId}: File not found.`); + } + + // TODO, it seems that the lag needs to be tracked manually throw new Error(`Could not determine replication lag for slot ${syncRulesId}`); } async getReplicationHead(): Promise { - throw new Error('not implemented'); - - // const [{ lsn }] = pgwire.pgwireRows( - // await pg_utils.retriedQuery(this.pool, `SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`) - // ); - // return String(lsn); + return readMasterComparableGtid(this.pool); } async getConnectionSchema(): Promise { diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 2773bc881..b14f58426 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -11,7 +11,7 @@ export type RetiredMySQLQueryOptions = { /** * Retry a simple query - up to 2 attempts total. */ -export async function retriedMysqlQuery(options: RetiredMySQLQueryOptions) { +export async function retriedQuery(options: RetiredMySQLQueryOptions) { const { db, query, params = [], retries = 2 } = options; for (let tries = retries; ; tries--) { try { @@ -26,6 +26,45 @@ export async function retriedMysqlQuery(options: RetiredMySQLQueryOptions) { } } +export async function checkSourceConfiguration(db: mysql.Pool) { + const errors: string[] = []; + const [[result]] = await retriedQuery({ + db, + query: ` + SELECT + @@GLOBAL.gtid_mode AS gtid_mode, + @@GLOBAL.log_bin AS log_bin, + @@GLOBAL.server_id AS server_id, + @@GLOBAL.log_bin_basename AS binlog_file, + @@GLOBAL.log_bin_index AS binlog_index_file + ` + }); + + if (result.gtid_mode != 'ON') { + errors.push(`GTID is not enabled, it is currently set to ${result.gtid_mode}. Please enable it.`); + } + + if (result.log_bin != 1) { + errors.push('Binary logging is not enabled. Please enable it.'); + } + + if (result.server_id < 0) { + errors.push( + `Your Server ID setting is too low, it must be greater than 0. It is currently ${result.server_id}. Please correct your configuration.` + ); + } + + if (!result.binlog_file) { + errors.push('Binary log file is not set. Please check your settings.'); + } + + if (!result.binlog_index_file) { + errors.push('Binary log index file is not set. Please check your settings.'); + } + + return errors; +} + /** * Transforms a GTID into a comparable string format, ensuring lexicographical * order aligns with the GTID's relative age. This assumes that all GTIDs diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 9c4df90c6..0ff0648f0 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -2,8 +2,8 @@ import { logger } from '@powersync/lib-services-framework'; import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/service-sync-rules'; import { SyncRulesStatus, TableInfo } from '@powersync/service-types'; +import { RouterServiceContext } from '../routes/router.js'; import * as storage from '../storage/storage-index.js'; -import * as system from '../system/system-index.js'; export interface DiagnosticsOptions { /** @@ -27,7 +27,7 @@ export interface DiagnosticsOptions { export const DEFAULT_DATASOURCE_ID = 'default'; export async function getSyncRulesStatus( - serviceContext: system.ServiceContext, + serviceContext: RouterServiceContext, sync_rules: storage.PersistedSyncRulesContent | null, options: DiagnosticsOptions ): Promise { @@ -63,6 +63,9 @@ export async function getSyncRulesStatus( let tables_flat: TableInfo[] = []; + const sourceConfig = await api?.getSourceConfig(); + const tag = sourceConfig?.tag ?? DEFAULT_TAG; + if (check_connection) { if (!api) { throw new Error('No connection configured'); @@ -70,6 +73,7 @@ export async function getSyncRulesStatus( const source_table_patterns = rules.getSourceTables(); const resolved_tables = await api.getDebugTablesInfo(source_table_patterns, rules); + tables_flat = resolved_tables.flatMap((info) => { if (info.table) { return [info.table]; @@ -136,9 +140,6 @@ export async function getSyncRulesStatus( }) ); - const sourceConfig = await api?.getSourceConfig(); - const tag = sourceConfig?.tag ?? DEFAULT_TAG; - return { content: include_content ? sync_rules.sync_rules_content : undefined, connections: [ diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 600e506a3..3e15839c2 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -75,10 +75,10 @@ export abstract class ReplicationModule extends modules.AbstractModule { const decodedConfig = this.configSchema().decode(baseMatchingConfig); const replicationAdapter = this.createReplicationAdapter(decodedConfig); this.replicationAdapters.add(replicationAdapter); - context.replicationEngine.register(replicationAdapter); + context.replicationEngine?.register(replicationAdapter); const apiAdapter = this.createSyncAPIAdapter(decodedConfig); - context.routerEngine.registerAPI(apiAdapter); + context.routerEngine?.registerAPI(apiAdapter); } catch (e) { logger.error(e); } diff --git a/packages/service-core/src/routes/configure-fastify.ts b/packages/service-core/src/routes/configure-fastify.ts index ec74d67b8..9c295f365 100644 --- a/packages/service-core/src/routes/configure-fastify.ts +++ b/packages/service-core/src/routes/configure-fastify.ts @@ -8,7 +8,7 @@ import { CHECKPOINT_ROUTES } from './endpoints/checkpointing.js'; import { SYNC_RULES_ROUTES } from './endpoints/sync-rules.js'; import { SYNC_STREAM_ROUTES } from './endpoints/sync-stream.js'; import { createRequestQueueHook, CreateRequestQueueParams } from './hooks.js'; -import { RouteDefinition } from './router.js'; +import { RouteDefinition, RouterServiceContext } from './router.js'; /** * A list of route definitions to be registered as endpoints. @@ -56,6 +56,19 @@ export const DEFAULT_ROUTE_OPTIONS = { */ export function configureFastifyServer(server: fastify.FastifyInstance, options: FastifyServerConfig) { const { service_context, routes = DEFAULT_ROUTE_OPTIONS } = options; + + const generateContext = async () => { + const { routerEngine } = service_context; + if (!routerEngine) { + throw new Error(`RouterEngine has not been registered`); + } + + return { + user_id: undefined, + service_context: service_context as RouterServiceContext + }; + }; + /** * Fastify creates an encapsulated context for each `.register` call. * Creating a separate context here to separate the concurrency limits for Admin APIs @@ -63,16 +76,7 @@ export function configureFastifyServer(server: fastify.FastifyInstance, options: * https://github.com/fastify/fastify/blob/main/docs/Reference/Encapsulation.md */ server.register(async function (childContext) { - registerFastifyRoutes( - childContext, - async () => { - return { - user_id: undefined, - service_context - }; - }, - routes.api?.routes ?? DEFAULT_ROUTE_OPTIONS.api.routes - ); + registerFastifyRoutes(childContext, generateContext, routes.api?.routes ?? DEFAULT_ROUTE_OPTIONS.api.routes); // Limit the active concurrent requests childContext.addHook( 'onRequest', @@ -84,12 +88,7 @@ export function configureFastifyServer(server: fastify.FastifyInstance, options: server.register(async function (childContext) { registerFastifyRoutes( childContext, - async () => { - return { - user_id: undefined, - service_context - }; - }, + generateContext, routes.sync_stream?.routes ?? DEFAULT_ROUTE_OPTIONS.sync_stream.routes ); // Limit the active concurrent requests diff --git a/packages/service-core/src/routes/configure-rsocket.ts b/packages/service-core/src/routes/configure-rsocket.ts index b2fd6b332..1851b35c6 100644 --- a/packages/service-core/src/routes/configure-rsocket.ts +++ b/packages/service-core/src/routes/configure-rsocket.ts @@ -8,7 +8,7 @@ import { ServiceContext } from '../system/ServiceContext.js'; import { generateContext, getTokenFromHeader } from './auth.js'; import { syncStreamReactive } from './endpoints/socket-route.js'; import { RSocketContextMeta, SocketRouteGenerator } from './router-socket.js'; -import { Context } from './router.js'; +import { Context, RouterServiceContext } from './router.js'; export type RSockerRouterConfig = { service_context: ServiceContext; @@ -36,11 +36,16 @@ export function configureRSocket(router: ReactiveSocketRouter, options: if (context?.token_payload == null) { throw new errors.AuthorizationError(token_errors ?? 'Authentication required'); } + + if (!service_context.routerEngine) { + throw new Error(`RouterEngine has not been registered`); + } + return { token, ...context, token_errors: token_errors, - service_context + service_context: service_context as RouterServiceContext }; } else { throw new errors.AuthorizationError('No token provided'); diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 8efd6bab2..dd0a6c8f8 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -3,9 +3,8 @@ import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules'; import type { FastifyPluginAsync } from 'fastify'; import * as t from 'ts-codec'; -import * as system from '../../system/system-index.js'; import { authApi } from '../auth.js'; -import { routeDefinition } from '../router.js'; +import { routeDefinition, RouterServiceContext } from '../router.js'; const DeploySyncRulesRequest = t.object({ content: t.string @@ -174,7 +173,7 @@ function replyPrettyJson(payload: any) { }); } -async function debugSyncRules(serviceContext: system.ServiceContext, sync_rules: string) { +async function debugSyncRules(serviceContext: RouterServiceContext, sync_rules: string) { try { const rules = SqlSyncRules.fromYaml(sync_rules); const source_table_patterns = rules.getSourceTables(); diff --git a/packages/service-core/src/routes/router.ts b/packages/service-core/src/routes/router.ts index f83f343af..b2a8be86f 100644 --- a/packages/service-core/src/routes/router.ts +++ b/packages/service-core/src/routes/router.ts @@ -1,13 +1,19 @@ import { router } from '@powersync/lib-services-framework'; import * as auth from '../auth/auth-index.js'; import { ServiceContext } from '../system/ServiceContext.js'; +import { RouterEngine } from './RouterEngine.js'; +/** + * The {@link RouterEngine} must be provided for these routes + */ +export type RouterServiceContext = ServiceContext & { routerEngine: RouterEngine }; /** * Common context for routes */ export type Context = { user_id?: string; - service_context: ServiceContext; + + service_context: RouterServiceContext; token_payload?: auth.JwtPayload; token_errors?: string[]; diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index 9fd7f2362..dca737a22 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -10,8 +10,8 @@ export interface ServiceContext { configuration: utils.ResolvedPowerSyncConfig; lifeCycleEngine: LifeCycledSystem; metrics: metrics.Metrics; - replicationEngine: replication.ReplicationEngine; - routerEngine: routes.RouterEngine; + replicationEngine: replication.ReplicationEngine | null; + routerEngine: routes.RouterEngine | null; storage: storage.StorageFactoryProvider; } @@ -35,12 +35,12 @@ export class ServiceContextContainer implements ServiceContext { this.storage.registerProvider(new storage.MongoStorageProvider()); } - get replicationEngine(): replication.ReplicationEngine { - return this.get(replication.ReplicationEngine); + get replicationEngine(): replication.ReplicationEngine | null { + return container.getOptional(replication.ReplicationEngine); } - get routerEngine(): routes.RouterEngine { - return this.get(routes.RouterEngine); + get routerEngine(): routes.RouterEngine | null { + return container.getOptional(routes.RouterEngine); } get metrics(): metrics.Metrics { diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index d20482d93..2be282f48 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -14,7 +14,7 @@ export const registerServerServices = (serviceContext: core.system.ServiceContex serviceContext.register(core.routes.RouterEngine, new core.routes.RouterEngine()); serviceContext.lifeCycleEngine.withLifecycle(serviceContext.routerEngine, { start: async (routerEngine) => { - await routerEngine.start(async (routes) => { + await routerEngine!.start(async (routes) => { const server = fastify.fastify(); server.register(cors, { @@ -54,7 +54,7 @@ export const registerServerServices = (serviceContext: core.system.ServiceContex }; }); }, - stop: (routerEngine) => routerEngine.shutdown() + stop: (routerEngine) => routerEngine!.shutdown() }); }; From 8f0d1f6f8584926edbe420fec28b587f0edcb234 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 14 Aug 2024 09:05:03 +0200 Subject: [PATCH 098/298] Introduced Replicator interface Cleaned up replication adapter interface WalStreamManager now implements the Replicator interface --- .../src/replication/WalStreamManager.ts | 28 ++- .../replication/core/ReplicationAdapter.ts | 119 +++++++++--- .../src/replication/core/ReplicationEngine.ts | 28 +-- .../src/replication/core/ReplicationEntity.ts | 8 +- .../src/replication/core/Replicator.ts | 171 +----------------- .../service-core/src/storage/SourceEntity.ts | 2 +- .../service-core/src/storage/storage-index.ts | 1 + 7 files changed, 135 insertions(+), 222 deletions(-) diff --git a/packages/service-core/src/replication/WalStreamManager.ts b/packages/service-core/src/replication/WalStreamManager.ts index 10dc1c268..b03069c37 100644 --- a/packages/service-core/src/replication/WalStreamManager.ts +++ b/packages/service-core/src/replication/WalStreamManager.ts @@ -8,30 +8,42 @@ import { DefaultErrorRateLimiter } from './ErrorRateLimiter.js'; import { WalStreamRunner } from './WalStreamRunner.js'; import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; import { container, logger } from '@powersync/lib-services-framework'; +import { Replicator } from './core/Replicator.js'; // 5 minutes const PING_INTERVAL = 1_000_000_000n * 300n; -export class WalStreamManager { - private streams = new Map(); +export interface WalStreamManagerOptions { + id: string; + system: CorePowerSyncSystem; + storage: storage.BucketStorageFactory; +} - private system: CorePowerSyncSystem; +export class WalStreamManager implements Replicator { + private streams = new Map(); private stopped = false; // First ping is only after 5 minutes, not when starting private lastPing = hrtime.bigint(); - private storage: storage.BucketStorageFactory; - /** * This limits the effect of retries when there is a persistent issue. */ private rateLimiter = new DefaultErrorRateLimiter(); - constructor(system: CorePowerSyncSystem) { - this.system = system; - this.storage = system.storage; + constructor(private options: WalStreamManagerOptions) {} + + public get id() { + return this.options.id; + } + + private get storage() { + return this.options.storage; + } + + private get system() { + return this.options.system; } start() { diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts index 703bd00da..a3c2d0d5b 100644 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ b/packages/service-core/src/replication/core/ReplicationAdapter.ts @@ -2,34 +2,58 @@ import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; import * as storage from '../../storage/storage-index.js'; import { SourceEntityDescriptor } from '../../storage/SourceEntity.js'; +/** + * Any additional configuration required for the ReplicationAdapters that is only available + * at runtime. + */ +export interface RuntimeConfiguration { + /** + * The unique identifier for the revision of the SyncRules that will be used for replication + */ + syncRuleId: string; +} + +export interface ReplicationAdapterFactory { + /** + * Unique name to identify the adapters in the PowerSync system for this datasource + * Suggestion: datasource type + datasource name ie. postgres-prod1 + */ + name: string; + create(configuration: RuntimeConfiguration): ReplicationAdapter; +} + /** * The ReplicationAdapter describes all the methods that are required by the - * Replicator to replicate data from a datasource into the PowerSync Bucket storage + * PluggableReplicator to replicate data from a datasource into the PowerSync Bucket storage */ export interface ReplicationAdapter { /** - * Unique name to identify this adapter in the PowerSync system - * Suggestion: datasource type + datasource name ie. postgres-prod1 + * Unique name for the adapter, will usually be provided by the ReplicationAdapterFactory implementation, + * but can be overridden if necessary. + * When provided, name takes the form of ReplicationAdapterFactory.name + sync rule identifier. */ - name(): string; + name: string; /** - * Check that the configuration required for replication on the datasource is in place. - * If any configuration is missing or incorrect, an error should be thrown with the details. + * Confirm that the required configuration for replication on the datasource is in place. + * If it isn't, attempt to create it. + * If any configuration is missing or could not be created, an error should be thrown with the details. */ - checkPrerequisites(): Promise; + ensureConfiguration(options: EnsureConfigurationOptions): Promise; /** * Get all the fully qualified entities that match the provided pattern - * @param pattern // TODO: Need something more generic then SourceTable + * @param pattern // TODO: Need something more generic than TablePattern */ - resolveReplicationEntities(pattern: TablePattern): Promise; + resolveReplicationEntities(pattern: TablePattern): Promise; /** - * Get the number of entries for this Entity + * Get the number of entries for this Entity. + * Return -1n if unknown. + * * @param entity */ - count(entity: storage.SourceTable): Promise; + count(entity: storage.SourceTable): Promise; /** * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided entryConsumer in batches. @@ -49,9 +73,17 @@ export interface ReplicationAdapter { /** * Clean up any configuration or state for the replication with the given identifier on the datasource. * This assumes that the replication is not currently active. - * @param syncRuleId The id of the SyncRule that was used to configure the replication */ - cleanupReplication(syncRuleId: number): Promise; + cleanupReplication(): Promise; + + /** + * Return the LSN descriptor for this data source. + */ + lsnDescriptor(): LSNDescriptor; +} + +export interface EnsureConfigurationOptions { + abortSignal: AbortSignal; } export interface InitializeDataBatch { @@ -69,37 +101,66 @@ export interface InitializeDataOptions { export interface StartReplicationOptions { entities: storage.SourceTable[]; - changeListener: (change: ReplicationUpdate) => {}; + changeListener: (change: ReplicationMessage) => {}; abortSignal: AbortSignal; } -export enum UpdateType { - INSERT = 'INSERT', - UPDATE = 'UPDATE', - DELETE = 'DELETE', +export enum ReplicationMessageType { + CRUD = 'CRUD', TRUNCATE = 'TRUNCATE', SCHEMA_CHANGE = 'SCHEMA_CHANGE', COMMIT = 'COMMIT', KEEP_ALIVE = 'KEEP_ALIVE' } -export interface ReplicationUpdate { - type: UpdateType; +export interface ReplicationMessage { + type: ReplicationMessageType; + payload: CrudOperation | SchemaUpdate | TruncateRequest | LSNUpdate; +} + +export type CrudOperation = { /** - * Descriptor of the data source entities that were updated. - * Usually only one entity is updated at a time, but for the truncate operation there could be multiple + * The entity for which the change occurred */ - entities: storage.SourceTable[]; + entity: storage.SourceTable; + /** + * Description of the change that happened to the entry + */ + entry: storage.SaveOptions; +}; + +export type SchemaUpdate = { /** - * Present when the update is an insert, update or delete. Contains the changed values for adding to the bucket storage + * Describes the new schema */ - entry?: storage.SaveOptions; + entityDescriptor: SourceEntityDescriptor; + // TODO: This shouldn't have to be here + connectionTag: string; +}; + +export type TruncateRequest = { + /** + * The entities that should be truncated + */ + entities: storage.SourceTable[]; +}; + +export type LSNUpdate = { + lsn: string; +}; + +/** + * Describes the LSN format for a data source, including a way to compare them. + */ +export interface LSNDescriptor { /** - * Present when the update is a schema change. Describes the new data source entity + * The zero LSN for this data source where transactions start from. */ - entityDescriptor?: SourceEntityDescriptor; + zeroLsn: string; /** - * Present when the update is a commit or a keep alive. + * Compare two LSNs in this data source's format and return a number indicating their order. + * @param lsnA + * @param lsnB */ - lsn?: string; + comparator: (lsnA: string, lsnB: string) => number; } diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/core/ReplicationEngine.ts index ab049a785..76a19d19b 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/core/ReplicationEngine.ts @@ -1,8 +1,6 @@ -import { ReplicationAdapter } from './ReplicationAdapter.js'; import { BucketStorageFactory } from '../../storage/BucketStorage.js'; -import { Replicator } from './Replicator.js'; -import { ConfigurationFileSyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; import { SyncRulesConfig } from '../../util/config/types.js'; +import { Replicator } from './Replicator.js'; export interface ReplicationEngineOptions { storage: BucketStorageFactory; @@ -11,30 +9,22 @@ export interface ReplicationEngineOptions { export class ReplicationEngine { private readonly options: ReplicationEngineOptions; - private readonly replicators: Map = new Map(); + private readonly replicators: Map = new Map(); constructor(options: ReplicationEngineOptions) { this.options = options; } /** - * Create a new Replicator from the provided ReplicationAdapter. Once started the Replicator will begin - * replicating data from the DataSource to PowerSync and keep it up to date. + * Register a Replicator with the engine * - * @param adapter + * @param replicator */ - public register(adapter: ReplicationAdapter) { - if (this.replicators.has(adapter)) { - throw new Error(`Replicator for type ${adapter.name} already registered`); + public register(replicator: Replicator) { + if (this.replicators.has(replicator.id)) { + throw new Error(`Replicator for type ${replicator.id} already registered`); } - this.replicators.set( - adapter, - new Replicator({ - adapter: adapter, - storage: this.options.storage, - sync_rule_provider: new ConfigurationFileSyncRulesProvider(this.options.config) - }) - ); + this.replicators.set(replicator.id, replicator); } /** @@ -42,7 +32,7 @@ export class ReplicationEngine { */ public async start(): Promise { for (const replicator of this.replicators.values()) { - await replicator.start(); + replicator.start(); } } diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts index 699ea96a5..3ad13c818 100644 --- a/packages/service-core/src/replication/core/ReplicationEntity.ts +++ b/packages/service-core/src/replication/core/ReplicationEntity.ts @@ -1,4 +1,4 @@ -import { SqliteRow } from '@powersync/service-sync-rules'; +import { ColumnDescriptor } from '../../storage/SourceEntity.js'; /** * Describes a replication entity, which is a logical representation of a table or collection in a data source. @@ -14,15 +14,15 @@ export interface ReplicationEntityDescriptor { /** * Identifier to uniquely identify the entity in PowerSync */ - id: string; + id: any; /** - * The entity's native identifier in the data source + * The entity's native identifier in the data source (oid) */ dataSourceEntityId: string; /** * The field(s) that uniquely identify an entry/row in this replication entity */ - primaryIdentifierFields: string[]; + primaryIdentifierFields: ColumnDescriptor[]; // Add any additional properties specific to the entity here additionalProperties: Record; diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts index 94bf26e6e..9bbca6d91 100644 --- a/packages/service-core/src/replication/core/Replicator.ts +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -1,164 +1,13 @@ -import * as storage from '../../storage/storage-index.js'; - -import { ReplicationAdapter } from './ReplicationAdapter.js'; -import { BucketStorageFactory } from '../../storage/BucketStorage.js'; -import { container, logger } from '@powersync/lib-services-framework'; -import { SyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; -import { ReplicationJob } from './ReplicationJob.js'; - -export interface ReplicatorOptions { - adapter: ReplicationAdapter; - storage: BucketStorageFactory; - sync_rule_provider: SyncRulesProvider; -} - /** - * A replicator manages the mechanics for replicating data from a data source to a storage bucket. - * This includes copying across the original data set and then keeping it in sync with the data source using ReplicationJobs. - * It also handles any changes to the sync rules. + * A replicator manages the mechanics for replicating data from a data source to powersync. + * This includes copying across the original data set and then keeping it in sync with the data source. + * It should also handle any changes to the sync rules -> TODO: For now */ -export class Replicator { - private readonly adapter: ReplicationAdapter; - private readonly storage: BucketStorageFactory; - - private replicationJobs = new Map(); - private stopped = false; - - constructor(private options: ReplicatorOptions) { - this.adapter = options.adapter; - this.storage = options.storage; - } - - public async start(): Promise { - this.runLoop().catch((e) => { - logger.error(`Data source fatal replication error: ${this.adapter.name()}`, e); - container.reporter.captureException(e); - setTimeout(() => { - process.exit(1); - }, 1000); - }); - } - - public async stop(): Promise { - this.stopped = true; - let promises: Promise[] = []; - for (const job of this.replicationJobs.values()) { - promises.push(job.stop()); - } - await Promise.all(promises); - } - - private async runLoop() { - const syncRules = await this.options.sync_rule_provider.get(); - let configuredLock: storage.ReplicationLock | undefined = undefined; - if (syncRules != null) { - logger.info('Loaded sync rules'); - try { - // Configure new sync rules, if they have changed. - // In that case, also immediately take out a lock, so that another process doesn't start replication on it. - const { lock } = await this.storage.configureSyncRules(syncRules, { - lock: true - }); - if (lock) { - configuredLock = lock; - } - } catch (e) { - // Log, but continue with previous sync rules - logger.error(`Failed to update sync rules from configuration`, e); - } - } else { - logger.info('No sync rules configured - configure via API'); - } - while (!this.stopped) { - await container.probes.touch(); - try { - await this.refresh({ configured_lock: configuredLock }); - // The lock is only valid on the first refresh. - configuredLock = undefined; - } catch (e) { - logger.error(`Failed to refresh replication jobs: ${this.adapter.name()}`, e); - } - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - - private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { - if (this.stopped) { - return; - } - - let configuredLock = options?.configured_lock; - - const existingJobs = new Map(this.replicationJobs.entries()); - const replicatingSyncRules = await this.storage.getReplicatingSyncRules(); - const newJobs = new Map(); - for (let syncRules of replicatingSyncRules) { - const existingJob = existingJobs.get(syncRules.id); - if (existingJob && !existingJob.isStopped()) { - // No change - existingJobs.delete(syncRules.id); - newJobs.set(syncRules.id, existingJob); - } else if (existingJob && existingJob.isStopped()) { - // Stopped (e.g. fatal error). - // Remove from the list. Next refresh call will restart the job. - existingJobs.delete(syncRules.id); - } else { - // New sync rules were found (or resume after restart) - try { - let lock: storage.ReplicationLock; - if (configuredLock?.sync_rules_id == syncRules.id) { - lock = configuredLock; - } else { - lock = await syncRules.lock(); - } - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const newJob = new ReplicationJob({ - adapter: this.adapter, - storage: storage, - lock: lock - }); - newJobs.set(syncRules.id, newJob); - newJob.start(); - } catch (e) { - // Could be a sync rules parse error, - // for example from stricter validation that was added. - // This will be retried every couple of seconds. - // When new (valid) sync rules are deployed and processed, this one be disabled. - logger.error(`Failed to start replication for ${this.adapter.name()} with new sync rules`, e); - } - } - } - - this.replicationJobs = newJobs; - - // Terminate any orphaned jobs that no longer have sync rules - for (let job of existingJobs.values()) { - // Old - stop and clean up - try { - await job.terminate(); - } catch (e) { - // This will be retried - logger.warn(`Failed to terminate old ${this.adapter.name()} replication job}`, e); - } - } - - // Sync rules stopped previously or by a different process. - const stopped = await this.storage.getStoppedSyncRules(); - for (let syncRules of stopped) { - try { - const lock = await syncRules.lock(); - try { - await this.adapter.cleanupReplication(syncRules.id); - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - await storage.terminate(); - } finally { - await lock.release(); - } - } catch (e) { - logger.warn(`Failed to terminate ${syncRules.id}`, e); - } - } - } +export interface Replicator { + /** + * Unique identifier for this replicator + */ + id: string; + start(): void; + stop(): Promise; } diff --git a/packages/service-core/src/storage/SourceEntity.ts b/packages/service-core/src/storage/SourceEntity.ts index f1bf90e68..3d85d371e 100644 --- a/packages/service-core/src/storage/SourceEntity.ts +++ b/packages/service-core/src/storage/SourceEntity.ts @@ -10,7 +10,7 @@ export interface ColumnDescriptor { typeId?: number; } -// TODO: This needs to be consolidate with SourceTable into something new. +// TODO: This needs to be consolidated with SourceTable into something new. export interface SourceEntityDescriptor { /** * The internal id of the data source structure in the database diff --git a/packages/service-core/src/storage/storage-index.ts b/packages/service-core/src/storage/storage-index.ts index 7ec8de933..624dadd40 100644 --- a/packages/service-core/src/storage/storage-index.ts +++ b/packages/service-core/src/storage/storage-index.ts @@ -1,6 +1,7 @@ export * from './SourceTable.js'; export * from './MongoBucketStorage.js'; export * from './BucketStorage.js'; +export * from './SourceEntity.js'; export * from './mongo/db.js'; export * from './mongo/models.js'; From a348eaf41e55f2ba7a1dad656e81480b24e1cf48 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 14 Aug 2024 11:10:20 +0200 Subject: [PATCH 099/298] Re-commissioned the Walstream classes Introduced PluggableReplicator --- .../src/replication/PgManager.ts | 66 + .../replication/PostgresReplicationAdapter.ts | 47 - .../src/replication/WalConnection.ts | 347 ++--- .../src/replication/WalStream.ts | 1246 ++++++++--------- .../src/replication/WalStreamManager.ts | 438 +++--- .../src/replication/WalStreamRunner.ts | 358 +++-- .../src/replication/WalStreamManager.ts | 225 --- .../replication/core/PluggableReplicator.ts | 178 +++ .../src/replication/core/ReplicationJob.ts | 101 +- .../src/replication/core/ReplicationModule.ts | 10 +- .../src/replication/core/replication-utils.ts | 8 + 11 files changed, 1511 insertions(+), 1513 deletions(-) create mode 100644 modules/module-postgres/src/replication/PgManager.ts delete mode 100644 modules/module-postgres/src/replication/PostgresReplicationAdapter.ts delete mode 100644 packages/service-core/src/replication/WalStreamManager.ts create mode 100644 packages/service-core/src/replication/core/PluggableReplicator.ts create mode 100644 packages/service-core/src/replication/core/replication-utils.ts diff --git a/modules/module-postgres/src/replication/PgManager.ts b/modules/module-postgres/src/replication/PgManager.ts new file mode 100644 index 000000000..8633cc125 --- /dev/null +++ b/modules/module-postgres/src/replication/PgManager.ts @@ -0,0 +1,66 @@ +import * as pgwire from '@powersync/service-jpgwire'; +import { NormalizedPostgresConnectionConfig } from '../types/types.js'; + +export class PgManager { + /** + * Do not use this for any transactions. + */ + public readonly pool: pgwire.PgClient; + + private connectionPromises: Promise[] = []; + + constructor(public options: NormalizedPostgresConnectionConfig, public poolOptions: pgwire.PgPoolOptions) { + // The pool is lazy - no connections are opened until a query is performed. + this.pool = pgwire.connectPgWirePool(this.options, poolOptions); + } + + /** + * Create a new replication connection. + */ + async replicationConnection(): Promise { + const p = pgwire.connectPgWire(this.options, { type: 'replication' }); + this.connectionPromises.push(p); + return await p; + } + + /** + * Create a new standard connection, used for initial snapshot. + * + * This connection must not be shared between multiple async contexts. + */ + async snapshotConnection(): Promise { + const p = pgwire.connectPgWire(this.options, { type: 'standard' }); + this.connectionPromises.push(p); + return await p; + } + + async end() { + for (let result of await Promise.allSettled([ + this.pool.end(), + ...this.connectionPromises.map(async (promise) => { + const connection = await promise; + return await connection.end(); + }) + ])) { + // Throw the first error, if any + if (result.status == 'rejected') { + throw result.reason; + } + } + } + + async destroy() { + this.pool.destroy(); + for (let result of await Promise.allSettled([ + ...this.connectionPromises.map(async (promise) => { + const connection = await promise; + return connection.destroy(); + }) + ])) { + // Throw the first error, if any + if (result.status == 'rejected') { + throw result.reason; + } + } + } +} diff --git a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts b/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts deleted file mode 100644 index 8e0e0f01b..000000000 --- a/modules/module-postgres/src/replication/PostgresReplicationAdapter.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { replication, SourceTable } from '@powersync/service-core'; -import * as pgwire from '@powersync/service-jpgwire'; -import * as sync_rules from '@powersync/service-sync-rules'; - -import { ResolvedConnectionConfig } from '../types/types.js'; - -export type PostgresConnection = pgwire.PgClient; - -export class PostgresReplicationAdapter implements replication.ReplicationAdapter { - constructor(protected config: ResolvedConnectionConfig) {} - resolveReplicationEntities(pattern: sync_rules.TablePattern): Promise { - throw new Error('Method not implemented.'); - } - count(entity: SourceTable): Promise { - throw new Error('Method not implemented.'); - } - initializeData(options: replication.InitializeDataOptions): Promise { - throw new Error('Method not implemented.'); - } - - name(): string { - return 'postgres'; - } - - shutdown(): Promise { - throw new Error('Method not implemented.'); - } - - checkPrerequisites(): Promise { - throw new Error('Method not implemented.'); - } - - toReplicationEntities(pattern: sync_rules.TablePattern): Promise { - throw new Error('Method not implemented.'); - } - - startReplication(options: replication.StartReplicationOptions): Promise { - throw new Error('Method not implemented.'); - } - cleanupReplication(syncRuleId: number): Promise { - throw new Error('Method not implemented.'); - } - - terminateReplication(): Promise { - throw new Error('Method not implemented.'); - } -} diff --git a/modules/module-postgres/src/replication/WalConnection.ts b/modules/module-postgres/src/replication/WalConnection.ts index 8914f52f4..a0c53e7a9 100644 --- a/modules/module-postgres/src/replication/WalConnection.ts +++ b/modules/module-postgres/src/replication/WalConnection.ts @@ -1,173 +1,174 @@ -// import * as pgwire from '@powersync/service-jpgwire'; -// import { DEFAULT_TAG, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; -// import { ReplicationError, TableInfo } from '@powersync/service-types'; -// import {} from '@powersync/service-core'; - -// // import { ReplicaIdentityResult, getReplicationIdentityColumns } from './util.js'; -// /** -// * Connection that _manages_ WAL, but does not do streaming. -// */ -// export class WalConnection { -// db: pgwire.PgClient; -// connectionTag = DEFAULT_TAG; -// publication_name = 'powersync'; - -// sync_rules: SqlSyncRules; - -// /** -// * db can be a PgConnection or PgPool. -// * -// * No transactions are used here, but it is up to the client to ensure -// * nothing here is called in the middle of another transaction if -// * PgConnection is used. -// */ -// constructor(options: { db: pgwire.PgClient; sync_rules: SqlSyncRules }) { -// this.db = options.db; -// this.sync_rules = options.sync_rules; -// } - -// async checkSourceConfiguration() { -// await checkSourceConfiguration(this.db); -// } - -// async getDebugTableInfo(tablePattern: TablePattern, name: string, relationId: number | null): Promise { -// const schema = tablePattern.schema; -// let id_columns_result: ReplicaIdentityResult | undefined = undefined; -// let id_columns_error = null; - -// if (relationId != null) { -// try { -// id_columns_result = await getReplicationIdentityColumns(this.db, relationId); -// } catch (e) { -// id_columns_error = { level: 'fatal', message: e.message }; -// } -// } - -// const id_columns = id_columns_result?.replicationColumns ?? []; - -// const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); - -// const syncData = this.sync_rules.tableSyncsData(sourceTable); -// const syncParameters = this.sync_rules.tableSyncsParameters(sourceTable); - -// if (relationId == null) { -// return { -// schema: schema, -// name: name, -// pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, -// replication_id: [], -// data_queries: syncData, -// parameter_queries: syncParameters, -// // Also -// errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] -// }; -// } -// if (id_columns.length == 0 && id_columns_error == null) { -// let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; -// if (id_columns_result?.replicationIdentity == 'default') { -// message += ' Configure a primary key on the table.'; -// } -// id_columns_error = { level: 'fatal', message }; -// } - -// let selectError = null; -// try { -// await util.retriedQuery(this.db, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); -// } catch (e) { -// selectError = { level: 'fatal', message: e.message }; -// } - -// let replicateError = null; - -// const publications = await util.retriedQuery(this.db, { -// statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, -// params: [ -// { type: 'varchar', value: this.publication_name }, -// { type: 'varchar', value: tablePattern.schema }, -// { type: 'varchar', value: name } -// ] -// }); -// if (publications.rows.length == 0) { -// replicateError = { -// level: 'fatal', -// message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` -// }; -// } - -// return { -// schema: schema, -// name: name, -// pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, -// replication_id: id_columns.map((c) => c.name), -// data_queries: syncData, -// parameter_queries: syncParameters, -// errors: [id_columns_error, selectError, replicateError].filter((error) => error != null) as ReplicationError[] -// }; -// } - -// async getDebugTablesInfo(tablePatterns: TablePattern[]): Promise { -// let result: PatternResult[] = []; - -// for (let tablePattern of tablePatterns) { -// const schema = tablePattern.schema; - -// let patternResult: PatternResult = { -// schema: schema, -// pattern: tablePattern.tablePattern, -// wildcard: tablePattern.isWildcard -// }; -// result.push(patternResult); - -// if (tablePattern.isWildcard) { -// patternResult.tables = []; -// const prefix = tablePattern.tablePrefix; -// const results = await util.retriedQuery(this.db, { -// statement: `SELECT c.oid AS relid, c.relname AS table_name -// FROM pg_class c -// JOIN pg_namespace n ON n.oid = c.relnamespace -// WHERE n.nspname = $1 -// AND c.relkind = 'r' -// AND c.relname LIKE $2`, -// params: [ -// { type: 'varchar', value: schema }, -// { type: 'varchar', value: tablePattern.tablePattern } -// ] -// }); - -// for (let row of pgwireRows(results)) { -// const name = row.table_name as string; -// const relationId = row.relid as number; -// if (!name.startsWith(prefix)) { -// continue; -// } -// const details = await this.getDebugTableInfo(tablePattern, name, relationId); -// patternResult.tables.push(details); -// } -// } else { -// const results = await util.retriedQuery(this.db, { -// statement: `SELECT c.oid AS relid, c.relname AS table_name -// FROM pg_class c -// JOIN pg_namespace n ON n.oid = c.relnamespace -// WHERE n.nspname = $1 -// AND c.relkind = 'r' -// AND c.relname = $2`, -// params: [ -// { type: 'varchar', value: schema }, -// { type: 'varchar', value: tablePattern.tablePattern } -// ] -// }); -// if (results.rows.length == 0) { -// // Table not found -// const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); -// patternResult.table = details; -// } else { -// const row = pgwireRows(results)[0]; -// const name = row.table_name as string; -// const relationId = row.relid as number; -// patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId); -// } -// } -// } -// return result; -// } -// } +import * as pgwire from '@powersync/service-jpgwire'; +import * as sync_rules from '@powersync/service-sync-rules'; +import { ReplicationError, TableInfo } from '@powersync/service-types'; +import { PatternResult, SourceTable } from '@powersync/service-core'; +import { getReplicationIdentityColumns, ReplicaIdentityResult } from './replication-utils.js'; +import * as util from '../utils/pgwire_utils.js'; + +/** + * Connection that _manages_ WAL, but does not do streaming. + */ +export class WalConnection { + db: pgwire.PgClient; + connectionTag = sync_rules.DEFAULT_TAG; + publication_name = 'powersync'; + + sync_rules: sync_rules.SqlSyncRules; + + /** + * db can be a PgConnection or PgPool. + * + * No transactions are used here, but it is up to the client to ensure + * nothing here is called in the middle of another transaction if + * PgConnection is used. + */ + constructor(options: { db: pgwire.PgClient; sync_rules: sync_rules.SqlSyncRules }) { + this.db = options.db; + this.sync_rules = options.sync_rules; + } + + async getDebugTableInfo( + tablePattern: sync_rules.TablePattern, + name: string, + relationId: number | null + ): Promise { + const schema = tablePattern.schema; + let id_columns_result: ReplicaIdentityResult | undefined = undefined; + let id_columns_error = null; + + if (relationId != null) { + try { + id_columns_result = await getReplicationIdentityColumns(this.db, relationId); + } catch (e) { + id_columns_error = { level: 'fatal', message: e.message }; + } + } + + const id_columns = id_columns_result?.replicationColumns ?? []; + + const sourceTable = new SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); + + const syncData = this.sync_rules.tableSyncsData(sourceTable); + const syncParameters = this.sync_rules.tableSyncsParameters(sourceTable); + + if (relationId == null) { + return { + schema: schema, + name: name, + pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + replication_id: [], + data_queries: syncData, + parameter_queries: syncParameters, + // Also + errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] + }; + } + if (id_columns.length == 0 && id_columns_error == null) { + let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; + if (id_columns_result?.replicationIdentity == 'default') { + message += ' Configure a primary key on the table.'; + } + id_columns_error = { level: 'fatal', message }; + } + + let selectError = null; + try { + await util.retriedQuery(this.db, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); + } catch (e) { + selectError = { level: 'fatal', message: e.message }; + } + + let replicateError = null; + + const publications = await util.retriedQuery(this.db, { + statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, + params: [ + { type: 'varchar', value: this.publication_name }, + { type: 'varchar', value: tablePattern.schema }, + { type: 'varchar', value: name } + ] + }); + if (publications.rows.length == 0) { + replicateError = { + level: 'fatal', + message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` + }; + } + + return { + schema: schema, + name: name, + pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + replication_id: id_columns.map((c) => c.name), + data_queries: syncData, + parameter_queries: syncParameters, + errors: [id_columns_error, selectError, replicateError].filter((error) => error != null) as ReplicationError[] + }; + } + + async getDebugTablesInfo(tablePatterns: sync_rules.TablePattern[]): Promise { + let result: PatternResult[] = []; + + for (let tablePattern of tablePatterns) { + const schema = tablePattern.schema; + + let patternResult: PatternResult = { + schema: schema, + pattern: tablePattern.tablePattern, + wildcard: tablePattern.isWildcard + }; + result.push(patternResult); + + if (tablePattern.isWildcard) { + patternResult.tables = []; + const prefix = tablePattern.tablePrefix; + const results = await util.retriedQuery(this.db, { + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname LIKE $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + + for (let row of pgwire.pgwireRows(results)) { + const name = row.table_name as string; + const relationId = row.relid as number; + if (!name.startsWith(prefix)) { + continue; + } + const details = await this.getDebugTableInfo(tablePattern, name, relationId); + patternResult.tables.push(details); + } + } else { + const results = await util.retriedQuery(this.db, { + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname = $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + if (results.rows.length == 0) { + // Table not found + const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); + patternResult.table = details; + } else { + const row = pgwire.pgwireRows(results)[0]; + const name = row.table_name as string; + const relationId = row.relid as number; + patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId); + } + } + } + return result; + } +} diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 415b7f842..585fa3408 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -1,624 +1,622 @@ -// import * as pgwire from '@powersync/service-jpgwire'; -// import { container, errors, logger } from '@powersync/lib-services-framework'; -// import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; - -// import * as storage from '../storage/storage-index.js'; -// import * as util from '../util/util-index.js'; - -// import { getPgOutputRelation, getRelId } from './PgRelation.js'; -// import { getReplicationIdentityColumns } from './util.js'; -// import { WalConnection } from './WalConnection.js'; -// import { Metrics } from '../metrics/Metrics.js'; -// import { SourceEntityDescriptor } from '../storage/SourceEntity.js'; - -// export const ZERO_LSN = '00000000/00000000'; - -// export interface WalStreamOptions { -// connections: util.PgManager; -// factory: storage.BucketStorageFactory; -// storage: storage.SyncRulesBucketStorage; -// abort_signal: AbortSignal; -// } - -// interface InitResult { -// needsInitialSync: boolean; -// } - -// export class MissingReplicationSlotError extends Error { -// constructor(message: string) { -// super(message); -// } -// } - -// export class WalStream { -// sync_rules: SqlSyncRules; -// group_id: number; - -// wal_connection: WalConnection; -// connection_id = 1; - -// private readonly storage: storage.SyncRulesBucketStorage; - -// private slot_name: string; - -// private connections: util.PgManager; - -// private abort_signal: AbortSignal; - -// private relation_cache = new Map(); - -// private startedStreaming = false; - -// constructor(options: WalStreamOptions) { -// this.storage = options.storage; -// this.sync_rules = options.storage.sync_rules; -// this.group_id = options.storage.group_id; -// this.slot_name = options.storage.slot_name; -// this.connections = options.connections; - -// this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); -// this.abort_signal = options.abort_signal; -// this.abort_signal.addEventListener( -// 'abort', -// () => { -// if (this.startedStreaming) { -// // Ping to speed up cancellation of streaming replication -// // We're not using pg_snapshot here, since it could be in the middle of -// // an initial replication transaction. -// const promise = util.retriedQuery( -// this.connections.pool, -// `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` -// ); -// promise.catch((e) => { -// // Failures here are okay - this only speeds up stopping the process. -// logger.warn('Failed to ping connection', e); -// }); -// } else { -// // If we haven't started streaming yet, it could be due to something like -// // and invalid password. In that case, don't attempt to ping. -// } -// }, -// { once: true } -// ); -// } - -// get publication_name() { -// return this.wal_connection.publication_name; -// } - -// get connectionTag() { -// return this.wal_connection.connectionTag; -// } - -// get stopped() { -// return this.abort_signal.aborted; -// } - -// async getQualifiedTableNames( -// batch: storage.BucketStorageBatch, -// db: pgwire.PgConnection, -// tablePattern: TablePattern -// ): Promise { -// const schema = tablePattern.schema; -// if (tablePattern.connectionTag != this.connectionTag) { -// return []; -// } - -// let tableRows: any[]; -// const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; -// if (tablePattern.isWildcard) { -// const result = await db.query({ -// statement: `SELECT c.oid AS relid, c.relname AS table_name -// FROM pg_class c -// JOIN pg_namespace n ON n.oid = c.relnamespace -// WHERE n.nspname = $1 -// AND c.relkind = 'r' -// AND c.relname LIKE $2`, -// params: [ -// { type: 'varchar', value: schema }, -// { type: 'varchar', value: tablePattern.tablePattern } -// ] -// }); -// tableRows = pgwire.pgwireRows(result); -// } else { -// const result = await db.query({ -// statement: `SELECT c.oid AS relid, c.relname AS table_name -// FROM pg_class c -// JOIN pg_namespace n ON n.oid = c.relnamespace -// WHERE n.nspname = $1 -// AND c.relkind = 'r' -// AND c.relname = $2`, -// params: [ -// { type: 'varchar', value: schema }, -// { type: 'varchar', value: tablePattern.tablePattern } -// ] -// }); - -// tableRows = pgwire.pgwireRows(result); -// } -// let result: storage.SourceTable[] = []; - -// for (let row of tableRows) { -// const name = row.table_name as string; -// if (typeof row.relid != 'bigint') { -// throw new Error(`missing relid for ${name}`); -// } -// const relid = Number(row.relid as bigint); - -// if (prefix && !name.startsWith(prefix)) { -// continue; -// } - -// const rs = await db.query({ -// statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, -// params: [ -// { type: 'varchar', value: this.publication_name }, -// { type: 'varchar', value: tablePattern.schema }, -// { type: 'varchar', value: name } -// ] -// }); -// if (rs.rows.length == 0) { -// logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); -// continue; -// } - -// const cresult = await getReplicationIdentityColumns(db, relid); - -// const table = await this.handleRelation( -// batch, -// { -// name, -// schema, -// objectId: relid, -// replicationColumns: cresult.replicationColumns -// } as SourceEntityDescriptor, -// false -// ); - -// result.push(table); -// } -// return result; -// } - -// async initSlot(): Promise { -// await this.wal_connection.checkSourceConfiguration(); - -// const slotName = this.slot_name; - -// const status = await this.storage.getStatus(); -// if (status.snapshot_done && status.checkpoint_lsn) { -// logger.info(`${slotName} Initial replication already done`); - -// let last_error = null; - -// // Check that replication slot exists -// for (let i = 120; i >= 0; i--) { -// await touch(); - -// if (i == 0) { -// container.reporter.captureException(last_error, { -// level: errors.ErrorSeverity.ERROR, -// metadata: { -// replication_slot: slotName -// } -// }); - -// throw last_error; -// } -// try { -// // We peek a large number of changes here, to make it more likely to pick up replication slot errors. -// // For example, "publication does not exist" only occurs here if the peek actually includes changes related -// // to the slot. -// await this.connections.pool.query({ -// statement: `SELECT * -// FROM pg_catalog.pg_logical_slot_peek_binary_changes($1, NULL, 1000, 'proto_version', '1', -// 'publication_names', $2)`, -// params: [ -// { type: 'varchar', value: slotName }, -// { type: 'varchar', value: this.publication_name } -// ] -// }); -// // Success -// logger.info(`Slot ${slotName} appears healthy`); -// return { needsInitialSync: false }; -// } catch (e) { -// last_error = e; -// logger.warn(`${slotName} Replication slot error`, e); - -// if (this.stopped) { -// throw e; -// } - -// // Could also be `publication "powersync" does not exist`, although this error may show up much later -// // in some cases. - -// if ( -// /incorrect prev-link/.test(e.message) || -// /replication slot.*does not exist/.test(e.message) || -// /publication.*does not exist/.test(e.message) -// ) { -// container.reporter.captureException(e, { -// level: errors.ErrorSeverity.WARNING, -// metadata: { -// try_index: i, -// replication_slot: slotName -// } -// }); -// // Sample: record with incorrect prev-link 10000/10000 at 0/18AB778 -// // Seen during development. Some internal error, fixed by re-creating slot. -// // -// // Sample: publication "powersync" does not exist -// // Happens when publication deleted or never created. -// // Slot must be re-created in this case. -// logger.info(`${slotName} does not exist anymore, will create new slot`); - -// throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`); -// } -// // Try again after a pause -// await new Promise((resolve) => setTimeout(resolve, 1000)); -// } -// } -// } - -// return { needsInitialSync: true }; -// } - -// async estimatedCount(db: pgwire.PgConnection, table: storage.SourceTable): Promise { -// const results = await db.query({ -// statement: `SELECT reltuples::bigint AS estimate -// FROM pg_class -// WHERE oid = $1::regclass`, -// params: [{ value: table.qualifiedName, type: 'varchar' }] -// }); -// const row = results.rows[0]; -// if ((row?.[0] ?? -1n) == -1n) { -// return '?'; -// } else { -// return `~${row[0]}`; -// } -// } - -// /** -// * Start initial replication. -// * -// * If (partial) replication was done before on this slot, this clears the state -// * and starts again from scratch. -// */ -// async startInitialReplication(replicationConnection: pgwire.PgConnection) { -// // If anything here errors, the entire replication process is aborted, -// // and all connections closed, including this one. -// const db = await this.connections.snapshotConnection(); - -// const slotName = this.slot_name; - -// await this.storage.clear(); - -// await db.query({ -// statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', -// params: [{ type: 'varchar', value: slotName }] -// }); - -// // We use the replication connection here, not a pool. -// // This connection needs to stay open at least until the snapshot is used below. -// const result = await replicationConnection.query( -// `CREATE_REPLICATION_SLOT ${slotName} LOGICAL pgoutput EXPORT_SNAPSHOT` -// ); -// const columns = result.columns; -// const row = result.rows[0]!; -// if (columns[1]?.name != 'consistent_point' || columns[2]?.name != 'snapshot_name' || row == null) { -// throw new Error(`Invalid CREATE_REPLICATION_SLOT output: ${JSON.stringify(columns)}`); -// } -// // This LSN could be used in initialReplication below. -// // But it's also safe to just use ZERO_LSN - we won't get any changes older than this lsn -// // with streaming replication. -// const lsn = pgwire.lsnMakeComparable(row[1]); -// const snapshot = row[2]; -// logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`); - -// // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn -// await db.query('BEGIN'); -// // Use the snapshot exported above. -// // Using SERIALIZABLE isolation level may give stronger guarantees, but that complicates -// // the replication slot + snapshot above. And we still won't have SERIALIZABLE -// // guarantees with streaming replication. -// // See: ./docs/serializability.md for details. -// // -// // Another alternative here is to use the same pgwire connection for initial replication as well, -// // instead of synchronizing a separate transaction to the snapshot. - -// try { -// await db.query(`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`); -// await db.query(`SET TRANSACTION READ ONLY`); -// await db.query(`SET TRANSACTION SNAPSHOT '${snapshot}'`); - -// // Disable statement timeout for the duration of this transaction. -// // On Supabase, the default is 2 minutes. -// await db.query(`set local statement_timeout = 0`); - -// logger.info(`${slotName} Starting initial replication`); -// await this.initialReplication(db, lsn); -// logger.info(`${slotName} Initial replication done`); -// await db.query('COMMIT'); -// } catch (e) { -// await db.query('ROLLBACK'); -// throw e; -// } -// } - -// async initialReplication(db: pgwire.PgConnection, lsn: string) { -// const sourceTables = this.sync_rules.getSourceTables(); -// await this.storage.startBatch({}, async (batch) => { -// for (let tablePattern of sourceTables) { -// const tables = await this.getQualifiedTableNames(batch, db, tablePattern); -// for (let table of tables) { -// await this.snapshotTable(batch, db, table); -// await batch.markSnapshotDone([table], lsn); - -// await touch(); -// } -// } -// await batch.commit(lsn); -// }); -// } - -// static *getQueryData(results: Iterable): Generator { -// for (let row of results) { -// yield toSyncRulesRow(row); -// } -// } - -// private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) { -// logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); -// const estimatedCount = await this.estimatedCount(db, table); -// let at = 0; -// let lastLogIndex = 0; -// const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); -// let columns: { i: number; name: string }[] = []; -// // pgwire streams rows in chunks. -// // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. - -// for await (let chunk of cursor) { -// if (chunk.tag == 'RowDescription') { -// let i = 0; -// columns = chunk.payload.map((c) => { -// return { i: i++, name: c.name }; -// }); -// continue; -// } - -// const rows = chunk.rows.map((row) => { -// let q: DatabaseInputRow = {}; -// for (let c of columns) { -// q[c.name] = row[c.i]; -// } -// return q; -// }); -// if (rows.length > 0 && at - lastLogIndex >= 5000) { -// logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`); -// lastLogIndex = at; -// } -// if (this.abort_signal.aborted) { -// throw new Error(`Aborted initial replication of ${this.slot_name}`); -// } - -// for (let record of WalStream.getQueryData(rows)) { -// // This auto-flushes when the batch reaches its size limit -// await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); -// } -// at += rows.length; -// Metrics.getInstance().rows_replicated_total.add(rows.length); - -// await touch(); -// } - -// await batch.flush(); -// } - -// async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { -// if (!descriptor.objectId) { -// throw new Error('objectId expected'); -// } -// const result = await this.storage.resolveTable({ -// group_id: this.group_id, -// connection_id: this.connection_id, -// connection_tag: this.connectionTag, -// entity_descriptor: descriptor, -// sync_rules: this.sync_rules -// }); -// this.relation_cache.set(descriptor.objectId, result.table); - -// // Drop conflicting tables. This includes for example renamed tables. -// await batch.drop(result.dropTables); - -// // Snapshot if: -// // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) -// // 2. Snapshot is not already done, AND: -// // 3. The table is used in sync rules. -// const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; - -// if (shouldSnapshot) { -// // Truncate this table, in case a previous snapshot was interrupted. -// await batch.truncate([result.table]); - -// let lsn: string = ZERO_LSN; -// // Start the snapshot inside a transaction. -// // We use a dedicated connection for this. -// const db = await this.connections.snapshotConnection(); -// try { -// await db.query('BEGIN'); -// try { -// // Get the current LSN. -// // The data will only be consistent once incremental replication -// // has passed that point. -// const rs = await db.query(`select pg_current_wal_lsn() as lsn`); -// lsn = rs.rows[0][0]; -// await this.snapshotTable(batch, db, result.table); -// await db.query('COMMIT'); -// } catch (e) { -// await db.query('ROLLBACK'); -// throw e; -// } -// } finally { -// await db.end(); -// } -// const [table] = await batch.markSnapshotDone([result.table], lsn); -// return table; -// } - -// return result.table; -// } - -// private getTable(relationId: number): storage.SourceTable { -// const table = this.relation_cache.get(relationId); -// if (table == null) { -// // We should always receive a replication message before the relation is used. -// // If we can't find it, it's a bug. -// throw new Error(`Missing relation cache for ${relationId}`); -// } -// return table; -// } - -// async writeChange( -// batch: storage.BucketStorageBatch, -// msg: pgwire.PgoutputMessage -// ): Promise { -// if (msg.lsn == null) { -// return null; -// } -// if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { -// const table = this.getTable(getRelId(msg.relation)); -// if (!table.syncAny) { -// logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); -// return null; -// } - -// if (msg.tag == 'insert') { -// Metrics.getInstance().rows_replicated_total.add(1); -// const baseRecord = util.constructAfterRecord(msg); -// return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); -// } else if (msg.tag == 'update') { -// Metrics.getInstance().rows_replicated_total.add(1); -// // "before" may be null if the replica id columns are unchanged -// // It's fine to treat that the same as an insert. -// const before = util.constructBeforeRecord(msg); -// const after = util.constructAfterRecord(msg); -// return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); -// } else if (msg.tag == 'delete') { -// Metrics.getInstance().rows_replicated_total.add(1); -// const before = util.constructBeforeRecord(msg)!; - -// return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); -// } -// } else if (msg.tag == 'truncate') { -// let tables: storage.SourceTable[] = []; -// for (let relation of msg.relations) { -// const table = this.getTable(getRelId(relation)); -// tables.push(table); -// } -// return await batch.truncate(tables); -// } -// return null; -// } - -// async replicate() { -// try { -// // If anything errors here, the entire replication process is halted, and -// // all connections automatically closed, including this one. -// const replicationConnection = await this.connections.replicationConnection(); -// await this.initReplication(replicationConnection); -// await this.streamChanges(replicationConnection); -// } catch (e) { -// await this.storage.reportError(e); -// throw e; -// } -// } - -// async initReplication(replicationConnection: pgwire.PgConnection) { -// const result = await this.initSlot(); -// if (result.needsInitialSync) { -// await this.startInitialReplication(replicationConnection); -// } -// } - -// async streamChanges(replicationConnection: pgwire.PgConnection) { -// // When changing any logic here, check /docs/wal-lsns.md. - -// const replicationStream = replicationConnection.logicalReplication({ -// slot: this.slot_name, -// options: { -// proto_version: '1', -// publication_names: this.publication_name -// } -// }); -// this.startedStreaming = true; - -// // Auto-activate as soon as initial replication is done -// await this.storage.autoActivate(); - -// await this.storage.startBatch({}, async (batch) => { -// // Replication never starts in the middle of a transaction -// let inTx = false; -// let count = 0; - -// for await (const chunk of replicationStream.pgoutputDecode()) { -// await touch(); - -// if (this.abort_signal.aborted) { -// break; -// } - -// // chunkLastLsn may come from normal messages in the chunk, -// // or from a PrimaryKeepalive message. -// const { messages, lastLsn: chunkLastLsn } = chunk; - -// for (const msg of messages) { -// if (msg.tag == 'relation') { -// await this.handleRelation(batch, getPgOutputRelation(msg), true); -// } else if (msg.tag == 'begin') { -// inTx = true; -// } else if (msg.tag == 'commit') { -// Metrics.getInstance().transactions_replicated_total.add(1); -// inTx = false; -// await batch.commit(msg.lsn!); -// await this.ack(msg.lsn!, replicationStream); -// } else { -// if (count % 100 == 0) { -// logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); -// } - -// count += 1; -// const result = await this.writeChange(batch, msg); -// } -// } - -// if (!inTx) { -// // In a transaction, we ack and commit according to the transaction progress. -// // Outside transactions, we use the PrimaryKeepalive messages to advance progress. -// // Big caveat: This _must not_ be used to skip individual messages, since this LSN -// // may be in the middle of the next transaction. -// // It must only be used to associate checkpoints with LSNs. -// if (await batch.keepalive(chunkLastLsn)) { -// await this.ack(chunkLastLsn, replicationStream); -// } -// } - -// Metrics.getInstance().chunks_replicated_total.add(1); -// } -// }); -// } - -// async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { -// if (lsn == ZERO_LSN) { -// return; -// } - -// replicationStream.ack(lsn); -// } -// } - -// async function touch() { -// // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. -// // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, -// // or reduce PING_INTERVAL here. -// return container.probes.touch(); -// } +import * as pgwire from '@powersync/service-jpgwire'; +import * as storage from '@powersync/service-core/dist/storage/storage-index.js'; +import * as util from '../utils/pgwire_utils.js'; +import { container, errors, logger } from '@powersync/lib-services-framework'; +import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; +import { getPgOutputRelation, getRelId } from './PgRelation.js'; +import { WalConnection } from './WalConnection.js'; +import { Metrics, SourceEntityDescriptor } from '@powersync/service-core'; +import { checkSourceConfiguration, getReplicationIdentityColumns } from './replication-utils.js'; +import { PgManager } from './PgManager.js'; + +export const ZERO_LSN = '00000000/00000000'; + +export interface WalStreamOptions { + connections: PgManager; + factory: storage.BucketStorageFactory; + storage: storage.SyncRulesBucketStorage; + abort_signal: AbortSignal; +} + +interface InitResult { + needsInitialSync: boolean; +} + +export class MissingReplicationSlotError extends Error { + constructor(message: string) { + super(message); + } +} + +export class WalStream { + sync_rules: SqlSyncRules; + group_id: number; + + wal_connection: WalConnection; + connection_id = 1; + + private readonly storage: storage.SyncRulesBucketStorage; + + private slot_name: string; + + private connections: PgManager; + + private abort_signal: AbortSignal; + + private relation_cache = new Map(); + + private startedStreaming = false; + + constructor(options: WalStreamOptions) { + this.storage = options.storage; + this.sync_rules = options.storage.sync_rules; + this.group_id = options.storage.group_id; + this.slot_name = options.storage.slot_name; + this.connections = options.connections; + + this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); + this.abort_signal = options.abort_signal; + this.abort_signal.addEventListener( + 'abort', + () => { + if (this.startedStreaming) { + // Ping to speed up cancellation of streaming replication + // We're not using pg_snapshot here, since it could be in the middle of + // an initial replication transaction. + const promise = util.retriedQuery( + this.connections.pool, + `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` + ); + promise.catch((e) => { + // Failures here are okay - this only speeds up stopping the process. + logger.warn('Failed to ping connection', e); + }); + } else { + // If we haven't started streaming yet, it could be due to something like + // and invalid password. In that case, don't attempt to ping. + } + }, + { once: true } + ); + } + + get publication_name() { + return this.wal_connection.publication_name; + } + + get connectionTag() { + return this.wal_connection.connectionTag; + } + + get stopped() { + return this.abort_signal.aborted; + } + + async getQualifiedTableNames( + batch: storage.BucketStorageBatch, + db: pgwire.PgConnection, + tablePattern: TablePattern + ): Promise { + const schema = tablePattern.schema; + if (tablePattern.connectionTag != this.connectionTag) { + return []; + } + + let tableRows: any[]; + const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; + if (tablePattern.isWildcard) { + const result = await db.query({ + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname LIKE $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + tableRows = pgwire.pgwireRows(result); + } else { + const result = await db.query({ + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname = $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + + tableRows = pgwire.pgwireRows(result); + } + let result: storage.SourceTable[] = []; + + for (let row of tableRows) { + const name = row.table_name as string; + if (typeof row.relid != 'bigint') { + throw new Error(`missing relid for ${name}`); + } + const relid = Number(row.relid as bigint); + + if (prefix && !name.startsWith(prefix)) { + continue; + } + + const rs = await db.query({ + statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, + params: [ + { type: 'varchar', value: this.publication_name }, + { type: 'varchar', value: tablePattern.schema }, + { type: 'varchar', value: name } + ] + }); + if (rs.rows.length == 0) { + logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); + continue; + } + + const cresult = await getReplicationIdentityColumns(db, relid); + + const table = await this.handleRelation( + batch, + { + name, + schema, + objectId: relid, + replicationColumns: cresult.replicationColumns + } as SourceEntityDescriptor, + false + ); + + result.push(table); + } + return result; + } + + async initSlot(): Promise { + await checkSourceConfiguration(this.connections.pool); + + const slotName = this.slot_name; + + const status = await this.storage.getStatus(); + if (status.snapshot_done && status.checkpoint_lsn) { + logger.info(`${slotName} Initial replication already done`); + + let last_error = null; + + // Check that replication slot exists + for (let i = 120; i >= 0; i--) { + await touch(); + + if (i == 0) { + container.reporter.captureException(last_error, { + level: errors.ErrorSeverity.ERROR, + metadata: { + replication_slot: slotName + } + }); + + throw last_error; + } + try { + // We peek a large number of changes here, to make it more likely to pick up replication slot errors. + // For example, "publication does not exist" only occurs here if the peek actually includes changes related + // to the slot. + await this.connections.pool.query({ + statement: `SELECT * + FROM pg_catalog.pg_logical_slot_peek_binary_changes($1, NULL, 1000, 'proto_version', '1', + 'publication_names', $2)`, + params: [ + { type: 'varchar', value: slotName }, + { type: 'varchar', value: this.publication_name } + ] + }); + // Success + logger.info(`Slot ${slotName} appears healthy`); + return { needsInitialSync: false }; + } catch (e) { + last_error = e; + logger.warn(`${slotName} Replication slot error`, e); + + if (this.stopped) { + throw e; + } + + // Could also be `publication "powersync" does not exist`, although this error may show up much later + // in some cases. + + if ( + /incorrect prev-link/.test(e.message) || + /replication slot.*does not exist/.test(e.message) || + /publication.*does not exist/.test(e.message) + ) { + container.reporter.captureException(e, { + level: errors.ErrorSeverity.WARNING, + metadata: { + try_index: i, + replication_slot: slotName + } + }); + // Sample: record with incorrect prev-link 10000/10000 at 0/18AB778 + // Seen during development. Some internal error, fixed by re-creating slot. + // + // Sample: publication "powersync" does not exist + // Happens when publication deleted or never created. + // Slot must be re-created in this case. + logger.info(`${slotName} does not exist anymore, will create new slot`); + + throw new MissingReplicationSlotError(`Replication slot ${slotName} does not exist anymore`); + } + // Try again after a pause + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } + } + + return { needsInitialSync: true }; + } + + async estimatedCount(db: pgwire.PgConnection, table: storage.SourceTable): Promise { + const results = await db.query({ + statement: `SELECT reltuples::bigint AS estimate +FROM pg_class +WHERE oid = $1::regclass`, + params: [{ value: table.qualifiedName, type: 'varchar' }] + }); + const row = results.rows[0]; + if ((row?.[0] ?? -1n) == -1n) { + return '?'; + } else { + return `~${row[0]}`; + } + } + + /** + * Start initial replication. + * + * If (partial) replication was done before on this slot, this clears the state + * and starts again from scratch. + */ + async startInitialReplication(replicationConnection: pgwire.PgConnection) { + // If anything here errors, the entire replication process is aborted, + // and all connections closed, including this one. + const db = await this.connections.snapshotConnection(); + + const slotName = this.slot_name; + + await this.storage.clear(); + + await db.query({ + statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', + params: [{ type: 'varchar', value: slotName }] + }); + + // We use the replication connection here, not a pool. + // This connection needs to stay open at least until the snapshot is used below. + const result = await replicationConnection.query( + `CREATE_REPLICATION_SLOT ${slotName} LOGICAL pgoutput EXPORT_SNAPSHOT` + ); + const columns = result.columns; + const row = result.rows[0]!; + if (columns[1]?.name != 'consistent_point' || columns[2]?.name != 'snapshot_name' || row == null) { + throw new Error(`Invalid CREATE_REPLICATION_SLOT output: ${JSON.stringify(columns)}`); + } + // This LSN could be used in initialReplication below. + // But it's also safe to just use ZERO_LSN - we won't get any changes older than this lsn + // with streaming replication. + const lsn = pgwire.lsnMakeComparable(row[1]); + const snapshot = row[2]; + logger.info(`Created replication slot ${slotName} at ${lsn} with snapshot ${snapshot}`); + + // https://stackoverflow.com/questions/70160769/postgres-logical-replication-starting-from-given-lsn + await db.query('BEGIN'); + // Use the snapshot exported above. + // Using SERIALIZABLE isolation level may give stronger guarantees, but that complicates + // the replication slot + snapshot above. And we still won't have SERIALIZABLE + // guarantees with streaming replication. + // See: ./docs/serializability.md for details. + // + // Another alternative here is to use the same pgwire connection for initial replication as well, + // instead of synchronizing a separate transaction to the snapshot. + + try { + await db.query(`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`); + await db.query(`SET TRANSACTION READ ONLY`); + await db.query(`SET TRANSACTION SNAPSHOT '${snapshot}'`); + + // Disable statement timeout for the duration of this transaction. + // On Supabase, the default is 2 minutes. + await db.query(`set local statement_timeout = 0`); + + logger.info(`${slotName} Starting initial replication`); + await this.initialReplication(db, lsn); + logger.info(`${slotName} Initial replication done`); + await db.query('COMMIT'); + } catch (e) { + await db.query('ROLLBACK'); + throw e; + } + } + + async initialReplication(db: pgwire.PgConnection, lsn: string) { + const sourceTables = this.sync_rules.getSourceTables(); + await this.storage.startBatch({}, async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, db, tablePattern); + for (let table of tables) { + await this.snapshotTable(batch, db, table); + await batch.markSnapshotDone([table], lsn); + + await touch(); + } + } + await batch.commit(lsn); + }); + } + + static *getQueryData(results: Iterable): Generator { + for (let row of results) { + yield toSyncRulesRow(row); + } + } + + private async snapshotTable(batch: storage.BucketStorageBatch, db: pgwire.PgConnection, table: storage.SourceTable) { + logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); + const estimatedCount = await this.estimatedCount(db, table); + let at = 0; + let lastLogIndex = 0; + const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); + let columns: { i: number; name: string }[] = []; + // pgwire streams rows in chunks. + // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. + + for await (let chunk of cursor) { + if (chunk.tag == 'RowDescription') { + let i = 0; + columns = chunk.payload.map((c) => { + return { i: i++, name: c.name }; + }); + continue; + } + + const rows = chunk.rows.map((row) => { + let q: DatabaseInputRow = {}; + for (let c of columns) { + q[c.name] = row[c.i]; + } + return q; + }); + if (rows.length > 0 && at - lastLogIndex >= 5000) { + logger.info(`${this.slot_name} Replicating ${table.qualifiedName} ${at}/${estimatedCount}`); + lastLogIndex = at; + } + if (this.abort_signal.aborted) { + throw new Error(`Aborted initial replication of ${this.slot_name}`); + } + + for (let record of WalStream.getQueryData(rows)) { + // This auto-flushes when the batch reaches its size limit + await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); + } + at += rows.length; + Metrics.getInstance().rows_replicated_total.add(rows.length); + + await touch(); + } + + await batch.flush(); + } + + async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { + if (!descriptor.objectId) { + throw new Error('objectId expected'); + } + const result = await this.storage.resolveTable({ + group_id: this.group_id, + connection_id: this.connection_id, + connection_tag: this.connectionTag, + entity_descriptor: descriptor, + sync_rules: this.sync_rules + }); + this.relation_cache.set(descriptor.objectId, result.table); + + // Drop conflicting tables. This includes for example renamed tables. + await batch.drop(result.dropTables); + + // Snapshot if: + // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) + // 2. Snapshot is not already done, AND: + // 3. The table is used in sync rules. + const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; + + if (shouldSnapshot) { + // Truncate this table, in case a previous snapshot was interrupted. + await batch.truncate([result.table]); + + let lsn: string = ZERO_LSN; + // Start the snapshot inside a transaction. + // We use a dedicated connection for this. + const db = await this.connections.snapshotConnection(); + try { + await db.query('BEGIN'); + try { + // Get the current LSN. + // The data will only be consistent once incremental replication + // has passed that point. + const rs = await db.query(`select pg_current_wal_lsn() as lsn`); + lsn = rs.rows[0][0]; + await this.snapshotTable(batch, db, result.table); + await db.query('COMMIT'); + } catch (e) { + await db.query('ROLLBACK'); + throw e; + } + } finally { + await db.end(); + } + const [table] = await batch.markSnapshotDone([result.table], lsn); + return table; + } + + return result.table; + } + + private getTable(relationId: number): storage.SourceTable { + const table = this.relation_cache.get(relationId); + if (table == null) { + // We should always receive a replication message before the relation is used. + // If we can't find it, it's a bug. + throw new Error(`Missing relation cache for ${relationId}`); + } + return table; + } + + async writeChange( + batch: storage.BucketStorageBatch, + msg: pgwire.PgoutputMessage + ): Promise { + if (msg.lsn == null) { + return null; + } + if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { + const table = this.getTable(getRelId(msg.relation)); + if (!table.syncAny) { + logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); + return null; + } + + if (msg.tag == 'insert') { + Metrics.getInstance().rows_replicated_total.add(1); + const baseRecord = util.constructAfterRecord(msg); + return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); + } else if (msg.tag == 'update') { + Metrics.getInstance().rows_replicated_total.add(1); + // "before" may be null if the replica id columns are unchanged + // It's fine to treat that the same as an insert. + const before = util.constructBeforeRecord(msg); + const after = util.constructAfterRecord(msg); + return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); + } else if (msg.tag == 'delete') { + Metrics.getInstance().rows_replicated_total.add(1); + const before = util.constructBeforeRecord(msg)!; + + return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); + } + } else if (msg.tag == 'truncate') { + let tables: storage.SourceTable[] = []; + for (let relation of msg.relations) { + const table = this.getTable(getRelId(relation)); + tables.push(table); + } + return await batch.truncate(tables); + } + return null; + } + + async replicate() { + try { + // If anything errors here, the entire replication process is halted, and + // all connections automatically closed, including this one. + const replicationConnection = await this.connections.replicationConnection(); + await this.initReplication(replicationConnection); + await this.streamChanges(replicationConnection); + } catch (e) { + await this.storage.reportError(e); + throw e; + } + } + + async initReplication(replicationConnection: pgwire.PgConnection) { + const result = await this.initSlot(); + if (result.needsInitialSync) { + await this.startInitialReplication(replicationConnection); + } + } + + async streamChanges(replicationConnection: pgwire.PgConnection) { + // When changing any logic here, check /docs/wal-lsns.md. + + const replicationStream = replicationConnection.logicalReplication({ + slot: this.slot_name, + options: { + proto_version: '1', + publication_names: this.publication_name + } + }); + this.startedStreaming = true; + + // Auto-activate as soon as initial replication is done + await this.storage.autoActivate(); + + await this.storage.startBatch({}, async (batch) => { + // Replication never starts in the middle of a transaction + let inTx = false; + let count = 0; + + for await (const chunk of replicationStream.pgoutputDecode()) { + await touch(); + + if (this.abort_signal.aborted) { + break; + } + + // chunkLastLsn may come from normal messages in the chunk, + // or from a PrimaryKeepalive message. + const { messages, lastLsn: chunkLastLsn } = chunk; + + for (const msg of messages) { + if (msg.tag == 'relation') { + await this.handleRelation(batch, getPgOutputRelation(msg), true); + } else if (msg.tag == 'begin') { + inTx = true; + } else if (msg.tag == 'commit') { + Metrics.getInstance().transactions_replicated_total.add(1); + inTx = false; + await batch.commit(msg.lsn!); + await this.ack(msg.lsn!, replicationStream); + } else { + if (count % 100 == 0) { + logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); + } + + count += 1; + const result = await this.writeChange(batch, msg); + } + } + + if (!inTx) { + // In a transaction, we ack and commit according to the transaction progress. + // Outside transactions, we use the PrimaryKeepalive messages to advance progress. + // Big caveat: This _must not_ be used to skip individual messages, since this LSN + // may be in the middle of the next transaction. + // It must only be used to associate checkpoints with LSNs. + if (await batch.keepalive(chunkLastLsn)) { + await this.ack(chunkLastLsn, replicationStream); + } + } + + Metrics.getInstance().chunks_replicated_total.add(1); + } + }); + } + + async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { + if (lsn == ZERO_LSN) { + return; + } + + replicationStream.ack(lsn); + } +} + +async function touch() { + // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. + // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, + // or reduce PING_INTERVAL here. + return container.probes.touch(); +} diff --git a/modules/module-postgres/src/replication/WalStreamManager.ts b/modules/module-postgres/src/replication/WalStreamManager.ts index 1533a37f6..53fd1ee8f 100644 --- a/modules/module-postgres/src/replication/WalStreamManager.ts +++ b/modules/module-postgres/src/replication/WalStreamManager.ts @@ -1,213 +1,225 @@ -// import * as pgwire from '@powersync/service-jpgwire'; -// import { hrtime } from 'node:process'; - -// import * as storage from '../storage/storage-index.js'; -// import * as util from '../util/util-index.js'; - -// import { DefaultErrorRateLimiter } from './ErrorRateLimiter.js'; -// import { WalStreamRunner } from './WalStreamRunner.js'; -// import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; -// import { container, logger } from '@powersync/lib-services-framework'; - -// // 5 minutes -// const PING_INTERVAL = 1_000_000_000n * 300n; - -// export class WalStreamManager { -// private streams = new Map(); - -// private system: CorePowerSyncSystem; - -// private stopped = false; - -// // First ping is only after 5 minutes, not when starting -// private lastPing = hrtime.bigint(); - -// private storage: storage.BucketStorageFactory; - -// /** -// * This limits the effect of retries when there is a persistent issue. -// */ -// private rateLimiter = new DefaultErrorRateLimiter(); - -// constructor(system: CorePowerSyncSystem) { -// this.system = system; -// this.storage = system.storage; -// } - -// start() { -// this.runLoop().catch((e) => { -// logger.error(`Fatal WalStream error`, e); -// container.reporter.captureException(e); -// setTimeout(() => { -// process.exit(1); -// }, 1000); -// }); -// } - -// async stop() { -// this.stopped = true; -// let promises: Promise[] = []; -// for (let stream of this.streams.values()) { -// promises.push(stream.stop()); -// } -// await Promise.all(promises); -// } - -// private async runLoop() { -// const configured_sync_rules = await util.loadSyncRules(this.system.config); -// let configured_lock: storage.ReplicationLock | undefined = undefined; -// if (configured_sync_rules != null) { -// logger.info('Loading sync rules from configuration'); -// try { -// // Configure new sync rules, if it has changed. -// // In that case, also immediately take out a lock, so that another process doesn't start replication on it. -// const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { -// lock: true -// }); -// if (lock) { -// configured_lock = lock; -// } -// } catch (e) { -// // Log, but continue with previous sync rules -// logger.error(`Failed to load sync rules from configuration`, e); -// } -// } else { -// logger.info('No sync rules configured - configure via API'); -// } -// while (!this.stopped) { -// await container.probes.touch(); -// try { -// const pool = this.system.pgwire_pool; -// if (pool) { -// await this.refresh({ configured_lock }); -// // The lock is only valid on the first refresh. -// configured_lock = undefined; - -// // TODO: Ping on all connections when we have multiple -// // Perhaps WalStreamRunner would be a better place to do pings? -// // We don't ping while in error retry back-off, to avoid having too -// // many authentication failures. -// if (this.rateLimiter.mayPing()) { -// await this.ping(pool); -// } -// } -// } catch (e) { -// logger.error(`Failed to refresh wal streams`, e); -// } -// await new Promise((resolve) => setTimeout(resolve, 5000)); -// } -// } - -// /** -// * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. -// * -// * The old WAL files are only deleted once no replication slot still references it. -// * -// * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot -// * advancing**. -// * -// * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. -// * -// * **This may be a bug in pgwire or how we're using it. -// */ -// private async ping(db: pgwire.PgClient) { -// const now = hrtime.bigint(); -// if (now - this.lastPing >= PING_INTERVAL) { -// try { -// await db.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`); -// } catch (e) { -// logger.warn(`Failed to ping`, e); -// } -// this.lastPing = now; -// } -// } - -// private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { -// if (this.stopped) { -// return; -// } - -// let configured_lock = options?.configured_lock; - -// const existingStreams = new Map(this.streams.entries()); -// const replicating = await this.storage.getReplicatingSyncRules(); -// const newStreams = new Map(); -// for (let syncRules of replicating) { -// const existing = existingStreams.get(syncRules.id); -// if (existing && !existing.stopped) { -// // No change -// existingStreams.delete(syncRules.id); -// newStreams.set(syncRules.id, existing); -// } else if (existing && existing.stopped) { -// // Stopped (e.g. fatal error, slot rename). -// // Remove from the list. Next refresh call will restart the stream. -// existingStreams.delete(syncRules.id); -// } else { -// // New (or resume after restart) -// try { -// let lock: storage.ReplicationLock; -// if (configured_lock?.sync_rules_id == syncRules.id) { -// lock = configured_lock; -// } else { -// lock = await syncRules.lock(); -// } -// const parsed = syncRules.parsed(); -// const storage = this.storage.getInstance(parsed); -// const stream = new WalStreamRunner({ -// factory: this.storage, -// storage: storage, -// source_db: this.system.config.connection!, -// lock, -// rateLimiter: this.rateLimiter -// }); -// newStreams.set(syncRules.id, stream); -// stream.start(); -// } catch (e) { -// // Could be a sync rules parse error, -// // for example from stricter validation that was added. -// // This will be retried every couple of seconds. -// // When new (valid) sync rules are deployed and processed, this one be disabled. -// logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); -// } -// } -// } - -// this.streams = newStreams; - -// // TODO: Should this termination be happening in the "background" instead? -// // That becomes tricky to manage - -// for (let stream of existingStreams.values()) { -// // Old - stop and remove. -// try { -// await stream.terminate(); -// } catch (e) { -// // This will be retried -// logger.warn(`Failed to terminate ${stream.slot_name}`, e); -// } -// } - -// // Sync rules stopped previously or by a different process. -// const stopped = await this.storage.getStoppedSyncRules(); -// for (let syncRules of stopped) { -// try { -// const lock = await syncRules.lock(); -// try { -// const parsed = syncRules.parsed(); -// const storage = this.storage.getInstance(parsed); -// const stream = new WalStreamRunner({ -// factory: this.storage, -// storage: storage, -// source_db: this.system.config.connection!, -// lock -// }); -// await stream.terminate(); -// } finally { -// await lock.release(); -// } -// } catch (e) { -// logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); -// } -// } -// } -// } +import { hrtime } from 'node:process'; + +import * as storage from '../../../../packages/service-core/src/storage/storage-index.js'; +import * as util from '../../../../packages/service-core/src/util/util-index.js'; + +import { DefaultErrorRateLimiter } from '@powersync/service-core'; + +import { Replicator } from '@powersync/service-core'; +import { WalStreamRunner } from './WalStreamRunner.js'; +import { ServiceContext } from '@powersync/service-core'; + +// 5 minutes +const PING_INTERVAL = 1_000_000_000n * 300n; + +export interface WalStreamManagerOptions { + id: string; + serviceContext: ServiceContext; + storage: storage.BucketStorageFactory; + connectionPool: util.PgManager; +} + +export class WalStreamManager implements Replicator { + private streams = new Map(); + + private stopped = false; + + // First ping is only after 5 minutes, not when starting + private lastPing = hrtime.bigint(); + + /** + * This limits the effect of retries when there is a persistent issue. + */ + private rateLimiter = new DefaultErrorRateLimiter(); + + constructor(private options: WalStreamManagerOptions) {} + + public get id() { + return this.options.id; + } + + private get storage() { + return this.options.storage; + } + + private get serviceContext() { + return this.options.serviceContext; + } + + start() { + this.runLoop().catch((e) => { + logger.error(`Fatal WalStream error`, e); + container.reporter.captureException(e); + setTimeout(() => { + process.exit(1); + }, 1000); + }); + } + + async stop() { + this.stopped = true; + let promises: Promise[] = []; + for (let stream of this.streams.values()) { + promises.push(stream.stop()); + } + await Promise.all(promises); + } + + private async runLoop() { + const configured_sync_rules = await util.loadSyncRules(this.serviceContext.config); + let configured_lock: storage.ReplicationLock | undefined = undefined; + if (configured_sync_rules != null) { + logger.info('Loading sync rules from configuration'); + try { + // Configure new sync rules, if it has changed. + // In that case, also immediately take out a lock, so that another process doesn't start replication on it. + const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { + lock: true + }); + if (lock) { + configured_lock = lock; + } + } catch (e) { + // Log, but continue with previous sync rules + logger.error(`Failed to load sync rules from configuration`, e); + } + } else { + logger.info('No sync rules configured - configure via API'); + } + while (!this.stopped) { + await container.probes.touch(); + try { + const pool = this.serviceContext.pgwire_pool; + if (pool) { + await this.refresh({ configured_lock }); + // The lock is only valid on the first refresh. + configured_lock = undefined; + + // TODO: Ping on all connections when we have multiple + // Perhaps WalStreamRunner would be a better place to do pings? + // We don't ping while in error retry back-off, to avoid having too + // many authentication failures. + if (this.rateLimiter.mayPing()) { + await this.ping(pool); + } + } + } catch (e) { + logger.error(`Failed to refresh wal streams`, e); + } + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + + /** + * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. + * + * The old WAL files are only deleted once no replication slot still references it. + * + * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot + * advancing**. + * + * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. + * + * **This may be a bug in pgwire or how we're using it. + */ + private async ping(db: pgwire.PgClient) { + const now = hrtime.bigint(); + if (now - this.lastPing >= PING_INTERVAL) { + try { + await db.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`); + } catch (e) { + logger.warn(`Failed to ping`, e); + } + this.lastPing = now; + } + } + + private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { + if (this.stopped) { + return; + } + + let configured_lock = options?.configured_lock; + + const existingStreams = new Map(this.streams.entries()); + const replicating = await this.storage.getReplicatingSyncRules(); + const newStreams = new Map(); + for (let syncRules of replicating) { + const existing = existingStreams.get(syncRules.id); + if (existing && !existing.stopped) { + // No change + existingStreams.delete(syncRules.id); + newStreams.set(syncRules.id, existing); + } else if (existing && existing.stopped) { + // Stopped (e.g. fatal error, slot rename). + // Remove from the list. Next refresh call will restart the stream. + existingStreams.delete(syncRules.id); + } else { + // New (or resume after restart) + try { + let lock: storage.ReplicationLock; + if (configured_lock?.sync_rules_id == syncRules.id) { + lock = configured_lock; + } else { + lock = await syncRules.lock(); + } + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + const stream = new WalStreamRunner({ + factory: this.storage, + storage: storage, + source_db: this.serviceContext.config.connection!, + lock, + rateLimiter: this.rateLimiter + }); + newStreams.set(syncRules.id, stream); + stream.start(); + } catch (e) { + // Could be a sync rules parse error, + // for example from stricter validation that was added. + // This will be retried every couple of seconds. + // When new (valid) sync rules are deployed and processed, this one be disabled. + logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); + } + } + } + + this.streams = newStreams; + + // TODO: Should this termination be happening in the "background" instead? + // That becomes tricky to manage + + for (let stream of existingStreams.values()) { + // Old - stop and remove. + try { + await stream.terminate(); + } catch (e) { + // This will be retried + logger.warn(`Failed to terminate ${stream.slot_name}`, e); + } + } + + // Sync rules stopped previously or by a different process. + const stopped = await this.storage.getStoppedSyncRules(); + for (let syncRules of stopped) { + try { + const lock = await syncRules.lock(); + try { + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + const stream = new WalStreamRunner({ + factory: this.storage, + storage: storage, + source_db: this.serviceContext.config.connection!, + lock + }); + await stream.terminate(); + } finally { + await lock.release(); + } + } catch (e) { + logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); + } + } + } +} diff --git a/modules/module-postgres/src/replication/WalStreamRunner.ts b/modules/module-postgres/src/replication/WalStreamRunner.ts index e0845fd22..ee6fbbe8f 100644 --- a/modules/module-postgres/src/replication/WalStreamRunner.ts +++ b/modules/module-postgres/src/replication/WalStreamRunner.ts @@ -1,180 +1,178 @@ -// import * as pgwire from '@powersync/service-jpgwire'; - -// import * as storage from '../storage/storage-index.js'; -// import * as util from '../util/util-index.js'; - -// import { ErrorRateLimiter } from './ErrorRateLimiter.js'; -// import { MissingReplicationSlotError, WalStream } from './WalStream.js'; -// import { ResolvedConnection } from '../util/config/types.js'; -// import { container, logger } from '@powersync/lib-services-framework'; - -// export interface WalStreamRunnerOptions { -// factory: storage.BucketStorageFactory; -// storage: storage.SyncRulesBucketStorage; -// source_db: ResolvedConnection; -// lock: storage.ReplicationLock; -// rateLimiter?: ErrorRateLimiter; -// } - -// export class WalStreamRunner { -// private abortController = new AbortController(); - -// private runPromise?: Promise; - -// private connections: util.PgManager | null = null; - -// private rateLimiter?: ErrorRateLimiter; - -// constructor(public options: WalStreamRunnerOptions) { -// this.rateLimiter = options.rateLimiter; -// } - -// start() { -// this.runPromise = this.run(); -// } - -// get slot_name() { -// return this.options.storage.slot_name; -// } - -// get stopped() { -// return this.abortController.signal.aborted; -// } - -// async run() { -// try { -// await this.replicateLoop(); -// } catch (e) { -// // Fatal exception -// container.reporter.captureException(e, { -// metadata: { -// replication_slot: this.slot_name -// } -// }); -// logger.error(`Replication failed on ${this.slot_name}`, e); - -// if (e instanceof MissingReplicationSlotError) { -// // This stops replication on this slot, and creates a new slot -// await this.options.storage.factory.slotRemoved(this.slot_name); -// } -// } finally { -// this.abortController.abort(); -// } -// await this.options.lock.release(); -// } - -// async replicateLoop() { -// while (!this.stopped) { -// await this.replicateOnce(); - -// if (!this.stopped) { -// await new Promise((resolve) => setTimeout(resolve, 5000)); -// } -// } -// } - -// async replicateOnce() { -// // New connections on every iteration (every error with retry), -// // otherwise we risk repeating errors related to the connection, -// // such as caused by cached PG schemas. -// let connections = new util.PgManager(this.options.source_db, { -// // Pool connections are only used intermittently. -// idleTimeout: 30_000, -// maxSize: 2 -// }); -// this.connections = connections; -// try { -// await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); -// if (this.stopped) { -// return; -// } -// const stream = new WalStream({ -// abort_signal: this.abortController.signal, -// factory: this.options.factory, -// storage: this.options.storage, -// connections -// }); -// await stream.replicate(); -// } catch (e) { -// logger.error(`Replication error`, e); -// if (e.cause != null) { -// // Example: -// // PgError.conn_ended: Unable to do postgres query on ended connection -// // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) -// // at stream.next () -// // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) -// // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) -// // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) -// // ... -// // cause: TypeError: match is not iterable -// // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) -// // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) -// // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) -// // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) -// // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) -// // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) -// // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), -// // [Symbol(pg.ErrorCode)]: 'conn_ended', -// // [Symbol(pg.ErrorResponse)]: undefined -// // } -// // Without this additional log, the cause would not be visible in the logs. -// logger.error(`cause`, e.cause); -// } -// if (e instanceof MissingReplicationSlotError) { -// throw e; -// } else { -// // Report the error if relevant, before retrying -// container.reporter.captureException(e, { -// metadata: { -// replication_slot: this.slot_name -// } -// }); -// // This sets the retry delay -// this.rateLimiter?.reportError(e); -// } -// } finally { -// this.connections = null; -// if (connections != null) { -// await connections.end(); -// } -// } -// } - -// /** -// * This will also release the lock if start() was called earlier. -// */ -// async stop(options?: { force?: boolean }) { -// logger.info(`${this.slot_name} Stopping replication`); -// // End gracefully -// this.abortController.abort(); - -// if (options?.force) { -// // destroy() is more forceful. -// await this.connections?.destroy(); -// } -// await this.runPromise; -// } - -// /** -// * Terminate this replication stream. This drops the replication slot and deletes the replication data. -// * -// * Stops replication if needed. -// */ -// async terminate(options?: { force?: boolean }) { -// logger.info(`${this.slot_name} Terminating replication`); -// await this.stop(options); - -// const slotName = this.slot_name; -// const db = await pgwire.connectPgWire(this.options.source_db, { type: 'standard' }); -// try { -// await db.query({ -// statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', -// params: [{ type: 'varchar', value: slotName }] -// }); -// } finally { -// await db.end(); -// } - -// await this.options.storage.terminate(); -// } -// } +import * as pgwire from '@powersync/service-jpgwire'; +import * as storage from '@powersync/service-core/dist/storage/storage-index.js'; +import { MissingReplicationSlotError, WalStream } from './WalStream.js'; +import { container, logger } from '@powersync/lib-services-framework'; +import { PgManager } from './PgManager.js'; +import { ErrorRateLimiter } from '@powersync/service-core'; +import { NormalizedPostgresConnectionConfig } from '../types/types.js'; + +export interface WalStreamRunnerOptions { + factory: storage.BucketStorageFactory; + storage: storage.SyncRulesBucketStorage; + sourceDbConfig: NormalizedPostgresConnectionConfig; + lock: storage.ReplicationLock; + rateLimiter?: ErrorRateLimiter; +} + +export class WalStreamRunner { + private abortController = new AbortController(); + + private runPromise?: Promise; + + private connections: PgManager | null = null; + + private rateLimiter?: ErrorRateLimiter; + + constructor(public options: WalStreamRunnerOptions) { + this.rateLimiter = options.rateLimiter; + } + + start() { + this.runPromise = this.run(); + } + + get slot_name() { + return this.options.storage.slot_name; + } + + get stopped() { + return this.abortController.signal.aborted; + } + + async run() { + try { + await this.replicateLoop(); + } catch (e) { + // Fatal exception + container.reporter.captureException(e, { + metadata: { + replication_slot: this.slot_name + } + }); + logger.error(`Replication failed on ${this.slot_name}`, e); + + if (e instanceof MissingReplicationSlotError) { + // This stops replication on this slot, and creates a new slot + await this.options.storage.factory.slotRemoved(this.slot_name); + } + } finally { + this.abortController.abort(); + } + await this.options.lock.release(); + } + + async replicateLoop() { + while (!this.stopped) { + await this.replicateOnce(); + + if (!this.stopped) { + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + } + + async replicateOnce() { + // New connections on every iteration (every error with retry), + // otherwise we risk repeating errors related to the connection, + // such as caused by cached PG schemas. + let connections = new PgManager(this.options.sourceDbConfig, { + // Pool connections are only used intermittently. + idleTimeout: 30_000, + maxSize: 2 + }); + this.connections = connections; + try { + await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); + if (this.stopped) { + return; + } + const stream = new WalStream({ + abort_signal: this.abortController.signal, + factory: this.options.factory, + storage: this.options.storage, + connections + }); + await stream.replicate(); + } catch (e) { + logger.error(`Replication error`, e); + if (e.cause != null) { + // Example: + // PgError.conn_ended: Unable to do postgres query on ended connection + // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) + // at stream.next () + // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) + // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) + // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) + // ... + // cause: TypeError: match is not iterable + // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) + // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) + // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) + // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) + // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) + // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), + // [Symbol(pg.ErrorCode)]: 'conn_ended', + // [Symbol(pg.ErrorResponse)]: undefined + // } + // Without this additional log, the cause would not be visible in the logs. + logger.error(`cause`, e.cause); + } + if (e instanceof MissingReplicationSlotError) { + throw e; + } else { + // Report the error if relevant, before retrying + container.reporter.captureException(e, { + metadata: { + replication_slot: this.slot_name + } + }); + // This sets the retry delay + this.rateLimiter?.reportError(e); + } + } finally { + this.connections = null; + if (connections != null) { + await connections.end(); + } + } + } + + /** + * This will also release the lock if start() was called earlier. + */ + async stop(options?: { force?: boolean }) { + logger.info(`${this.slot_name} Stopping replication`); + // End gracefully + this.abortController.abort(); + + if (options?.force) { + // destroy() is more forceful. + await this.connections?.destroy(); + } + await this.runPromise; + } + + /** + * Terminate this replication stream. This drops the replication slot and deletes the replication data. + * + * Stops replication if needed. + */ + async terminate(options?: { force?: boolean }) { + logger.info(`${this.slot_name} Terminating replication`); + await this.stop(options); + + const slotName = this.slot_name; + const db = await pgwire.connectPgWire(this.options.sourceDbConfig, { type: 'standard' }); + try { + await db.query({ + statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', + params: [{ type: 'varchar', value: slotName }] + }); + } finally { + await db.end(); + } + + await this.options.storage.terminate(); + } +} diff --git a/packages/service-core/src/replication/WalStreamManager.ts b/packages/service-core/src/replication/WalStreamManager.ts deleted file mode 100644 index b03069c37..000000000 --- a/packages/service-core/src/replication/WalStreamManager.ts +++ /dev/null @@ -1,225 +0,0 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import { hrtime } from 'node:process'; - -import * as storage from '../storage/storage-index.js'; -import * as util from '../util/util-index.js'; - -import { DefaultErrorRateLimiter } from './ErrorRateLimiter.js'; -import { WalStreamRunner } from './WalStreamRunner.js'; -import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js'; -import { container, logger } from '@powersync/lib-services-framework'; -import { Replicator } from './core/Replicator.js'; - -// 5 minutes -const PING_INTERVAL = 1_000_000_000n * 300n; - -export interface WalStreamManagerOptions { - id: string; - system: CorePowerSyncSystem; - storage: storage.BucketStorageFactory; -} - -export class WalStreamManager implements Replicator { - private streams = new Map(); - - private stopped = false; - - // First ping is only after 5 minutes, not when starting - private lastPing = hrtime.bigint(); - - /** - * This limits the effect of retries when there is a persistent issue. - */ - private rateLimiter = new DefaultErrorRateLimiter(); - - constructor(private options: WalStreamManagerOptions) {} - - public get id() { - return this.options.id; - } - - private get storage() { - return this.options.storage; - } - - private get system() { - return this.options.system; - } - - start() { - this.runLoop().catch((e) => { - logger.error(`Fatal WalStream error`, e); - container.reporter.captureException(e); - setTimeout(() => { - process.exit(1); - }, 1000); - }); - } - - async stop() { - this.stopped = true; - let promises: Promise[] = []; - for (let stream of this.streams.values()) { - promises.push(stream.stop()); - } - await Promise.all(promises); - } - - private async runLoop() { - const configured_sync_rules = await util.loadSyncRules(this.system.config); - let configured_lock: storage.ReplicationLock | undefined = undefined; - if (configured_sync_rules != null) { - logger.info('Loading sync rules from configuration'); - try { - // Configure new sync rules, if it has changed. - // In that case, also immediately take out a lock, so that another process doesn't start replication on it. - const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { - lock: true - }); - if (lock) { - configured_lock = lock; - } - } catch (e) { - // Log, but continue with previous sync rules - logger.error(`Failed to load sync rules from configuration`, e); - } - } else { - logger.info('No sync rules configured - configure via API'); - } - while (!this.stopped) { - await container.probes.touch(); - try { - const pool = this.system.pgwire_pool; - if (pool) { - await this.refresh({ configured_lock }); - // The lock is only valid on the first refresh. - configured_lock = undefined; - - // TODO: Ping on all connections when we have multiple - // Perhaps WalStreamRunner would be a better place to do pings? - // We don't ping while in error retry back-off, to avoid having too - // many authentication failures. - if (this.rateLimiter.mayPing()) { - await this.ping(pool); - } - } - } catch (e) { - logger.error(`Failed to refresh wal streams`, e); - } - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - - /** - * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. - * - * The old WAL files are only deleted once no replication slot still references it. - * - * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot - * advancing**. - * - * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. - * - * **This may be a bug in pgwire or how we're using it. - */ - private async ping(db: pgwire.PgClient) { - const now = hrtime.bigint(); - if (now - this.lastPing >= PING_INTERVAL) { - try { - await db.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`); - } catch (e) { - logger.warn(`Failed to ping`, e); - } - this.lastPing = now; - } - } - - private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { - if (this.stopped) { - return; - } - - let configured_lock = options?.configured_lock; - - const existingStreams = new Map(this.streams.entries()); - const replicating = await this.storage.getReplicatingSyncRules(); - const newStreams = new Map(); - for (let syncRules of replicating) { - const existing = existingStreams.get(syncRules.id); - if (existing && !existing.stopped) { - // No change - existingStreams.delete(syncRules.id); - newStreams.set(syncRules.id, existing); - } else if (existing && existing.stopped) { - // Stopped (e.g. fatal error, slot rename). - // Remove from the list. Next refresh call will restart the stream. - existingStreams.delete(syncRules.id); - } else { - // New (or resume after restart) - try { - let lock: storage.ReplicationLock; - if (configured_lock?.sync_rules_id == syncRules.id) { - lock = configured_lock; - } else { - lock = await syncRules.lock(); - } - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const stream = new WalStreamRunner({ - factory: this.storage, - storage: storage, - source_db: this.system.config.connection!, - lock, - rateLimiter: this.rateLimiter - }); - newStreams.set(syncRules.id, stream); - stream.start(); - } catch (e) { - // Could be a sync rules parse error, - // for example from stricter validation that was added. - // This will be retried every couple of seconds. - // When new (valid) sync rules are deployed and processed, this one be disabled. - logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); - } - } - } - - this.streams = newStreams; - - // TODO: Should this termination be happening in the "background" instead? - // That becomes tricky to manage - - for (let stream of existingStreams.values()) { - // Old - stop and remove. - try { - await stream.terminate(); - } catch (e) { - // This will be retried - logger.warn(`Failed to terminate ${stream.slot_name}`, e); - } - } - - // Sync rules stopped previously or by a different process. - const stopped = await this.storage.getStoppedSyncRules(); - for (let syncRules of stopped) { - try { - const lock = await syncRules.lock(); - try { - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const stream = new WalStreamRunner({ - factory: this.storage, - storage: storage, - source_db: this.system.config.connection!, - lock - }); - await stream.terminate(); - } finally { - await lock.release(); - } - } catch (e) { - logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); - } - } - } -} diff --git a/packages/service-core/src/replication/core/PluggableReplicator.ts b/packages/service-core/src/replication/core/PluggableReplicator.ts new file mode 100644 index 000000000..b4b924a9e --- /dev/null +++ b/packages/service-core/src/replication/core/PluggableReplicator.ts @@ -0,0 +1,178 @@ +import * as storage from '../../storage/storage-index.js'; + +import { ReplicationAdapterFactory } from './ReplicationAdapter.js'; +import { BucketStorageFactory } from '../../storage/BucketStorage.js'; +import { container, logger } from '@powersync/lib-services-framework'; +import { SyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; +import { ReplicationJob } from './ReplicationJob.js'; +import { Replicator } from './Replicator.js'; + +export interface PluggableReplicatorOptions { + id: string; + adapterFactory: ReplicationAdapterFactory; + storage: BucketStorageFactory; + syncRuleProvider: SyncRulesProvider; +} + +/** + * A replicator manages the mechanics for replicating data from a data source to a storage bucket. + * This includes copying across the original data set and then keeping it in sync with the data source using ReplicationJobs. + * It also handles any changes to the sync rules. + */ +export class PluggableReplicator implements Replicator { + private replicationJobs = new Map(); + private stopped = false; + + constructor(private options: PluggableReplicatorOptions) {} + + public get id() { + return this.options.id; + } + + private get adapterFactory() { + return this.options.adapterFactory; + } + + private get storage() { + return this.options.storage; + } + + public async start(): Promise { + this.runLoop().catch((e) => { + logger.error(`Data source fatal replication error: ${this.adapterFactory.name}`, e); + container.reporter.captureException(e); + setTimeout(() => { + process.exit(1); + }, 1000); + }); + } + + public async stop(): Promise { + this.stopped = true; + let promises: Promise[] = []; + for (const job of this.replicationJobs.values()) { + promises.push(job.stop()); + } + await Promise.all(promises); + } + + private async runLoop() { + const syncRules = await this.options.syncRuleProvider.get(); + let configuredLock: storage.ReplicationLock | undefined = undefined; + if (syncRules != null) { + logger.info('Loaded sync rules'); + try { + // Configure new sync rules, if they have changed. + // In that case, also immediately take out a lock, so that another process doesn't start replication on it. + const { lock } = await this.storage.configureSyncRules(syncRules, { + lock: true + }); + if (lock) { + configuredLock = lock; + } + } catch (e) { + // Log, but continue with previous sync rules + logger.error(`Failed to update sync rules from configuration`, e); + } + } else { + logger.info('No sync rules configured - configure via API'); + } + while (!this.stopped) { + await container.probes.touch(); + try { + await this.refresh({ configured_lock: configuredLock }); + // The lock is only valid on the first refresh. + configuredLock = undefined; + } catch (e) { + logger.error(`Failed to refresh replication jobs: ${this.adapterFactory.name}`, e); + } + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + + private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { + if (this.stopped) { + return; + } + + let configuredLock = options?.configured_lock; + + const existingJobs = new Map(this.replicationJobs.entries()); + const replicatingSyncRules = await this.storage.getReplicatingSyncRules(); + const newJobs = new Map(); + for (let syncRules of replicatingSyncRules) { + const existingJob = existingJobs.get(syncRules.id); + if (existingJob && !existingJob.isStopped()) { + // No change + existingJobs.delete(syncRules.id); + newJobs.set(syncRules.id, existingJob); + } else if (existingJob && existingJob.isStopped()) { + // Stopped (e.g. fatal error). + // Remove from the list. Next refresh call will restart the job. + existingJobs.delete(syncRules.id); + } else { + // New sync rules were found (or resume after restart) + try { + let lock: storage.ReplicationLock; + if (configuredLock?.sync_rules_id == syncRules.id) { + lock = configuredLock; + } else { + lock = await syncRules.lock(); + } + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + const newJob = new ReplicationJob({ + adapter: this.adapterFactory.create({ + syncRuleId: syncRules.id.toString() + }), + storage: storage, + lock: lock + }); + newJobs.set(syncRules.id, newJob); + newJob.start(); + } catch (e) { + // Could be a sync rules parse error, + // for example from stricter validation that was added. + // This will be retried every couple of seconds. + // When new (valid) sync rules are deployed and processed, this one be disabled. + logger.error(`Failed to start replication for ${this.adapterFactory.name} with new sync rules`, e); + } + } + } + + this.replicationJobs = newJobs; + + // Terminate any orphaned jobs that no longer have sync rules + for (let job of existingJobs.values()) { + // Old - stop and clean up + try { + await job.terminate(); + } catch (e) { + // This will be retried + logger.warn(`Failed to terminate old ${this.adapterFactory.name} replication job}`, e); + } + } + + // Sync rules stopped previously or by a different process. + const stopped = await this.storage.getStoppedSyncRules(); + for (let syncRules of stopped) { + try { + const lock = await syncRules.lock(); + try { + const tempAdapter = this.adapterFactory.create({ + syncRuleId: syncRules.id.toString() + }); + + await tempAdapter.cleanupReplication(); + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + await storage.terminate(); + } finally { + await lock.release(); + } + } catch (e) { + logger.warn(`Failed to terminate ${syncRules.id}`, e); + } + } + } +} diff --git a/packages/service-core/src/replication/core/ReplicationJob.ts b/packages/service-core/src/replication/core/ReplicationJob.ts index ea3a20a4e..fb4b1ff15 100644 --- a/packages/service-core/src/replication/core/ReplicationJob.ts +++ b/packages/service-core/src/replication/core/ReplicationJob.ts @@ -1,9 +1,17 @@ -import { ReplicationAdapter, ReplicationUpdate, UpdateType } from './ReplicationAdapter.js'; +import { + CrudOperation, + LSNUpdate, + ReplicationAdapter, + ReplicationMessage, + ReplicationMessageType, + SchemaUpdate, + TruncateRequest +} from './ReplicationAdapter.js'; import { container, logger } from '@powersync/lib-services-framework'; import { ErrorRateLimiter } from '../ErrorRateLimiter.js'; import { Metrics } from '../../metrics/Metrics.js'; import * as storage from '../../storage/storage-index.js'; -import { SourceEntityDescriptor } from '../../storage/SourceEntity.js'; +import * as util from './replication-utils.js'; export interface ReplicationJobOptions { adapter: ReplicationAdapter; @@ -41,11 +49,11 @@ export class ReplicationJob { // Fatal exception container.reporter.captureException(e, { metadata: { - replicator: this.adapter.name() + replicator: this.adapter.name // TODO We could allow extra metadata contributed from the adapter here } }); - logger.error(`Replication failed on ${this.adapter.name()}`, e); + logger.error(`Replication failed on ${this.adapter.name}`, e); } finally { this.abortController.abort(); } @@ -64,14 +72,17 @@ export class ReplicationJob { async replicate() { try { - await this.adapter.checkPrerequisites(); + await this.adapter.ensureConfiguration({ abortSignal: this.abortController.signal }); const status = await this.storage.getStatus(); const sourceEntities = await this.getSourceEntities(); - if (status.snapshot_done && status.checkpoint_lsn) { + if (!(status.snapshot_done && status.checkpoint_lsn)) { + logger.info(`${this.adapter.name}: Starting initial replication...`); await this.storage.clear(); await this.storage.startBatch({}, async (batch) => { await this.initializeData(sourceEntities, batch); }); + } else { + logger.info(`${this.adapter.name}: Initial replication already done.`); } await this.startReplication(sourceEntities); } catch (e) { @@ -85,7 +96,17 @@ export class ReplicationJob { for (const sourceEntity of this.storage.sync_rules.getSourceTables()) { const resolved = await this.adapter.resolveReplicationEntities(sourceEntity); - resolvedSourceEntities.push(...resolved); + for (const resolvedSourceEntity of resolved) { + const result = await this.storage.resolveTable({ + group_id: this.storage.group_id, + connection_id: this.connectionId, + connection_tag: sourceEntity.connectionTag, + entity_descriptor: resolvedSourceEntity, + sync_rules: this.storage.sync_rules + }); + + resolvedSourceEntities.push(result.table); + } } return resolvedSourceEntities; @@ -95,20 +116,22 @@ export class ReplicationJob { let at = 0; let replicatedCount = 0; let currentEntity: storage.SourceTable | null = null; - let estimatedCount = 0; + let estimatedCount = 0n; await this.adapter.initializeData({ entities: sourceEntities, abortSignal: this.abortController.signal, entryConsumer: async (batch) => { + await util.touch(); + if (batch.entity != currentEntity) { - logger.info(`${this.adapter.name()}: Replicating: ${batch.entity.table}`); + logger.info(`${this.adapter.name}: Replicating: ${batch.entity.table}`); currentEntity = batch.entity; estimatedCount = await this.adapter.count(currentEntity); } if (batch.entries.length > 0 && at - replicatedCount >= 5000) { - logger.info(`${this.adapter.name()}: Replicating: ${currentEntity.table} Progress: ${at}/${estimatedCount}`); + logger.info(`${this.adapter.name}: Replicating: ${currentEntity.table} Progress: ${at}/${estimatedCount}`); } for (const entry of batch.entries) { @@ -123,10 +146,8 @@ export class ReplicationJob { at = 0; replicatedCount = 0; currentEntity = null; - estimatedCount = 0; + estimatedCount = -1n; } - - await this.touch(); } }); } @@ -143,32 +164,33 @@ export class ReplicationJob { }); } - private async handeReplicationUpdate(update: ReplicationUpdate, batch: storage.BucketStorageBatch): Promise { - switch (update.type) { - case UpdateType.INSERT: - case UpdateType.UPDATE: - case UpdateType.DELETE: - await batch.save(update.entry!); + private async handeReplicationUpdate(message: ReplicationMessage, batch: storage.BucketStorageBatch): Promise { + switch (message.type) { + case ReplicationMessageType.CRUD: + const crudOperation = message.payload as CrudOperation; + await batch.save(crudOperation.entry); Metrics.getInstance().rows_replicated_total.add(1); return; - case UpdateType.TRUNCATE: - await batch.truncate(update.entities); + case ReplicationMessageType.TRUNCATE: + const truncateRequest = message.payload as TruncateRequest; + await batch.truncate(truncateRequest.entities); return; - case UpdateType.SCHEMA_CHANGE: - await this.handleSchemaChange(batch, update.entityDescriptor!, update.entities[0]); + case ReplicationMessageType.SCHEMA_CHANGE: + const schemaUpdate = message.payload as SchemaUpdate; + await this.handleSchemaUpdate(batch, schemaUpdate); break; - case UpdateType.COMMIT: - await batch.commit(update.lsn!); + case ReplicationMessageType.COMMIT: + await batch.commit((message.payload as LSNUpdate).lsn!); Metrics.getInstance().transactions_replicated_total.add(1); return; - case UpdateType.KEEP_ALIVE: - await batch.keepalive(update.lsn!); + case ReplicationMessageType.KEEP_ALIVE: + await batch.keepalive((message.payload as LSNUpdate).lsn!); return; } } async stop(): Promise { - logger.info(`Stopping ${this.adapter.name()} replication job: ${this.storage.group_id}`); + logger.info(`Stopping ${this.adapter.name} replication job: ${this.storage.group_id}`); // End gracefully this.abortController.abort(); await this.isReplicatingPromise; @@ -181,25 +203,21 @@ export class ReplicationJob { * TODO: Confirm if this is still needed at all. */ async terminate(): Promise { - logger.info(`Terminating ${this.adapter.name()} replication job: ${this.storage.group_id}`); + logger.info(`Terminating ${this.adapter.name} replication job: ${this.storage.group_id}`); await this.stop(); - await this.adapter.cleanupReplication(this.storage.group_id); + await this.adapter.cleanupReplication(); await this.options.storage.terminate(); } - async handleSchemaChange( - storageBatch: storage.BucketStorageBatch, - entityDescriptor: SourceEntityDescriptor, - entity: storage.SourceTable - ): Promise { - if (!entityDescriptor.objectId) { + async handleSchemaUpdate(storageBatch: storage.BucketStorageBatch, update: SchemaUpdate): Promise { + if (!update.entityDescriptor.objectId) { throw new Error('relationId expected'); } const result = await this.storage.resolveTable({ group_id: this.storage.group_id, connection_id: this.connectionId, - connection_tag: entity.connectionTag, - entity_descriptor: entityDescriptor, + connection_tag: update.connectionTag, + entity_descriptor: update.entityDescriptor, sync_rules: this.storage.sync_rules }); @@ -211,11 +229,4 @@ export class ReplicationJob { await this.initializeData([result.table], storageBatch); } - - async touch(): Promise { - // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. - // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, - // or reduce PING_INTERVAL here. - return container.probes.touch(); - } } diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 5ba212850..cc8b604e0 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -1,4 +1,3 @@ -import { logger, schema } from '@powersync/lib-services-framework'; import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSyncConfig.js'; import * as t from 'ts-codec'; @@ -7,6 +6,8 @@ import * as api from '../../api/api-index.js'; import * as modules from '../../modules/modules-index.js'; import * as system from '../../system/system-index.js'; import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { logger, schema } from '@powersync/lib-services-framework'; +import { Replicator } from './Replicator.js'; export interface ReplicationModuleOptions extends modules.AbstractModuleOptions { type: string; @@ -40,7 +41,7 @@ export abstract class ReplicationModule extends modules.AbstractModule { /** * Create the ReplicationAdapter to be used by PowerSync replicator. */ - protected abstract createReplicationAdapter(config: DataSourceConfig): ReplicationAdapter; + protected abstract createReplicator(): Replicator; /** * Return the TS codec schema describing the required configuration values for this module. @@ -69,10 +70,7 @@ export abstract class ReplicationModule extends modules.AbstractModule { // If validation fails, log the error and continue, no replication will happen for this data source this.validateConfig(baseMatchingConfig); const decodedConfig = this.configSchema().decode(baseMatchingConfig); - const replicationAdapter = this.createReplicationAdapter(decodedConfig); - this.replicationAdapters.add(replicationAdapter); - context.replicationEngine.register(replicationAdapter); - + context.replicationEngine.register(this.createReplicator()); const apiAdapter = this.createSyncAPIAdapter(decodedConfig); context.routerEngine.registerAPI(apiAdapter); } catch (e) { diff --git a/packages/service-core/src/replication/core/replication-utils.ts b/packages/service-core/src/replication/core/replication-utils.ts new file mode 100644 index 000000000..5e2b5c45e --- /dev/null +++ b/packages/service-core/src/replication/core/replication-utils.ts @@ -0,0 +1,8 @@ +import { container } from '@powersync/lib-services-framework'; + +export async function touch() { + // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. + // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, + // or reduce PING_INTERVAL here. + return container.probes.touch(); +} From e095f3ea4c460ad74815c9e8e702c94fa5a0a8cd Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 14 Aug 2024 14:12:40 +0200 Subject: [PATCH 100/298] added table debug info calls --- .../module-mysql/src/api/MySQLAPIAdapter.ts | 219 ++++++------------ .../src/utils/replication/schema.ts | 101 ++++++++ 2 files changed, 177 insertions(+), 143 deletions(-) create mode 100644 modules/module-mysql/src/utils/replication/schema.ts diff --git a/modules/module-mysql/src/api/MySQLAPIAdapter.ts b/modules/module-mysql/src/api/MySQLAPIAdapter.ts index 4cec0a143..7026702e6 100644 --- a/modules/module-mysql/src/api/MySQLAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLAPIAdapter.ts @@ -1,10 +1,11 @@ -import { api } from '@powersync/service-core'; +import { api, storage } from '@powersync/service-core'; import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; import * as types from '../types/types.js'; import { checkSourceConfiguration, readMasterComparableGtid, retriedQuery } from '../utils/mysql_utils.js'; +import { getReplicationIdentityColumns, ReplicationIdentityColumnsResult } from '../utils/replication/schema.js'; export class MySQLAPIAdapter implements api.RouteAPI { protected pool: mysql.Pool; @@ -122,7 +123,14 @@ export class MySQLAPIAdapter implements api.RouteAPI { ): Promise { let result: api.PatternResult[] = []; - for (let tablePattern of tablePatterns) { + /** + * This is a hack. The schema should always be the database name in MySQL. + * The default value of `public` is not valid. + * We might need to implement this better where the original table patterns are created. + */ + const mappedPatterns = tablePatterns.map((t) => new sync_rules.TablePattern(this.config.database, t.tablePattern)); + + for (let tablePattern of mappedPatterns) { const schema = tablePattern.schema; let patternResult: api.PatternResult = { schema: schema, @@ -150,8 +158,8 @@ export class MySQLAPIAdapter implements api.RouteAPI { continue; } - // const details = await getDebugTableInfo(tablePattern, name, sqlSyncRules); - // patternResult.tables.push(details); + const details = await this.getDebugTableInfo(tablePattern, name, sqlSyncRules); + patternResult.tables.push(details); } } else { const [results] = await this.pool.query( @@ -159,158 +167,83 @@ export class MySQLAPIAdapter implements api.RouteAPI { FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, - [schema, tablePattern.tablePattern] + [ + /** + * TODO:!!!! The Schema here is the default Postgres `public` + * which is not a thing in MySQL. The TABLE_SCHEMA in MySQL is the database name. + */ + // schema + this.config.database, + tablePattern.tablePattern + ] ); - // if (results.length == 0) { - // // Table not found - // const details = await getDebugTableInfo(tablePattern, tablePattern.name, sqlSyncRules); - // patternResult.table = details; - // } else { - // const row = results[0]; - // const name = row.table_name as string; - - // patternResult.table = await getDebugTableInfo(tablePattern, name, sqlSyncRules); - // } + if (results.length == 0) { + // Table not found + const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, sqlSyncRules); + patternResult.table = details; + } else { + const row = results[0]; + patternResult.table = await this.getDebugTableInfo(tablePattern, row.table_name, sqlSyncRules); + } } } return result; - - // let result: api.PatternResult[] = []; - // for (let tablePattern of tablePatterns) { - // const schema = tablePattern.schema; - // let patternResult: api.PatternResult = { - // schema: schema, - // pattern: tablePattern.tablePattern, - // wildcard: tablePattern.isWildcard - // }; - // result.push(patternResult); - // if (tablePattern.isWildcard) { - // patternResult.tables = []; - // const prefix = tablePattern.tablePrefix; - // const results = await pg_utils.retriedQuery(this.pool, { - // statement: `SELECT c.oid AS relid, c.relname AS table_name - // FROM pg_class c - // JOIN pg_namespace n ON n.oid = c.relnamespace - // WHERE n.nspname = $1 - // AND c.relkind = 'r' - // AND c.relname LIKE $2`, - // params: [ - // { type: 'varchar', value: schema }, - // { type: 'varchar', value: tablePattern.tablePattern } - // ] - // }); - // for (let row of pgwire.pgwireRows(results)) { - // const name = row.table_name as string; - // const relationId = row.relid as number; - // if (!name.startsWith(prefix)) { - // continue; - // } - // const details = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules); - // patternResult.tables.push(details); - // } - // } else { - // const results = await pg_utils.retriedQuery(this.pool, { - // statement: `SELECT c.oid AS relid, c.relname AS table_name - // FROM pg_class c - // JOIN pg_namespace n ON n.oid = c.relnamespace - // WHERE n.nspname = $1 - // AND c.relkind = 'r' - // AND c.relname = $2`, - // params: [ - // { type: 'varchar', value: schema }, - // { type: 'varchar', value: tablePattern.tablePattern } - // ] - // }); - // if (results.rows.length == 0) { - // // Table not found - // const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null, sqlSyncRules); - // patternResult.table = details; - // } else { - // const row = pgwire.pgwireRows(results)[0]; - // const name = row.table_name as string; - // const relationId = row.relid as number; - // patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId, sqlSyncRules); - // } - // } - // } - // return result; } protected async getDebugTableInfo( tablePattern: sync_rules.TablePattern, - name: string, - relationId: number | null, + tableName: string, syncRules: sync_rules.SqlSyncRules ): Promise { - throw new Error('not implemented'); + const { schema } = tablePattern; - // const schema = tablePattern.schema; - // let id_columns_result: replication_utils.ReplicaIdentityResult | undefined = undefined; - // let id_columns_error = null; - // if (relationId != null) { - // try { - // id_columns_result = await replication_utils.getReplicationIdentityColumns(this.pool, relationId); - // } catch (e) { - // id_columns_error = { level: 'fatal', message: e.message }; - // } - // } - // const id_columns = id_columns_result?.replicationColumns ?? []; - // const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); - // const syncData = syncRules.tableSyncsData(sourceTable); - // const syncParameters = syncRules.tableSyncsParameters(sourceTable); - // if (relationId == null) { - // return { - // schema: schema, - // name: name, - // pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - // replication_id: [], - // data_queries: syncData, - // parameter_queries: syncParameters, - // // Also - // errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] - // }; - // } - // if (id_columns.length == 0 && id_columns_error == null) { - // let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; - // if (id_columns_result?.replicationIdentity == 'default') { - // message += ' Configure a primary key on the table.'; - // } - // id_columns_error = { level: 'fatal', message }; - // } - // let selectError = null; - // try { - // await pg_utils.retriedQuery(this.pool, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); - // } catch (e) { - // selectError = { level: 'fatal', message: e.message }; - // } - // let replicateError = null; - // const publications = await pg_utils.retriedQuery(this.pool, { - // statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, - // params: [ - // { type: 'varchar', value: this.publication_name }, - // { type: 'varchar', value: tablePattern.schema }, - // { type: 'varchar', value: name } - // ] - // }); - // if (publications.rows.length == 0) { - // replicateError = { - // level: 'fatal', - // message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` - // }; - // } - // return { - // schema: schema, - // name: name, - // pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - // replication_id: id_columns.map((c) => c.name), - // data_queries: syncData, - // parameter_queries: syncParameters, - // errors: [id_columns_error, selectError, replicateError].filter( - // (error) => error != null - // ) as service_types.ReplicationError[] - // }; + let idColumnsResult: ReplicationIdentityColumnsResult | null = null; + let idColumnsError: service_types.ReplicationError | null = null; + try { + idColumnsResult = await getReplicationIdentityColumns({ + db: this.pool, + schema, + table_name: tableName + }); + } catch (ex) { + idColumnsError = { level: 'fatal', message: ex.message }; + } + + const idColumns = idColumnsResult?.columns ?? []; + const sourceTable = new storage.SourceTable(0, this.config.tag, tableName, schema, tableName, idColumns, true); + const syncData = syncRules.tableSyncsData(sourceTable); + const syncParameters = syncRules.tableSyncsParameters(sourceTable); + + if (idColumns.length == 0 && idColumnsError == null) { + let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${idColumnsResult?.identity}.`; + if (idColumnsResult?.identity == 'default') { + message += ' Configure a primary key on the table.'; + } + idColumnsError = { level: 'fatal', message }; + } + + let selectError: service_types.ReplicationError | null = null; + try { + await retriedQuery({ + db: this.pool, + query: `SELECT * FROM ${sourceTable.table} LIMIT 1` + }); + } catch (e) { + selectError = { level: 'fatal', message: e.message }; + } + + // Not sure if table level checks are possible yet + return { + schema: schema, + name: tableName, + pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + replication_id: idColumns.map((c) => c.name), + data_queries: syncData, + parameter_queries: syncParameters, + errors: [idColumnsError, selectError].filter((error) => error != null) as service_types.ReplicationError[] + }; } async getReplicationLag(syncRulesId: string): Promise { diff --git a/modules/module-mysql/src/utils/replication/schema.ts b/modules/module-mysql/src/utils/replication/schema.ts new file mode 100644 index 000000000..40185f537 --- /dev/null +++ b/modules/module-mysql/src/utils/replication/schema.ts @@ -0,0 +1,101 @@ +import { storage } from '@powersync/service-core'; +import mysql from 'mysql2/promise'; +import { retriedQuery } from '../mysql_utils.js'; + +export type GetReplicationColumnsOptions = { + db: mysql.Pool; + schema: string; + table_name: string; +}; + +export type ReplicationIdentityColumnsResult = { + columns: storage.ColumnDescriptor[]; + // TODO maybe export an enum from the core package + identity: string; +}; + +export async function getReplicationIdentityColumns( + options: GetReplicationColumnsOptions +): Promise { + const { db, schema, table_name } = options; + const [primaryKeyColumns] = await retriedQuery({ + db, + query: ` + SELECT s.COLUMN_NAME AS name, c.DATA_TYPE AS type + FROM INFORMATION_SCHEMA.STATISTICS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + AND s.INDEX_NAME = 'PRIMARY' + ORDER BY s.SEQ_IN_INDEX; + `, + params: [schema, table_name] + }); + + if (primaryKeyColumns.length) { + return { + columns: primaryKeyColumns.map((row) => ({ + name: row.name, + type: row.type + })), + identity: 'default' + }; + } + + // TODO: test code with tables with unique keys, compound key etc. + // No primary key, find the first valid unique key + const [uniqueKeyColumns] = await retriedQuery({ + db, + query: ` + SELECT s.INDEX_NAME, s.COLUMN_NAME, c.DATA_TYPE, s.NON_UNIQUE, s.NULLABLE + FROM INFORMATION_SCHEMA.STATISTICS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + AND s.INDEX_NAME != 'PRIMARY' + AND s.NON_UNIQUE = 0 + ORDER BY s.SEQ_IN_INDEX; + `, + params: [schema, table_name] + }); + + if (uniqueKeyColumns.length > 0) { + return { + columns: uniqueKeyColumns.map((col) => ({ + name: col.COLUMN_NAME, + type: col.DATA_TYPE + })), + identity: 'index' + }; + } + + const [allColumns] = await retriedQuery({ + db, + query: ` + SELECT s.COLUMN_NAME AS name, c.DATA_TYPE as type + FROM INFORMATION_SCHEMA.COLUMNS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + ORDER BY s.ORDINAL_POSITION; + `, + params: [schema, table_name] + }); + + return { + columns: allColumns.map((row) => ({ + name: row.name, + type: row.type + })), + identity: 'full' + }; +} From 5700fe8314847e1f56dd8b9568c955a10e404566 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 14 Aug 2024 16:00:26 +0200 Subject: [PATCH 101/298] Update Walstream classes to use the factories from the service context Simplified configuration of Replication module --- .../src/module/PostgresModule.ts | 81 +++++++------------ .../replication/ConnectionManagerFactory.ts | 28 +++++++ .../src/replication/PgManager.ts | 2 +- .../src/replication/WalStream.ts | 3 +- .../src/replication/WalStreamManager.ts | 39 +++++---- .../src/replication/WalStreamRunner.ts | 33 ++++---- .../src/replication/core/ReplicationModule.ts | 35 ++++---- 7 files changed, 120 insertions(+), 101 deletions(-) create mode 100644 modules/module-postgres/src/replication/ConnectionManagerFactory.ts diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index cce6d0401..b48c2e3ab 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,27 +1,28 @@ -import { api, auth, replication, system } from '@powersync/service-core'; +import { + api, + auth, + ConfigurationFileSyncRulesProvider, + replication, + Replicator, + system +} from '@powersync/service-core'; import * as jpgwire from '@powersync/service-jpgwire'; -import * as t from 'ts-codec'; - import { logger } from '@powersync/lib-services-framework'; import * as types from '../types/types.js'; - import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; -import { PostgresReplicationAdapter } from '../replication/PostgresReplicationAdapter.js'; +import { WalStreamManager } from '../replication/WalStreamManager.js'; +import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js'; -export class PostgresModule extends replication.ReplicationModule { +export class PostgresModule extends replication.ReplicationModule { constructor() { super({ name: 'Postgres', - type: types.POSTGRES_CONNECTION_TYPE + type: types.POSTGRES_CONNECTION_TYPE, + configSchema: types.PostgresConnectionConfig }); } - protected configSchema(): t.AnyCodec { - // Intersection types have some limitations in codec typing - return types.PostgresConnectionConfig; - } - async initialize(context: system.ServiceContextContainer): Promise { await super.initialize(context); @@ -37,12 +38,24 @@ export class PostgresModule extends replication.ReplicationModule { }); } - protected createSyncAPIAdapter(config: types.PostgresConnectionConfig): api.RouteAPI { - return new PostgresRouteAPIAdapter(this.resolveConfig(config)); + protected createRouteAPIAdapter(decodedConfig: types.PostgresConnectionConfig): api.RouteAPI { + return new PostgresRouteAPIAdapter(this.resolveConfig(decodedConfig)); } - protected createReplicationAdapter(config: types.PostgresConnectionConfig): PostgresReplicationAdapter { - return new PostgresReplicationAdapter(this.resolveConfig(config)); + protected createReplicator( + decodedConfig: types.PostgresConnectionConfig, + context: system.ServiceContext + ): Replicator { + const normalisedConfig = this.resolveConfig(decodedConfig); + const connectionFactory = new ConnectionManagerFactory(normalisedConfig); + const syncRuleProvider = new ConfigurationFileSyncRulesProvider(context.configuration.sync_rules); + + return new WalStreamManager({ + id: this.getDefaultId(normalisedConfig.database), + syncRuleProvider: syncRuleProvider, + storageFactory: context.storage, + connectionFactory: connectionFactory + }); } /** @@ -55,40 +68,8 @@ export class PostgresModule extends replication.ReplicationModule { }; } - async teardown(): Promise { - // TODO this needs the service context to operate. - // Should this keep a refference? - // const mongoDB = storage.createPowerSyncMongo(context.configuration.storage); - // try { - // // TODO this should not be necessary since the service context - // // has already been initialized. - // // However we need a direct mongo connection for this. - // // Maybe we can add termination methods to the storage. - // // TODO improve this when other storage methods or connections are implemented - // logger.info(`Waiting for auth`); - // await db.mongo.waitForAuth(mongoDB.db); - // logger.info(`Terminating replication slots`); - // const connections = (context.configuration.connections ?? []) - // .filter((c) => c.type == 'postgresql') - // .map((c) => types.PostgresConnectionConfig.decode(c as any)); - // for (const connection of connections) { - // await terminateReplicators(context.storage, this.resolveConfig(connection)); - // } - // const database = mongoDB.db; - // logger.info(`Dropping database ${database.namespace}`); - // await database.dropDatabase(); - // logger.info(`Done`); - // await mongoDB.client.close(); - // // If there was an error connecting to postgress, the process may stay open indefinitely. - // // This forces an exit. - // // We do not consider those errors a teardown failure. - // process.exit(0); - // } catch (e) { - // logger.error(`Teardown failure`, e); - // await mongoDB.client.close(); - // process.exit(1); - // } - } + // TODO: Confirm if there are any resources that would not already be closed by the Replication and Router engines + async teardown(): Promise {} protected registerSupabaseAuth(context: system.ServiceContextContainer) { const { configuration } = context; diff --git a/modules/module-postgres/src/replication/ConnectionManagerFactory.ts b/modules/module-postgres/src/replication/ConnectionManagerFactory.ts new file mode 100644 index 000000000..0c46b9f24 --- /dev/null +++ b/modules/module-postgres/src/replication/ConnectionManagerFactory.ts @@ -0,0 +1,28 @@ +import { PgManager } from './PgManager.js'; +import { NormalizedPostgresConnectionConfig } from '../types/types.js'; +import { PgPoolOptions } from '@powersync/service-jpgwire'; +import { logger } from '@powersync/lib-services-framework'; + +export class ConnectionManagerFactory { + private readonly connectionManagers: PgManager[]; + private readonly dbConnectionConfig: NormalizedPostgresConnectionConfig; + + constructor(dbConnectionConfig: NormalizedPostgresConnectionConfig) { + this.dbConnectionConfig = dbConnectionConfig; + this.connectionManagers = []; + } + + create(poolOptions: PgPoolOptions) { + const manager = new PgManager(this.dbConnectionConfig, poolOptions); + this.connectionManagers.push(manager); + return manager; + } + + async shutdown() { + logger.info('Shutting down Postgres connection Managers...'); + for (const manager of this.connectionManagers) { + await manager.end(); + } + logger.info('Postgres connection Managers shutdown completed.'); + } +} diff --git a/modules/module-postgres/src/replication/PgManager.ts b/modules/module-postgres/src/replication/PgManager.ts index 8633cc125..d611fe851 100644 --- a/modules/module-postgres/src/replication/PgManager.ts +++ b/modules/module-postgres/src/replication/PgManager.ts @@ -34,7 +34,7 @@ export class PgManager { return await p; } - async end() { + async end(): Promise { for (let result of await Promise.allSettled([ this.pool.end(), ...this.connectionPromises.map(async (promise) => { diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 585fa3408..7eed4257a 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -1,11 +1,10 @@ import * as pgwire from '@powersync/service-jpgwire'; -import * as storage from '@powersync/service-core/dist/storage/storage-index.js'; import * as util from '../utils/pgwire_utils.js'; import { container, errors, logger } from '@powersync/lib-services-framework'; import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; import { getPgOutputRelation, getRelId } from './PgRelation.js'; import { WalConnection } from './WalConnection.js'; -import { Metrics, SourceEntityDescriptor } from '@powersync/service-core'; +import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; import { checkSourceConfiguration, getReplicationIdentityColumns } from './replication-utils.js'; import { PgManager } from './PgManager.js'; diff --git a/modules/module-postgres/src/replication/WalStreamManager.ts b/modules/module-postgres/src/replication/WalStreamManager.ts index 53fd1ee8f..d891dc028 100644 --- a/modules/module-postgres/src/replication/WalStreamManager.ts +++ b/modules/module-postgres/src/replication/WalStreamManager.ts @@ -1,26 +1,25 @@ import { hrtime } from 'node:process'; -import * as storage from '../../../../packages/service-core/src/storage/storage-index.js'; -import * as util from '../../../../packages/service-core/src/util/util-index.js'; - -import { DefaultErrorRateLimiter } from '@powersync/service-core'; - -import { Replicator } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; +import { Replicator, SyncRulesProvider, DefaultErrorRateLimiter, storage } from '@powersync/service-core'; import { WalStreamRunner } from './WalStreamRunner.js'; -import { ServiceContext } from '@powersync/service-core'; +import { PgManager } from './PgManager.js'; +import { container, logger } from '@powersync/lib-services-framework'; +import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; // 5 minutes const PING_INTERVAL = 1_000_000_000n * 300n; export interface WalStreamManagerOptions { id: string; - serviceContext: ServiceContext; - storage: storage.BucketStorageFactory; - connectionPool: util.PgManager; + storageFactory: storage.StorageFactoryProvider; + syncRuleProvider: SyncRulesProvider; + connectionFactory: ConnectionManagerFactory; } export class WalStreamManager implements Replicator { private streams = new Map(); + private connectionManager: PgManager | null = null; private stopped = false; @@ -39,14 +38,19 @@ export class WalStreamManager implements Replicator { } private get storage() { - return this.options.storage; + return this.options.storageFactory.bucketStorage; + } + + private get syncRuleProvider() { + return this.options.syncRuleProvider; } - private get serviceContext() { - return this.options.serviceContext; + private get connectionFactory() { + return this.options.connectionFactory; } start() { + this.connectionManager = this.connectionFactory.create({ maxSize: 1, idleTimeout: 30000 }); this.runLoop().catch((e) => { logger.error(`Fatal WalStream error`, e); container.reporter.captureException(e); @@ -62,11 +66,12 @@ export class WalStreamManager implements Replicator { for (let stream of this.streams.values()) { promises.push(stream.stop()); } + promises.push(this.connectionManager!.end()); await Promise.all(promises); } private async runLoop() { - const configured_sync_rules = await util.loadSyncRules(this.serviceContext.config); + const configured_sync_rules = await this.syncRuleProvider.get(); let configured_lock: storage.ReplicationLock | undefined = undefined; if (configured_sync_rules != null) { logger.info('Loading sync rules from configuration'); @@ -89,7 +94,7 @@ export class WalStreamManager implements Replicator { while (!this.stopped) { await container.probes.touch(); try { - const pool = this.serviceContext.pgwire_pool; + const pool = this.connectionManager?.pool; if (pool) { await this.refresh({ configured_lock }); // The lock is only valid on the first refresh. @@ -168,7 +173,7 @@ export class WalStreamManager implements Replicator { const stream = new WalStreamRunner({ factory: this.storage, storage: storage, - source_db: this.serviceContext.config.connection!, + connectionFactory: this.connectionFactory, lock, rateLimiter: this.rateLimiter }); @@ -210,7 +215,7 @@ export class WalStreamManager implements Replicator { const stream = new WalStreamRunner({ factory: this.storage, storage: storage, - source_db: this.serviceContext.config.connection!, + connectionFactory: this.connectionFactory, lock }); await stream.terminate(); diff --git a/modules/module-postgres/src/replication/WalStreamRunner.ts b/modules/module-postgres/src/replication/WalStreamRunner.ts index ee6fbbe8f..f462567c8 100644 --- a/modules/module-postgres/src/replication/WalStreamRunner.ts +++ b/modules/module-postgres/src/replication/WalStreamRunner.ts @@ -1,15 +1,13 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import * as storage from '@powersync/service-core/dist/storage/storage-index.js'; import { MissingReplicationSlotError, WalStream } from './WalStream.js'; import { container, logger } from '@powersync/lib-services-framework'; import { PgManager } from './PgManager.js'; -import { ErrorRateLimiter } from '@powersync/service-core'; -import { NormalizedPostgresConnectionConfig } from '../types/types.js'; +import { ErrorRateLimiter, storage } from '@powersync/service-core'; +import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; export interface WalStreamRunnerOptions { factory: storage.BucketStorageFactory; storage: storage.SyncRulesBucketStorage; - sourceDbConfig: NormalizedPostgresConnectionConfig; + connectionFactory: ConnectionManagerFactory; lock: storage.ReplicationLock; rateLimiter?: ErrorRateLimiter; } @@ -19,7 +17,7 @@ export class WalStreamRunner { private runPromise?: Promise; - private connections: PgManager | null = null; + private connectionManager: PgManager | null = null; private rateLimiter?: ErrorRateLimiter; @@ -39,6 +37,10 @@ export class WalStreamRunner { return this.abortController.signal.aborted; } + private get connectionFactory() { + return this.options.connectionFactory; + } + async run() { try { await this.replicateLoop(); @@ -75,12 +77,11 @@ export class WalStreamRunner { // New connections on every iteration (every error with retry), // otherwise we risk repeating errors related to the connection, // such as caused by cached PG schemas. - let connections = new PgManager(this.options.sourceDbConfig, { + this.connectionManager = this.connectionFactory.create({ // Pool connections are only used intermittently. idleTimeout: 30_000, maxSize: 2 }); - this.connections = connections; try { await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); if (this.stopped) { @@ -90,7 +91,7 @@ export class WalStreamRunner { abort_signal: this.abortController.signal, factory: this.options.factory, storage: this.options.storage, - connections + connections: this.connectionManager }); await stream.replicate(); } catch (e) { @@ -131,10 +132,10 @@ export class WalStreamRunner { this.rateLimiter?.reportError(e); } } finally { - this.connections = null; - if (connections != null) { - await connections.end(); + if (this.connectionManager) { + await this.connectionManager.end(); } + this.connectionManager = null; } } @@ -148,7 +149,7 @@ export class WalStreamRunner { if (options?.force) { // destroy() is more forceful. - await this.connections?.destroy(); + await this.connectionManager?.destroy(); } await this.runPromise; } @@ -163,14 +164,14 @@ export class WalStreamRunner { await this.stop(options); const slotName = this.slot_name; - const db = await pgwire.connectPgWire(this.options.sourceDbConfig, { type: 'standard' }); + const connectionManager = this.connectionFactory.create({ maxSize: 1, idleTimeout: 30_000 }); try { - await db.query({ + await connectionManager.pool.query({ statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', params: [{ type: 'varchar', value: slotName }] }); } finally { - await db.end(); + await connectionManager.end(); } await this.options.storage.terminate(); diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/replication/core/ReplicationModule.ts index 111e20e99..47d7d095b 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/replication/core/ReplicationModule.ts @@ -11,14 +11,16 @@ import { Replicator } from './Replicator.js'; export interface ReplicationModuleOptions extends modules.AbstractModuleOptions { type: string; + configSchema: t.AnyCodec; } /** * A replication module describes all the functionality that PowerSync requires to * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. */ -export abstract class ReplicationModule extends modules.AbstractModule { +export abstract class ReplicationModule extends modules.AbstractModule { protected type: string; + protected configSchema: t.AnyCodec; protected replicationAdapters: Set; @@ -26,9 +28,10 @@ export abstract class ReplicationModule extends modules.AbstractModule { * @protected * @param options */ - protected constructor(options: ReplicationModuleOptions) { + protected constructor(protected options: ReplicationModuleOptions) { super(options); this.type = options.type; + this.configSchema = options.configSchema; this.replicationAdapters = new Set(); } @@ -36,17 +39,12 @@ export abstract class ReplicationModule extends modules.AbstractModule { * Create the API adapter for the DataSource required by the sync API * endpoints. */ - protected abstract createSyncAPIAdapter(config: DataSourceConfig): api.RouteAPI; + protected abstract createRouteAPIAdapter(decodedConfig: TConfig): api.RouteAPI; /** * Create the ReplicationAdapter to be used by PowerSync replicator. */ - protected abstract createReplicator(): Replicator; - - /** - * Return the TS codec schema describing the required configuration values for this module. - */ - protected abstract configSchema(): t.AnyCodec; + protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): Replicator; /** * Register this module's replication adapters and sync API providers if the required configuration is present. @@ -70,23 +68,26 @@ export abstract class ReplicationModule extends modules.AbstractModule { } try { - const baseMatchingConfig = matchingConfig[0]; + const baseMatchingConfig = matchingConfig[0] as TConfig; // If validation fails, log the error and continue, no replication will happen for this data source this.validateConfig(baseMatchingConfig); - const decodedConfig = this.configSchema().decode(baseMatchingConfig); - context.replicationEngine.register(this.createReplicator()); - const apiAdapter = this.createSyncAPIAdapter(decodedConfig); + const decodedConfig = this.configSchema.decode(baseMatchingConfig); + context.replicationEngine.register(this.createReplicator(decodedConfig, context)); + const apiAdapter = this.createRouteAPIAdapter(decodedConfig); context.routerEngine.registerAPI(apiAdapter); } catch (e) { logger.error(e); } } - private validateConfig(config: DataSourceConfig): void { + private validateConfig(config: TConfig): void { const validator = schema .parseJSONSchema( // This generates a schema for the encoded form of the codec - t.generateJSONSchema(this.configSchema(), { allowAdditional: true, parsers: [types.configFile.portParser] }) + t.generateJSONSchema(this.configSchema, { + allowAdditional: true, + parsers: [types.configFile.portParser] + }) ) .validator(); @@ -96,4 +97,8 @@ export abstract class ReplicationModule extends modules.AbstractModule { throw new Error(`Failed to validate Module ${this.name} configuration: ${valid.errors.join(', ')}`); } } + + protected getDefaultId(dataSourceName: string): string { + return `${this.type}-${dataSourceName}`; + } } From 3188c45ffdda1a9c796f69a88aa013197e85b5e9 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 09:42:35 +0200 Subject: [PATCH 102/298] added connection schema api method and initial workings for replication lag --- .../module-mysql/src/api/MySQLAPIAdapter.ts | 211 +++++++++--------- 1 file changed, 110 insertions(+), 101 deletions(-) diff --git a/modules/module-mysql/src/api/MySQLAPIAdapter.ts b/modules/module-mysql/src/api/MySQLAPIAdapter.ts index 7026702e6..0407d64c8 100644 --- a/modules/module-mysql/src/api/MySQLAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLAPIAdapter.ts @@ -7,6 +7,12 @@ import * as types from '../types/types.js'; import { checkSourceConfiguration, readMasterComparableGtid, retriedQuery } from '../utils/mysql_utils.js'; import { getReplicationIdentityColumns, ReplicationIdentityColumnsResult } from '../utils/replication/schema.js'; +type SchemaResult = { + schemaname: string; + tablename: string; + columns: Array<{ data_type: string; column_name: string }>; +}; + export class MySQLAPIAdapter implements api.RouteAPI { protected pool: mysql.Pool; @@ -167,15 +173,7 @@ export class MySQLAPIAdapter implements api.RouteAPI { FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?`, - [ - /** - * TODO:!!!! The Schema here is the default Postgres `public` - * which is not a thing in MySQL. The TABLE_SCHEMA in MySQL is the database name. - */ - // schema - this.config.database, - tablePattern.tablePattern - ] + [tablePattern.schema, tablePattern.tablePattern] ); if (results.length == 0) { @@ -246,32 +244,51 @@ export class MySQLAPIAdapter implements api.RouteAPI { }; } - async getReplicationLag(syncRulesId: string): Promise { - const [binLogFiles] = await retriedQuery({ + async getReplicationLag(binLogFilename: string): Promise { + // Need to get the GTID for the last replicated point. + // Need more params for this. + const head = ''; + + const [[gtidEvent]] = await retriedQuery({ db: this.pool, - query: `SHOW MASTER STATUS` + query: ` + SHOW + BINLOG EVENTS IN ? + WHERE + Event_type = 'Previous_gtids' + AND Info = ? + LIMIT 1 + `, + params: [binLogFilename, head] }); - /** - * The format will be something like: - * Array<{ - * File: 'binlog.000002', - * Position: 197, - * Binlog_Do_DB: '', - * Binlog_Ignore_DB: '', - * Executed_Gtid_Set: 'a7b95c22-5987-11ef-ac5e-0242ac190003:1-12' - * }> - * The file should be the syncRulesId. The Position should be the current head in bytes. - */ - - const matchingFile = binLogFiles.find((f) => f.File == syncRulesId); - if (!matchingFile) { - throw new Error(`Could not determine replication lag for file ${syncRulesId}: File not found.`); + if (!gtidEvent) { + throw new Error(`Could not find binlog event for GTID`); } - // TODO, it seems that the lag needs to be tracked manually + // This is the BinLog file position at the last replicated GTID. + // The position is the file offset in bytes. + const headBinlogPosition = gtidEvent.Pos; + + // Get the position of the latest event in the BinLog file + const [[binLogEntry]] = await retriedQuery({ + db: this.pool, + query: ` + SHOW + master status + WHERE + File = ? + AND Info = ? + LIMIT 1 + `, + params: [binLogFilename] + }); + + if (!binLogEntry) { + throw new Error(`Could not find master status for binlog file`); + } - throw new Error(`Could not determine replication lag for slot ${syncRulesId}`); + return binLogEntry.Position - headBinlogPosition; } async getReplicationHead(): Promise { @@ -279,77 +296,69 @@ export class MySQLAPIAdapter implements api.RouteAPI { } async getConnectionSchema(): Promise { - throw new Error('not implemented'); - - // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts - // const results = await pg_utils.retriedQuery( - // this.pool, - // `SELECT - // tbl.schemaname, - // tbl.tablename, - // tbl.quoted_name, - // json_agg(a ORDER BY attnum) as columns - // FROM - // ( - // SELECT - // n.nspname as schemaname, - // c.relname as tablename, - // (quote_ident(n.nspname) || '.' || quote_ident(c.relname)) as quoted_name - // FROM - // pg_catalog.pg_class c - // JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace - // WHERE - // c.relkind = 'r' - // AND n.nspname not in ('information_schema', 'pg_catalog', 'pg_toast') - // AND n.nspname not like 'pg_temp_%' - // AND n.nspname not like 'pg_toast_temp_%' - // AND c.relnatts > 0 - // AND has_schema_privilege(n.oid, 'USAGE') = true - // AND has_table_privilege(quote_ident(n.nspname) || '.' || quote_ident(c.relname), 'SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER') = true - // ) as tbl - // LEFT JOIN ( - // SELECT - // attrelid, - // attname, - // format_type(atttypid, atttypmod) as data_type, - // (SELECT typname FROM pg_catalog.pg_type WHERE oid = atttypid) as pg_type, - // attnum, - // attisdropped - // FROM - // pg_attribute - // ) as a ON ( - // a.attrelid = tbl.quoted_name::regclass - // AND a.attnum > 0 - // AND NOT a.attisdropped - // AND has_column_privilege(tbl.quoted_name, a.attname, 'SELECT, INSERT, UPDATE, REFERENCES') - // ) - // GROUP BY schemaname, tablename, quoted_name` - // ); - // const rows = pgwire.pgwireRows(results); - // let schemas: Record = {}; - // for (let row of rows) { - // const schema = (schemas[row.schemaname] ??= { - // name: row.schemaname, - // tables: [] - // }); - // const table = { - // name: row.tablename, - // columns: [] as any[] - // }; - // schema.tables.push(table); - // const columnInfo = JSON.parse(row.columns); - // for (let column of columnInfo) { - // let pg_type = column.pg_type as string; - // if (pg_type.startsWith('_')) { - // pg_type = `${pg_type.substring(1)}[]`; - // } - // table.columns.push({ - // name: column.attname, - // type: column.data_type, - // pg_type: pg_type - // }); - // } - // } - // return Object.values(schemas); + const [results] = await retriedQuery({ + db: this.pool, + query: ` + SELECT + tbl.schemaname, + tbl.tablename, + tbl.quoted_name, + JSON_ARRAYAGG(JSON_OBJECT('column_name', a.column_name, 'data_type', a.data_type)) AS columns +FROM + ( + SELECT + TABLE_SCHEMA AS schemaname, + TABLE_NAME AS tablename, + CONCAT('\`', TABLE_SCHEMA, '\`.\`', TABLE_NAME, '\`') AS quoted_name + FROM + INFORMATION_SCHEMA.TABLES + WHERE + TABLE_TYPE = 'BASE TABLE' + AND TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') + ) AS tbl +LEFT JOIN + ( + SELECT + TABLE_SCHEMA AS schemaname, + TABLE_NAME AS tablename, + COLUMN_NAME AS column_name, + COLUMN_TYPE AS data_type + FROM + INFORMATION_SCHEMA.COLUMNS + ) AS a + ON tbl.schemaname = a.schemaname + AND tbl.tablename = a.tablename +GROUP BY + tbl.schemaname, tbl.tablename, tbl.quoted_name; + ` + }); + + /** + * Reduces the SQL results into a Record of {@link DatabaseSchema} + * then returns the values as an array. + */ + + return Object.values( + (results as SchemaResult[]).reduce((hash: Record, result) => { + const schema = + hash[result.schemaname] || + (hash[result.schemaname] = { + name: result.schemaname, + tables: [] + }); + + schema.tables.push({ + name: result.tablename, + columns: result.columns.map((column) => ({ + name: column.column_name, + type: column.data_type, + // FIXME remove this + pg_type: column.data_type + })) + }); + + return hash; + }, {}) + ); } } From 338d490bb7a68a219abcc41102550e51eda30870 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 10:36:32 +0200 Subject: [PATCH 103/298] update replication lag API. Cleanup queries --- modules/module-mysql/README.md | 4 +- .../src/api/MySQLRouteAPIAdapter.ts | 91 ++++++++++--------- modules/module-mysql/src/utils/mysql_utils.ts | 9 ++ .../src/utils/replication/schema.ts | 60 ++++++------ .../src/api/PostgresRouteAPIAdapter.ts | 7 +- packages/service-core/src/api/RouteAPI.ts | 8 +- packages/service-core/src/api/diagnostics.ts | 6 +- 7 files changed, 104 insertions(+), 81 deletions(-) diff --git a/modules/module-mysql/README.md b/modules/module-mysql/README.md index f17e382c9..93b33d14c 100644 --- a/modules/module-mysql/README.md +++ b/modules/module-mysql/README.md @@ -1,3 +1,3 @@ -# PowerSync MSSQL Module +# PowerSync MySQL Module -This is a module which provides MSSQL replication to PowerSync +This is a module which provides MySQL replication to PowerSync. diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 20aadd61f..25688529b 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -150,10 +150,13 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { const prefix = tablePattern.tablePrefix; const [results] = await this.pool.query( - `SELECT TABLE_NAME AS table_name - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = ? - AND TABLE_NAME LIKE ?`, + `SELECT + TABLE_NAME AS table_name + FROM + INFORMATION_SCHEMA.TABLES + WHERE + TABLE_SCHEMA = ? + AND TABLE_NAME LIKE ?`, [schema, tablePattern.tablePattern] ); @@ -169,10 +172,13 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } } else { const [results] = await this.pool.query( - `SELECT TABLE_NAME AS table_name - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = ? - AND TABLE_NAME = ?`, + `SELECT + TABLE_NAME AS table_name + FROM + INFORMATION_SCHEMA.TABLES + WHERE + TABLE_SCHEMA = ? + AND TABLE_NAME = ?`, [tablePattern.schema, tablePattern.tablePattern] ); @@ -244,10 +250,8 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { }; } - async getReplicationLag(binLogFilename: string): Promise { - // Need to get the GTID for the last replicated point. - // Need more params for this. - const head = ''; + async getReplicationLag(options: api.ReplicationLagOptions): Promise { + const { replication_identifier: binLogFilename, last_checkpoint_identifier } = options; const [[gtidEvent]] = await retriedQuery({ db: this.pool, @@ -259,7 +263,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { AND Info = ? LIMIT 1 `, - params: [binLogFilename, head] + params: [binLogFilename, last_checkpoint_identifier] }); if (!gtidEvent) { @@ -299,37 +303,38 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { const [results] = await retriedQuery({ db: this.pool, query: ` - SELECT - tbl.schemaname, - tbl.tablename, - tbl.quoted_name, - JSON_ARRAYAGG(JSON_OBJECT('column_name', a.column_name, 'data_type', a.data_type)) AS columns -FROM - ( - SELECT - TABLE_SCHEMA AS schemaname, - TABLE_NAME AS tablename, - CONCAT('\`', TABLE_SCHEMA, '\`.\`', TABLE_NAME, '\`') AS quoted_name - FROM - INFORMATION_SCHEMA.TABLES - WHERE - TABLE_TYPE = 'BASE TABLE' - AND TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') - ) AS tbl -LEFT JOIN - ( SELECT - TABLE_SCHEMA AS schemaname, - TABLE_NAME AS tablename, - COLUMN_NAME AS column_name, - COLUMN_TYPE AS data_type - FROM - INFORMATION_SCHEMA.COLUMNS - ) AS a - ON tbl.schemaname = a.schemaname - AND tbl.tablename = a.tablename -GROUP BY - tbl.schemaname, tbl.tablename, tbl.quoted_name; + tbl.schemaname, + tbl.tablename, + tbl.quoted_name, + JSON_ARRAYAGG(JSON_OBJECT('column_name', a.column_name, 'data_type', a.data_type)) AS columns + FROM + ( + SELECT + TABLE_SCHEMA AS schemaname, + TABLE_NAME AS tablename, + CONCAT('\`', TABLE_SCHEMA, '\`.\`', TABLE_NAME, '\`') AS quoted_name + FROM + INFORMATION_SCHEMA.TABLES + WHERE + TABLE_TYPE = 'BASE TABLE' + AND TABLE_SCHEMA NOT IN ('information_schema', 'mysql', 'performance_schema', 'sys') + ) AS tbl + LEFT JOIN + ( + SELECT + TABLE_SCHEMA AS schemaname, + TABLE_NAME AS tablename, + COLUMN_NAME AS column_name, + COLUMN_TYPE AS data_type + FROM + INFORMATION_SCHEMA.COLUMNS + ) AS a + ON + tbl.schemaname = a.schemaname + AND tbl.tablename = a.tablename + GROUP BY + tbl.schemaname, tbl.tablename, tbl.quoted_name; ` }); diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index b14f58426..202059fd9 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -62,6 +62,15 @@ export async function checkSourceConfiguration(db: mysql.Pool) { errors.push('Binary log index file is not set. Please check your settings.'); } + const [[binLogFormatResult]] = await retriedQuery({ + db, + query: `SHOW VARIABLES LIKE 'binlog_format';` + }); + + if (binLogFormatResult.Value !== 'ROW') { + errors.push('Binary log format must be set to "ROW". Please correct your configuration'); + } + return errors; } diff --git a/modules/module-mysql/src/utils/replication/schema.ts b/modules/module-mysql/src/utils/replication/schema.ts index 40185f537..fb3ac6fec 100644 --- a/modules/module-mysql/src/utils/replication/schema.ts +++ b/modules/module-mysql/src/utils/replication/schema.ts @@ -21,16 +21,16 @@ export async function getReplicationIdentityColumns( const [primaryKeyColumns] = await retriedQuery({ db, query: ` - SELECT s.COLUMN_NAME AS name, c.DATA_TYPE AS type - FROM INFORMATION_SCHEMA.STATISTICS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? - AND s.TABLE_NAME = ? - AND s.INDEX_NAME = 'PRIMARY' - ORDER BY s.SEQ_IN_INDEX; + SELECT s.COLUMN_NAME AS name, c.DATA_TYPE AS type + FROM INFORMATION_SCHEMA.STATISTICS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + AND s.INDEX_NAME = 'PRIMARY' + ORDER BY s.SEQ_IN_INDEX; `, params: [schema, table_name] }); @@ -50,17 +50,17 @@ export async function getReplicationIdentityColumns( const [uniqueKeyColumns] = await retriedQuery({ db, query: ` - SELECT s.INDEX_NAME, s.COLUMN_NAME, c.DATA_TYPE, s.NON_UNIQUE, s.NULLABLE - FROM INFORMATION_SCHEMA.STATISTICS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? - AND s.TABLE_NAME = ? - AND s.INDEX_NAME != 'PRIMARY' - AND s.NON_UNIQUE = 0 - ORDER BY s.SEQ_IN_INDEX; + SELECT s.INDEX_NAME, s.COLUMN_NAME, c.DATA_TYPE, s.NON_UNIQUE, s.NULLABLE + FROM INFORMATION_SCHEMA.STATISTICS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + AND s.INDEX_NAME != 'PRIMARY' + AND s.NON_UNIQUE = 0 + ORDER BY s.SEQ_IN_INDEX; `, params: [schema, table_name] }); @@ -78,15 +78,15 @@ export async function getReplicationIdentityColumns( const [allColumns] = await retriedQuery({ db, query: ` - SELECT s.COLUMN_NAME AS name, c.DATA_TYPE as type - FROM INFORMATION_SCHEMA.COLUMNS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? - AND s.TABLE_NAME = ? - ORDER BY s.ORDINAL_POSITION; + SELECT s.COLUMN_NAME AS name, c.DATA_TYPE as type + FROM INFORMATION_SCHEMA.COLUMNS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + ORDER BY s.ORDINAL_POSITION; `, params: [schema, table_name] }); diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 7294ac1a4..f3eadc31d 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -261,7 +261,8 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { }; } - async getReplicationLag(syncRulesId: string): Promise { + async getReplicationLag(options: api.ReplicationLagOptions): Promise { + const { replication_identifier: slotName } = options; const results = await pg_utils.retriedQuery(this.pool, { statement: `SELECT slot_name, @@ -269,14 +270,14 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { pg_current_wal_lsn(), (pg_current_wal_lsn() - confirmed_flush_lsn) AS lsn_distance FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, - params: [{ type: 'varchar', value: syncRulesId }] + params: [{ type: 'varchar', value: slotName }] }); const [row] = pgwire.pgwireRows(results); if (row) { return Number(row.lsn_distance); } - throw new Error(`Could not determine replication lag for slot ${syncRulesId}`); + throw new Error(`Could not determine replication lag for slot ${slotName}`); } async getReplicationHead(): Promise { diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index 559336715..30e7bb74d 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -9,6 +9,11 @@ export interface PatternResult { table?: types.TableInfo; } +export interface ReplicationLagOptions { + replication_identifier: string; + last_checkpoint_identifier: string; +} + /** * Describes all the methods currently required to service the sync API endpoints. */ @@ -40,9 +45,8 @@ export interface RouteAPI { /** * @returns The replication lag: that is the amount of data which has not been * replicated yet, in bytes. - * @param {string} syncRulesId An identifier representing which set of sync rules the lag is required for. */ - getReplicationLag(syncRulesId: string): Promise; + getReplicationLag(options: ReplicationLagOptions): Promise; /** * Get the current LSN or equivalent replication HEAD position identifier diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 0ff0648f0..cdd1041e9 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -86,7 +86,11 @@ export async function getSyncRulesStatus( if (systemStorage) { try { - replication_lag_bytes = await api.getReplicationLag(systemStorage.slot_name); + const lastCheckpoint = await systemStorage.getCheckpoint(); + replication_lag_bytes = await api.getReplicationLag({ + replication_identifier: systemStorage.slot_name, + last_checkpoint_identifier: lastCheckpoint.lsn + }); } catch (e) { // Ignore logger.warn(`Unable to get replication lag`, e); From f174f013f1047f51980f078a1ce14e02abea8fea Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 10:49:15 +0200 Subject: [PATCH 104/298] cleanup --- .../src/api/MySQLRouteAPIAdapter.ts | 7 +- .../src/utils/replication/schema.ts | 69 ++++++++++++------- 2 files changed, 50 insertions(+), 26 deletions(-) diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 25688529b..f6f6bbbfa 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -238,7 +238,6 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { selectError = { level: 'fatal', message: e.message }; } - // Not sure if table level checks are possible yet return { schema: schema, name: tableName, @@ -261,7 +260,8 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { WHERE Event_type = 'Previous_gtids' AND Info = ? - LIMIT 1 + LIMIT + 1 `, params: [binLogFilename, last_checkpoint_identifier] }); @@ -283,7 +283,8 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { WHERE File = ? AND Info = ? - LIMIT 1 + LIMIT + 1 `, params: [binLogFilename] }); diff --git a/modules/module-mysql/src/utils/replication/schema.ts b/modules/module-mysql/src/utils/replication/schema.ts index fb3ac6fec..84dbd35b7 100644 --- a/modules/module-mysql/src/utils/replication/schema.ts +++ b/modules/module-mysql/src/utils/replication/schema.ts @@ -21,16 +21,23 @@ export async function getReplicationIdentityColumns( const [primaryKeyColumns] = await retriedQuery({ db, query: ` - SELECT s.COLUMN_NAME AS name, c.DATA_TYPE AS type - FROM INFORMATION_SCHEMA.STATISTICS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? + SELECT + s.COLUMN_NAME AS name, + c.DATA_TYPE AS type + FROM + INFORMATION_SCHEMA.STATISTICS s + JOIN + INFORMATION_SCHEMA.COLUMNS c + ON + s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE + s.TABLE_SCHEMA = ? AND s.TABLE_NAME = ? AND s.INDEX_NAME = 'PRIMARY' - ORDER BY s.SEQ_IN_INDEX; + ORDER BY + s.SEQ_IN_INDEX; `, params: [schema, table_name] }); @@ -50,13 +57,22 @@ export async function getReplicationIdentityColumns( const [uniqueKeyColumns] = await retriedQuery({ db, query: ` - SELECT s.INDEX_NAME, s.COLUMN_NAME, c.DATA_TYPE, s.NON_UNIQUE, s.NULLABLE - FROM INFORMATION_SCHEMA.STATISTICS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? + SELECT + s.INDEX_NAME, + s.COLUMN_NAME, + c.DATA_TYPE, + s.NON_UNIQUE, + s.NULLABLE + FROM + INFORMATION_SCHEMA.STATISTICS s + JOIN + INFORMATION_SCHEMA.COLUMNS c + ON + s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE + s.TABLE_SCHEMA = ? AND s.TABLE_NAME = ? AND s.INDEX_NAME != 'PRIMARY' AND s.NON_UNIQUE = 0 @@ -78,15 +94,22 @@ export async function getReplicationIdentityColumns( const [allColumns] = await retriedQuery({ db, query: ` - SELECT s.COLUMN_NAME AS name, c.DATA_TYPE as type - FROM INFORMATION_SCHEMA.COLUMNS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? + SELECT + s.COLUMN_NAME AS name, + c.DATA_TYPE as type + FROM + INFORMATION_SCHEMA.COLUMNS s + JOIN + INFORMATION_SCHEMA.COLUMNS c + ON + s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE + s.TABLE_SCHEMA = ? AND s.TABLE_NAME = ? - ORDER BY s.ORDINAL_POSITION; + ORDER BY + s.ORDINAL_POSITION; `, params: [schema, table_name] }); From 196f21ce75c418534bbd72c882d9a38ba9a4198e Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 11:47:53 +0200 Subject: [PATCH 105/298] cleanup retried queries. Add SSL config for MySQL config --- .../src/api/MySQLRouteAPIAdapter.ts | 58 +++++++++++-------- .../module-mysql/src/module/MySQLModule.ts | 1 + modules/module-mysql/src/types/types.ts | 8 ++- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index f6f6bbbfa..a0471b64e 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -21,7 +21,12 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { host: config.hostname, user: config.username, password: config.password, - database: config.database + database: config.database, + ssl: { + ca: config.cacert, + key: config.client_private_key, + cert: config.client_certificate + } }); } @@ -39,8 +44,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { uri: `mysql://${this.config.hostname}:${this.config.port}/${this.config.database}` }; try { - await retriedQuery({ - db: this.pool, + await this.retriedQuery({ query: `SELECT 'PowerSync connection test'` }); } catch (e) { @@ -230,8 +234,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { let selectError: service_types.ReplicationError | null = null; try { - await retriedQuery({ - db: this.pool, + await this.retriedQuery({ query: `SELECT * FROM ${sourceTable.table} LIMIT 1` }); } catch (e) { @@ -252,16 +255,15 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { async getReplicationLag(options: api.ReplicationLagOptions): Promise { const { replication_identifier: binLogFilename, last_checkpoint_identifier } = options; - const [[gtidEvent]] = await retriedQuery({ - db: this.pool, + const [[gtidEvent]] = await this.retriedQuery({ query: ` - SHOW - BINLOG EVENTS IN ? - WHERE - Event_type = 'Previous_gtids' - AND Info = ? - LIMIT - 1 + SHOW + BINLOG EVENTS IN ? + WHERE + Event_type = 'Previous_gtids' + AND Info = ? + LIMIT + 1 `, params: [binLogFilename, last_checkpoint_identifier] }); @@ -275,16 +277,15 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { const headBinlogPosition = gtidEvent.Pos; // Get the position of the latest event in the BinLog file - const [[binLogEntry]] = await retriedQuery({ - db: this.pool, + const [[binLogEntry]] = await this.retriedQuery({ query: ` - SHOW - master status - WHERE - File = ? - AND Info = ? - LIMIT - 1 + SHOW + master status + WHERE + File = ? + AND Info = ? + LIMIT + 1 `, params: [binLogFilename] }); @@ -301,8 +302,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async getConnectionSchema(): Promise { - const [results] = await retriedQuery({ - db: this.pool, + const [results] = await this.retriedQuery({ query: ` SELECT tbl.schemaname, @@ -367,4 +367,12 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { }, {}) ); } + + protected retriedQuery(options: { query: string; params?: any[] }) { + return retriedQuery({ + db: this.pool, + query: options.query, + params: options.params + }); + } } diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts index d5b8afa2e..e121ae213 100644 --- a/modules/module-mysql/src/module/MySQLModule.ts +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -16,6 +16,7 @@ export class MySQLModule extends replication.ReplicationModule { await super.initialize(context); + // TODO move this to the binlog consumer // jpgwire.setMetricsRecorder({ // addBytesRead(bytes) { // context.metrics.data_replicated_bytes.add(bytes); diff --git a/modules/module-mysql/src/types/types.ts b/modules/module-mysql/src/types/types.ts index 34b80ce46..835d6c8d4 100644 --- a/modules/module-mysql/src/types/types.ts +++ b/modules/module-mysql/src/types/types.ts @@ -26,7 +26,13 @@ export const MySQLConnectionConfig = service_types.configFile.dataSourceConfig.a port: service_types.configFile.portCodec.optional(), username: t.string.optional(), password: t.string.optional(), - database: t.string.optional() + database: t.string.optional(), + + /** Required for verify-ca, optional for verify-full */ + cacert: t.string.optional(), + + client_certificate: t.string.optional(), + client_private_key: t.string.optional() }) ); From a6614f1490ff54c7e68134e79d2321e96c531a82 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 12:11:02 +0200 Subject: [PATCH 106/298] Use framework errors. Update DataSourceConfig and ResolvedDataSourceConfig. Added DatabaseSchemaV2 type to remove Postgres fields. --- .../src/api/MySQLRouteAPIAdapter.ts | 21 +++++++----- modules/module-mysql/src/types/types.ts | 3 +- .../src/api/PostgresRouteAPIAdapter.ts | 6 ++-- modules/module-postgres/src/types/types.ts | 2 +- packages/service-core/src/api/RouteAPI.ts | 4 +-- packages/service-core/src/api/schema.ts | 23 ++++++++++--- .../src/routes/endpoints/admin.ts | 12 ++++--- packages/types/src/config/PowerSyncConfig.ts | 19 +++++++++-- packages/types/src/definitions.ts | 34 +++++++++++++++++++ 9 files changed, 97 insertions(+), 27 deletions(-) diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index a0471b64e..5131ee06e 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -1,5 +1,6 @@ import { api, storage } from '@powersync/service-core'; +import * as framework from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; @@ -34,7 +35,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { return this.pool.end(); } - async getSourceConfig(): Promise { + async getSourceConfig(): Promise { return this.config; } @@ -269,7 +270,10 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { }); if (!gtidEvent) { - throw new Error(`Could not find binlog event for GTID`); + throw new framework.errors.ResourceNotFound( + 'GTID', + `Could not find binlog event for ${last_checkpoint_identifier}` + ); } // This is the BinLog file position at the last replicated GTID. @@ -291,7 +295,10 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { }); if (!binLogEntry) { - throw new Error(`Could not find master status for binlog file`); + throw new framework.errors.ResourceNotFound( + `BinLog Status`, + `Could not find master status for ${binLogFilename} file` + ); } return binLogEntry.Position - headBinlogPosition; @@ -301,7 +308,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { return readMasterComparableGtid(this.pool); } - async getConnectionSchema(): Promise { + async getConnectionSchema(): Promise { const [results] = await this.retriedQuery({ query: ` SELECT @@ -345,7 +352,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { */ return Object.values( - (results as SchemaResult[]).reduce((hash: Record, result) => { + (results as SchemaResult[]).reduce((hash: Record, result) => { const schema = hash[result.schemaname] || (hash[result.schemaname] = { @@ -357,9 +364,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { name: result.tablename, columns: result.columns.map((column) => ({ name: column.column_name, - type: column.data_type, - // FIXME remove this - pg_type: column.data_type + type: column.data_type })) }); diff --git a/modules/module-mysql/src/types/types.ts b/modules/module-mysql/src/types/types.ts index 835d6c8d4..876f0da83 100644 --- a/modules/module-mysql/src/types/types.ts +++ b/modules/module-mysql/src/types/types.ts @@ -18,7 +18,7 @@ export interface NormalizedMySQLConnectionConfig { // TODO SSL config } -export const MySQLConnectionConfig = service_types.configFile.dataSourceConfig.and( +export const MySQLConnectionConfig = service_types.configFile.DataSourceConfig.and( t.object({ type: t.literal(MYSQL_CONNECTION_TYPE), uri: t.string.optional(), @@ -28,7 +28,6 @@ export const MySQLConnectionConfig = service_types.configFile.dataSourceConfig.a password: t.string.optional(), database: t.string.optional(), - /** Required for verify-ca, optional for verify-full */ cacert: t.string.optional(), client_certificate: t.string.optional(), diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index f3eadc31d..bbeca2ec0 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -25,7 +25,7 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { await this.pool.end(); } - async getSourceConfig(): Promise { + async getSourceConfig(): Promise { return this.config; } @@ -287,7 +287,7 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, return String(lsn); } - async getConnectionSchema(): Promise { + async getConnectionSchema(): Promise { // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts const results = await pg_utils.retriedQuery( this.pool, @@ -356,7 +356,7 @@ GROUP BY schemaname, tablename, quoted_name` table.columns.push({ name: column.attname, type: column.data_type, - pg_type: pg_type + internal_type: pg_type }); } } diff --git a/modules/module-postgres/src/types/types.ts b/modules/module-postgres/src/types/types.ts index 4f4acbb83..8b1e49c57 100644 --- a/modules/module-postgres/src/types/types.ts +++ b/modules/module-postgres/src/types/types.ts @@ -22,7 +22,7 @@ export interface NormalizedPostgresConnectionConfig { client_private_key: string | undefined; } -export const PostgresConnectionConfig = service_types.configFile.dataSourceConfig.and( +export const PostgresConnectionConfig = service_types.configFile.DataSourceConfig.and( t.object({ type: t.literal(POSTGRES_CONNECTION_TYPE), /** Unique identifier for the connection - optional when a single connection is present. */ diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index 30e7bb74d..57514c48f 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -21,7 +21,7 @@ export interface RouteAPI { /** * @returns basic identification of the connection */ - getSourceConfig(): Promise; + getSourceConfig(): Promise; /** * Checks the current connection status of the data source. @@ -60,7 +60,7 @@ export interface RouteAPI { * contains `pg_type` which we might need to deprecate and add another generic * type field - or just use this field as the connection specific type. */ - getConnectionSchema(): Promise; + getConnectionSchema(): Promise; /** * Executes a query and return the result from the data source. This is currently used in the diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index 30e737363..98495f007 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -10,13 +10,28 @@ export async function getConnectionsSchema(api: api.RouteAPI | null): Promise ({ + ...schema, + tables: schema.tables.map((table) => ({ + ...table, + columns: table.columns.map((column) => ({ + ...column, + // Defaults back to the original type if not provided + pg_type: column.internal_type ?? column.type + })) + })) + })) } ] }; diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index 55af1dd07..3b634a9de 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -1,4 +1,4 @@ -import { errors, router, schema } from '@powersync/lib-services-framework'; +import { errors, router, schema, ValidationError } from '@powersync/lib-services-framework'; import { SqlSyncRules, StaticSchema } from '@powersync/service-sync-rules'; import { internal_routes } from '@powersync/service-types'; @@ -129,14 +129,18 @@ export const reprocess = routeDefinition({ }); const apiHandler = service_context.routerEngine.getAPI(); - const baseConfig = await apiHandler?.getSourceConfig(); + if (!apiHandler) { + throw new ValidationError(`No active route API handler has been found.`); + } + + const baseConfig = await apiHandler.getSourceConfig(); return internal_routes.ReprocessResponse.encode({ connections: [ { // Previously the connection was asserted with `!` - tag: baseConfig!.tag!, - id: baseConfig!.id, + tag: baseConfig.tag, + id: baseConfig.id, slot_name: new_rules.slot_name } ] diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index 87607842c..404c41710 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -19,7 +19,7 @@ export const portParser = { }) }; -export const dataSourceConfig = t +export const DataSourceConfig = t .object({ // Unique string identifier for the data source type: t.string, @@ -34,7 +34,20 @@ export const dataSourceConfig = t }) .and(t.record(t.any)); // This essentially allows any extra fields on this type -export type DataSourceConfig = t.Decoded; +export type DataSourceConfig = t.Decoded; + +/** + * Resolved version of {@link DataSourceConfig} where the optional + * `id` and `tag` field is now required. + */ +export const ResolvedDataSourceConfig = DataSourceConfig.and( + t.object({ + id: t.string, + tag: t.string + }) +); + +export type ResolvedDataSourceConfig = t.Decoded; export const jwkRSA = t.object({ kty: t.literal('RSA'), @@ -78,7 +91,7 @@ export type StorageConfig = t.Decoded; export const powerSyncConfig = t.object({ replication: t .object({ - connections: t.array(dataSourceConfig).optional() + connections: t.array(DataSourceConfig).optional() }) .optional(), diff --git a/packages/types/src/definitions.ts b/packages/types/src/definitions.ts index 35dc64177..51e3959e1 100644 --- a/packages/types/src/definitions.ts +++ b/packages/types/src/definitions.ts @@ -114,6 +114,29 @@ export const DatabaseSchema = t.object({ }); export type DatabaseSchema = t.Encoded; +export const DatabaseSchemaV2 = t.object({ + name: t.string, + tables: t.array( + t.object({ + name: t.string, + columns: t.array( + t.object({ + name: t.string, + /** + * Full type name, e.g. "character varying(255)[]" + */ + type: t.string, + /** + * Internal type, e.g. "varchar[]". + */ + internal_type: t.string.optional() + }) + ) + }) + ) +}); +export type DatabaseSchemaV2 = t.Encoded; + export const InstanceSchema = t.object({ connections: t.array( t.object({ @@ -124,3 +147,14 @@ export const InstanceSchema = t.object({ ) }); export type InstanceSchema = t.Encoded; + +export const InstanceSchemaV2 = t.object({ + connections: t.array( + t.object({ + id: t.string.optional(), + tag: t.string, + schemas: t.array(DatabaseSchemaV2) + }) + ) +}); +export type InstanceSchemaV2 = t.Encoded; From 4a0b5cef6f526a9ed7a41339b677dc78cdca484d Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 12:25:00 +0200 Subject: [PATCH 107/298] more mysql cleanup --- modules/module-mysql/src/types/types.ts | 5 +++-- modules/module-mysql/src/utils/mysql_utils.ts | 4 ++-- packages/service-core/src/api/diagnostics.ts | 4 ++++ service/src/runners/server.ts | 6 +++--- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/module-mysql/src/types/types.ts b/modules/module-mysql/src/types/types.ts index 876f0da83..9c188bf80 100644 --- a/modules/module-mysql/src/types/types.ts +++ b/modules/module-mysql/src/types/types.ts @@ -15,7 +15,9 @@ export interface NormalizedMySQLConnectionConfig { username: string; password: string; - // TODO SSL config + cacert?: string; + client_certificate?: string; + client_private_key?: string; } export const MySQLConnectionConfig = service_types.configFile.DataSourceConfig.and( @@ -29,7 +31,6 @@ export const MySQLConnectionConfig = service_types.configFile.DataSourceConfig.a database: t.string.optional(), cacert: t.string.optional(), - client_certificate: t.string.optional(), client_private_key: t.string.optional() }) diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 202059fd9..c075c5899 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -97,6 +97,6 @@ export function gtidMakeComparable(gtid: string): string { } export async function readMasterComparableGtid(db: mysql.Pool): Promise { - const results = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); - return gtidMakeComparable(results[0].length > 0 ? `${results[0][0]['GTID']}` : '0:0'); + const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); + return gtidMakeComparable(gtidResult?.GTID ?? '0:0'); } diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index cdd1041e9..f3b5810c9 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -64,6 +64,10 @@ export async function getSyncRulesStatus( let tables_flat: TableInfo[] = []; const sourceConfig = await api?.getSourceConfig(); + // This is a bit weird. + // This method can run under some situations if no connection is configured. + // It will return a default tag if not connection is available. This default tag + // is not module specific. const tag = sourceConfig?.tag ?? DEFAULT_TAG; if (check_connection) { diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 2be282f48..461bbdd60 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -12,9 +12,9 @@ import { SocketRouter } from '../routes/router.js'; */ export const registerServerServices = (serviceContext: core.system.ServiceContextContainer) => { serviceContext.register(core.routes.RouterEngine, new core.routes.RouterEngine()); - serviceContext.lifeCycleEngine.withLifecycle(serviceContext.routerEngine, { + serviceContext.lifeCycleEngine.withLifecycle(serviceContext.routerEngine!, { start: async (routerEngine) => { - await routerEngine!.start(async (routes) => { + await routerEngine.start(async (routes) => { const server = fastify.fastify(); server.register(cors, { @@ -54,7 +54,7 @@ export const registerServerServices = (serviceContext: core.system.ServiceContex }; }); }, - stop: (routerEngine) => routerEngine!.shutdown() + stop: (routerEngine) => routerEngine.shutdown() }); }; From b3a5c086f0dcb0f7192d7cc004deff2948ed4cb1 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 12:30:40 +0200 Subject: [PATCH 108/298] update failing Github action name to be clearer --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c02dd753..b5ba9011c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push + - name: Test Build Docker Image uses: docker/build-push-action@v5 with: cache-from: type=registry,ref=stevenontong/${{vars.DOCKER_REGISTRY}}:cache From 76bac2fcdd4579c356f4c022dc1511a149362a7f Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 15:14:20 +0200 Subject: [PATCH 109/298] share some utilities. Playing around with replication --- .../src/api/MySQLRouteAPIAdapter.ts | 56 +++++-------- .../module-mysql/src/module/MySQLModule.ts | 2 +- .../src/replication/MSSQLReplicator.ts | 22 +++-- modules/module-mysql/src/utils/mysql_utils.ts | 84 ++++--------------- .../src/utils/replication/configuration.ts | 77 +++++++++++++++++ .../utils/replication/replication-utils.ts | 2 + .../src/replication/core/Replicator.ts | 2 +- 7 files changed, 135 insertions(+), 110 deletions(-) create mode 100644 modules/module-mysql/src/utils/replication/configuration.ts create mode 100644 modules/module-mysql/src/utils/replication/replication-utils.ts diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 5131ee06e..fc97dd0ce 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -5,12 +5,12 @@ import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; import * as types from '../types/types.js'; -import { checkSourceConfiguration, readMasterComparableGtid, retriedQuery } from '../utils/mysql_utils.js'; -import { getReplicationIdentityColumns, ReplicationIdentityColumnsResult } from '../utils/replication/schema.js'; +import * as mysql_utils from '../utils/mysql_utils.js'; +import * as replication_utils from '../utils/replication/replication-utils.js'; type SchemaResult = { - schemaname: string; - tablename: string; + schema_name: string; + table_name: string; columns: Array<{ data_type: string; column_name: string }>; }; @@ -18,17 +18,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { protected pool: mysql.Pool; constructor(protected config: types.ResolvedConnectionConfig) { - this.pool = mysql.createPool({ - host: config.hostname, - user: config.username, - password: config.password, - database: config.database, - ssl: { - ca: config.cacert, - key: config.client_private_key, - cert: config.client_certificate - } - }); + this.pool = mysql_utils.createPool(config); } async shutdown(): Promise { @@ -56,7 +46,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { }; } try { - const errors = await checkSourceConfiguration(this.pool); + const errors = await replication_utils.checkSourceConfiguration(this.pool); if (errors.length) { return { ...base, @@ -208,10 +198,10 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { ): Promise { const { schema } = tablePattern; - let idColumnsResult: ReplicationIdentityColumnsResult | null = null; + let idColumnsResult: replication_utils.ReplicationIdentityColumnsResult | null = null; let idColumnsError: service_types.ReplicationError | null = null; try { - idColumnsResult = await getReplicationIdentityColumns({ + idColumnsResult = await replication_utils.getReplicationIdentityColumns({ db: this.pool, schema, table_name: tableName @@ -305,22 +295,22 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async getReplicationHead(): Promise { - return readMasterComparableGtid(this.pool); + return replication_utils.readMasterComparableGtid(this.pool); } async getConnectionSchema(): Promise { const [results] = await this.retriedQuery({ query: ` SELECT - tbl.schemaname, - tbl.tablename, + tbl.schema_name, + tbl.table_name, tbl.quoted_name, JSON_ARRAYAGG(JSON_OBJECT('column_name', a.column_name, 'data_type', a.data_type)) AS columns FROM ( SELECT - TABLE_SCHEMA AS schemaname, - TABLE_NAME AS tablename, + TABLE_SCHEMA AS schema_name, + TABLE_NAME AS table_name, CONCAT('\`', TABLE_SCHEMA, '\`.\`', TABLE_NAME, '\`') AS quoted_name FROM INFORMATION_SCHEMA.TABLES @@ -331,18 +321,18 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { LEFT JOIN ( SELECT - TABLE_SCHEMA AS schemaname, - TABLE_NAME AS tablename, + TABLE_SCHEMA AS schema_name, + TABLE_NAME AS table_name, COLUMN_NAME AS column_name, COLUMN_TYPE AS data_type FROM INFORMATION_SCHEMA.COLUMNS ) AS a ON - tbl.schemaname = a.schemaname - AND tbl.tablename = a.tablename + tbl.schema_name = a.schema_name + AND tbl.table_name = a.table_name GROUP BY - tbl.schemaname, tbl.tablename, tbl.quoted_name; + tbl.schema_name, tbl.table_name, tbl.quoted_name; ` }); @@ -354,14 +344,14 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { return Object.values( (results as SchemaResult[]).reduce((hash: Record, result) => { const schema = - hash[result.schemaname] || - (hash[result.schemaname] = { - name: result.schemaname, + hash[result.schema_name] || + (hash[result.schema_name] = { + name: result.schema_name, tables: [] }); schema.tables.push({ - name: result.tablename, + name: result.table_name, columns: result.columns.map((column) => ({ name: column.column_name, type: column.data_type @@ -374,7 +364,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } protected retriedQuery(options: { query: string; params?: any[] }) { - return retriedQuery({ + return mysql_utils.retriedQuery({ db: this.pool, query: options.query, params: options.params diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts index e121ae213..f04ee117f 100644 --- a/modules/module-mysql/src/module/MySQLModule.ts +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -30,7 +30,7 @@ export class MySQLModule extends replication.ReplicationModule { + console.log('starting'); } - stop(): Promise { - throw new Error('Method not implemented.'); + async stop(): Promise { + // throw new Error('Method not implemented.'); } } diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index c075c5899..8f65bb500 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -1,5 +1,6 @@ import { logger } from '@powersync/lib-services-framework'; import mysql from 'mysql2/promise'; +import * as types from '../types/types.js'; export type RetiredMySQLQueryOptions = { db: mysql.Pool; @@ -26,77 +27,20 @@ export async function retriedQuery(options: RetiredMySQLQueryOptions) { } } -export async function checkSourceConfiguration(db: mysql.Pool) { - const errors: string[] = []; - const [[result]] = await retriedQuery({ - db, - query: ` - SELECT - @@GLOBAL.gtid_mode AS gtid_mode, - @@GLOBAL.log_bin AS log_bin, - @@GLOBAL.server_id AS server_id, - @@GLOBAL.log_bin_basename AS binlog_file, - @@GLOBAL.log_bin_index AS binlog_index_file - ` - }); - - if (result.gtid_mode != 'ON') { - errors.push(`GTID is not enabled, it is currently set to ${result.gtid_mode}. Please enable it.`); - } - - if (result.log_bin != 1) { - errors.push('Binary logging is not enabled. Please enable it.'); - } - - if (result.server_id < 0) { - errors.push( - `Your Server ID setting is too low, it must be greater than 0. It is currently ${result.server_id}. Please correct your configuration.` - ); - } - - if (!result.binlog_file) { - errors.push('Binary log file is not set. Please check your settings.'); - } - - if (!result.binlog_index_file) { - errors.push('Binary log index file is not set. Please check your settings.'); - } - - const [[binLogFormatResult]] = await retriedQuery({ - db, - query: `SHOW VARIABLES LIKE 'binlog_format';` +export function createPool(config: types.ResolvedConnectionConfig) { + return mysql.createPool({ + host: config.hostname, + user: config.username, + password: config.password, + database: config.database, + ssl: { + ca: config.cacert, + key: config.client_private_key, + cert: config.client_certificate + } }); - - if (binLogFormatResult.Value !== 'ROW') { - errors.push('Binary log format must be set to "ROW". Please correct your configuration'); - } - - return errors; -} - -/** - * Transforms a GTID into a comparable string format, ensuring lexicographical - * order aligns with the GTID's relative age. This assumes that all GTIDs - * have the same server ID. - * - * @param gtid - The GTID string in the format 'server_id:transaction_ranges' - * @returns A comparable string in the format 'padded_end_transaction|original_gtid' - */ -export function gtidMakeComparable(gtid: string): string { - const [serverId, transactionRanges] = gtid.split(':'); - - let maxTransactionId = 0; - - for (const range of transactionRanges.split(',')) { - const [start, end] = range.split('-'); - maxTransactionId = Math.max(maxTransactionId, parseInt(start, 10), parseInt(end || start, 10)); - } - - const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); - return `${paddedTransactionId}|${gtid}`; } -export async function readMasterComparableGtid(db: mysql.Pool): Promise { - const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); - return gtidMakeComparable(gtidResult?.GTID ?? '0:0'); +export function createConnection(config: types.ResolvedConnectionConfig) { + return mysql.createConnection(config); } diff --git a/modules/module-mysql/src/utils/replication/configuration.ts b/modules/module-mysql/src/utils/replication/configuration.ts new file mode 100644 index 000000000..d8e1d837f --- /dev/null +++ b/modules/module-mysql/src/utils/replication/configuration.ts @@ -0,0 +1,77 @@ +import mysql from 'mysql2/promise'; +import { retriedQuery } from '../mysql_utils.js'; + +export async function checkSourceConfiguration(db: mysql.Pool) { + const errors: string[] = []; + const [[result]] = await retriedQuery({ + db, + query: ` + SELECT + @@GLOBAL.gtid_mode AS gtid_mode, + @@GLOBAL.log_bin AS log_bin, + @@GLOBAL.server_id AS server_id, + @@GLOBAL.log_bin_basename AS binlog_file, + @@GLOBAL.log_bin_index AS binlog_index_file + ` + }); + + if (result.gtid_mode != 'ON') { + errors.push(`GTID is not enabled, it is currently set to ${result.gtid_mode}. Please enable it.`); + } + + if (result.log_bin != 1) { + errors.push('Binary logging is not enabled. Please enable it.'); + } + + if (result.server_id < 0) { + errors.push( + `Your Server ID setting is too low, it must be greater than 0. It is currently ${result.server_id}. Please correct your configuration.` + ); + } + + if (!result.binlog_file) { + errors.push('Binary log file is not set. Please check your settings.'); + } + + if (!result.binlog_index_file) { + errors.push('Binary log index file is not set. Please check your settings.'); + } + + const [[binLogFormatResult]] = await retriedQuery({ + db, + query: `SHOW VARIABLES LIKE 'binlog_format';` + }); + + if (binLogFormatResult.Value !== 'ROW') { + errors.push('Binary log format must be set to "ROW". Please correct your configuration'); + } + + return errors; +} + +/** + * Transforms a GTID into a comparable string format, ensuring lexicographical + * order aligns with the GTID's relative age. This assumes that all GTIDs + * have the same server ID. + * + * @param gtid - The GTID string in the format 'server_id:transaction_ranges' + * @returns A comparable string in the format 'padded_end_transaction|original_gtid' + */ +export function gtidMakeComparable(gtid: string): string { + const [serverId, transactionRanges] = gtid.split(':'); + + let maxTransactionId = 0; + + for (const range of transactionRanges.split(',')) { + const [start, end] = range.split('-'); + maxTransactionId = Math.max(maxTransactionId, parseInt(start, 10), parseInt(end || start, 10)); + } + + const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); + return `${paddedTransactionId}|${gtid}`; +} + +export async function readMasterComparableGtid(db: mysql.Pool): Promise { + const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); + return gtidMakeComparable(gtidResult?.GTID ?? '0:0'); +} diff --git a/modules/module-mysql/src/utils/replication/replication-utils.ts b/modules/module-mysql/src/utils/replication/replication-utils.ts new file mode 100644 index 000000000..d9797a96e --- /dev/null +++ b/modules/module-mysql/src/utils/replication/replication-utils.ts @@ -0,0 +1,2 @@ +export * from './configuration.js'; +export * from './schema.js'; diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts index 9bbca6d91..46c93086e 100644 --- a/packages/service-core/src/replication/core/Replicator.ts +++ b/packages/service-core/src/replication/core/Replicator.ts @@ -8,6 +8,6 @@ export interface Replicator { * Unique identifier for this replicator */ id: string; - start(): void; + start(): Promise; stop(): Promise; } From ce78ee89340c6007dfe30d29a84d2af916bbecd1 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 15 Aug 2024 17:32:52 +0200 Subject: [PATCH 110/298] unfortunate implementation of replication lag --- modules/module-mysql/package.json | 1 + .../src/api/MySQLRouteAPIAdapter.ts | 53 ++++----------- .../src/replication/MSSQLReplicator.ts | 23 ++++++- .../src/utils/replication/configuration.ts | 27 -------- .../src/utils/replication/gtid-compare.ts | 31 +++++++++ .../src/utils/replication/gtid-distance.ts | 64 +++++++++++++++++++ .../src/utils/replication/gtid-position.ts | 55 ++++++++++++++++ .../utils/replication/replication-utils.ts | 3 + .../src/replication/WalStreamManager.ts | 10 +-- pnpm-lock.yaml | 48 ++++++++++++++ 10 files changed, 240 insertions(+), 75 deletions(-) create mode 100644 modules/module-mysql/src/utils/replication/gtid-compare.ts create mode 100644 modules/module-mysql/src/utils/replication/gtid-distance.ts create mode 100644 modules/module-mysql/src/utils/replication/gtid-position.ts diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 59e120b6d..23bb0f21d 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -28,6 +28,7 @@ "@powersync/service-jsonbig": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", + "@vlasky/zongji": "^0.5.9", "mysql2": "^3.11.0", "ts-codec": "^1.2.2", "uri-js": "^4.4.1", diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index fc97dd0ce..e9d76c112 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -1,6 +1,5 @@ import { api, storage } from '@powersync/service-core'; -import * as framework from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; @@ -244,54 +243,24 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async getReplicationLag(options: api.ReplicationLagOptions): Promise { - const { replication_identifier: binLogFilename, last_checkpoint_identifier } = options; + const { last_checkpoint_identifier } = options; - const [[gtidEvent]] = await this.retriedQuery({ - query: ` - SHOW - BINLOG EVENTS IN ? - WHERE - Event_type = 'Previous_gtids' - AND Info = ? - LIMIT - 1 - `, - params: [binLogFilename, last_checkpoint_identifier] + const [[currentHead]] = await this.retriedQuery({ + query: `SELECT @@GLOBAL.gtid_executed as GTID;` }); - if (!gtidEvent) { - throw new framework.errors.ResourceNotFound( - 'GTID', - `Could not find binlog event for ${last_checkpoint_identifier}` - ); - } - - // This is the BinLog file position at the last replicated GTID. - // The position is the file offset in bytes. - const headBinlogPosition = gtidEvent.Pos; - - // Get the position of the latest event in the BinLog file - const [[binLogEntry]] = await this.retriedQuery({ - query: ` - SHOW - master status - WHERE - File = ? - AND Info = ? - LIMIT - 1 - `, - params: [binLogFilename] + const lag = await replication_utils.getGTIDDistance({ + db: this.pool, + // This is a comparable identifier, need the raw GTID + start_gtid: replication_utils.extractGTIDComparable(last_checkpoint_identifier), + end_gtid: currentHead.GTID }); - if (!binLogEntry) { - throw new framework.errors.ResourceNotFound( - `BinLog Status`, - `Could not find master status for ${binLogFilename} file` - ); + if (lag == null) { + throw new Error(`Could not determine replication lag`); } - return binLogEntry.Position - headBinlogPosition; + return lag; } async getReplicationHead(): Promise { diff --git a/modules/module-mysql/src/replication/MSSQLReplicator.ts b/modules/module-mysql/src/replication/MSSQLReplicator.ts index 98c8383d3..4d6801f11 100644 --- a/modules/module-mysql/src/replication/MSSQLReplicator.ts +++ b/modules/module-mysql/src/replication/MSSQLReplicator.ts @@ -1,4 +1,6 @@ import { replication } from '@powersync/service-core'; +//@ts-ignore +import ZongJi from '@vlasky/zongji'; import mysql_promise from 'mysql2/promise'; import * as types from '../types/types.js'; import * as mysql_utils from '../utils/mysql_utils.js'; @@ -10,12 +12,31 @@ export class MSSQLReplicator implements replication.Replicator { constructor(protected config: types.ResolvedConnectionConfig) { this.id = 'mysql'; - this.pool = mysql_utils.createPool(config); } async start(): Promise { console.log('starting'); + const zongji = new ZongJi({ + host: this.config.hostname, + user: this.config.username, + password: this.config.password + // debug: true + }); + + zongji.on('binlog', function (evt: any) { + // evt.dump(); + console.log(evt); + }); + + zongji.start({ + includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'] + }); + + process.on('SIGINT', function () { + console.log('Got SIGINT.'); + zongji.stop(); + }); } async stop(): Promise { // throw new Error('Method not implemented.'); diff --git a/modules/module-mysql/src/utils/replication/configuration.ts b/modules/module-mysql/src/utils/replication/configuration.ts index d8e1d837f..3ecca7269 100644 --- a/modules/module-mysql/src/utils/replication/configuration.ts +++ b/modules/module-mysql/src/utils/replication/configuration.ts @@ -48,30 +48,3 @@ export async function checkSourceConfiguration(db: mysql.Pool) { return errors; } - -/** - * Transforms a GTID into a comparable string format, ensuring lexicographical - * order aligns with the GTID's relative age. This assumes that all GTIDs - * have the same server ID. - * - * @param gtid - The GTID string in the format 'server_id:transaction_ranges' - * @returns A comparable string in the format 'padded_end_transaction|original_gtid' - */ -export function gtidMakeComparable(gtid: string): string { - const [serverId, transactionRanges] = gtid.split(':'); - - let maxTransactionId = 0; - - for (const range of transactionRanges.split(',')) { - const [start, end] = range.split('-'); - maxTransactionId = Math.max(maxTransactionId, parseInt(start, 10), parseInt(end || start, 10)); - } - - const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); - return `${paddedTransactionId}|${gtid}`; -} - -export async function readMasterComparableGtid(db: mysql.Pool): Promise { - const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); - return gtidMakeComparable(gtidResult?.GTID ?? '0:0'); -} diff --git a/modules/module-mysql/src/utils/replication/gtid-compare.ts b/modules/module-mysql/src/utils/replication/gtid-compare.ts new file mode 100644 index 000000000..432035cc7 --- /dev/null +++ b/modules/module-mysql/src/utils/replication/gtid-compare.ts @@ -0,0 +1,31 @@ +import mysql from 'mysql2/promise'; +/** + * Transforms a GTID into a comparable string format, ensuring lexicographical + * order aligns with the GTID's relative age. This assumes that all GTIDs + * have the same server ID. + * + * @param gtid - The GTID string in the format 'server_id:transaction_ranges' + * @returns A comparable string in the format 'padded_end_transaction|original_gtid' + */ +export function gtidMakeComparable(gtid: string): string { + const [serverId, transactionRanges] = gtid.split(':'); + + let maxTransactionId = 0; + + for (const range of transactionRanges.split(',')) { + const [start, end] = range.split('-'); + maxTransactionId = Math.max(maxTransactionId, parseInt(start, 10), parseInt(end || start, 10)); + } + + const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); + return `${paddedTransactionId}|${gtid}`; +} + +export function extractGTIDComparable(comparableGTID: string) { + return comparableGTID.split('|')[1]; +} + +export async function readMasterComparableGtid(db: mysql.Pool): Promise { + const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); + return gtidMakeComparable(gtidResult?.GTID ?? '0:0'); +} diff --git a/modules/module-mysql/src/utils/replication/gtid-distance.ts b/modules/module-mysql/src/utils/replication/gtid-distance.ts new file mode 100644 index 000000000..568e480c0 --- /dev/null +++ b/modules/module-mysql/src/utils/replication/gtid-distance.ts @@ -0,0 +1,64 @@ +import mysql from 'mysql2/promise'; +import { retriedQuery } from '../mysql_utils.js'; +import { getGTIDPositions } from './gtid-position.js'; + +export type GetGTIDDistanceOptions = { + db: mysql.Pool; + start_gtid: string; + end_gtid: string; + page_size?: number; +}; + +/** + * Get the distance in bytes between GTIDs + */ +export async function getGTIDDistance(options: GetGTIDDistanceOptions): Promise { + const { db, page_size = 1000, start_gtid, end_gtid } = options; + const [logFiles] = await retriedQuery({ + db, + query: `SHOW BINARY LOGS;` + }); + + const gtids = new Set(); + gtids.add(start_gtid); + gtids.add(end_gtid); + + const positions = await getGTIDPositions({ + db, + gtids, + page_size + }); + + const endResult = positions.get(end_gtid); + const startResult = positions.get(start_gtid); + + if (!endResult || !startResult) { + return null; + } + + if (endResult.filename == startResult.filename) { + return endResult.position - startResult.position; + } + + // The entries are on different files + const startFileIndex = logFiles.findIndex((f) => f['Log_name'] == startResult.filename); + const startFileEntry = logFiles[startFileIndex]; + + if (!startFileEntry) { + return null; + } + + const endFileIndex = logFiles.findIndex((f) => f['Log_name'] == endResult.filename); + const endFileEntry = logFiles[endFileIndex]; + + if (!endFileEntry) { + return null; + } + + return ( + startFileEntry['File_size'] - + startResult.position + + endResult.position + + logFiles.slice(startFileIndex + 1, endFileIndex).reduce((sum, file) => sum + file['File_size'], 0) + ); +} diff --git a/modules/module-mysql/src/utils/replication/gtid-position.ts b/modules/module-mysql/src/utils/replication/gtid-position.ts new file mode 100644 index 000000000..2429ee3b3 --- /dev/null +++ b/modules/module-mysql/src/utils/replication/gtid-position.ts @@ -0,0 +1,55 @@ +import mysql from 'mysql2/promise'; +import { retriedQuery } from '../mysql_utils.js'; + +export type GetGTIDPositionOptions = { + db: mysql.Pool; + gtids: Set; + page_size?: number; +}; + +export type GTIDPosition = { + filename: string; + position: number; +}; + +/** + * Given a set of GTID it will find the binlog files and offset positions. + */ +export async function getGTIDPositions(options: GetGTIDPositionOptions): Promise> { + const { db, gtids, page_size = 1000 } = options; + const [binLogFileResults] = await retriedQuery({ + db, + query: `SHOW BINARY LOGS` + }); + + const binLogFilenames: string[] = binLogFileResults.map((r) => r.Log_name); + const output: Map = new Map(); + + for (const filename of binLogFilenames.reverse()) { + let returnedCount = 0; + let pageIndex = 0; + do { + // Unfortunately this cannot be sorted in descending order + const [events] = await retriedQuery({ + db, + query: `SHOW BINLOG EVENTS IN ? FROM ? LIMIT ?`, + params: [filename, pageIndex++ * page_size, page_size] + }); + + returnedCount = events.length; + const matchedEvent = events.find((e) => e['Event_type'] == 'Previous_gtids' && gtids.has(e['Info'])); + + if (matchedEvent) { + output.set(matchedEvent['Previous_gtids'], { + filename, + position: matchedEvent['Pos'] + }); + + if (output.size == gtids.size) { + return output; + } + } + } while (returnedCount > 0); + } + return output; +} diff --git a/modules/module-mysql/src/utils/replication/replication-utils.ts b/modules/module-mysql/src/utils/replication/replication-utils.ts index d9797a96e..b410750f2 100644 --- a/modules/module-mysql/src/utils/replication/replication-utils.ts +++ b/modules/module-mysql/src/utils/replication/replication-utils.ts @@ -1,2 +1,5 @@ export * from './configuration.js'; +export * from './gtid-compare.js'; +export * from './gtid-distance.js'; +export * from './gtid-position.js'; export * from './schema.js'; diff --git a/modules/module-postgres/src/replication/WalStreamManager.ts b/modules/module-postgres/src/replication/WalStreamManager.ts index d891dc028..b3848d1de 100644 --- a/modules/module-postgres/src/replication/WalStreamManager.ts +++ b/modules/module-postgres/src/replication/WalStreamManager.ts @@ -1,11 +1,11 @@ import { hrtime } from 'node:process'; -import * as pgwire from '@powersync/service-jpgwire'; -import { Replicator, SyncRulesProvider, DefaultErrorRateLimiter, storage } from '@powersync/service-core'; -import { WalStreamRunner } from './WalStreamRunner.js'; -import { PgManager } from './PgManager.js'; import { container, logger } from '@powersync/lib-services-framework'; +import { DefaultErrorRateLimiter, Replicator, storage, SyncRulesProvider } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; +import { PgManager } from './PgManager.js'; +import { WalStreamRunner } from './WalStreamRunner.js'; // 5 minutes const PING_INTERVAL = 1_000_000_000n * 300n; @@ -49,7 +49,7 @@ export class WalStreamManager implements Replicator { return this.options.connectionFactory; } - start() { + async start() { this.connectionManager = this.connectionFactory.create({ maxSize: 1, idleTimeout: 30000 }); this.runLoop().catch((e) => { logger.error(`Fatal WalStream error`, e); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5dc1c8c9e..bf61fda90 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -111,6 +111,9 @@ importers: '@powersync/service-types': specifier: workspace:* version: link:../../packages/types + '@vlasky/zongji': + specifier: ^0.5.9 + version: 0.5.9 mysql2: specifier: ^3.11.0 version: 3.11.0 @@ -1349,6 +1352,14 @@ packages: '@vitest/utils@0.34.6': resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vlasky/mysql@2.18.6': + resolution: {integrity: sha512-c+qz/zzqecteQLchoje0E0rjLla935d6hHPpMKmfyQJnHlycLpR49ekS6s/zUAt8w0Um5hFglKXm4+PeJTVhaQ==} + engines: {node: '>= 0.6'} + + '@vlasky/zongji@0.5.9': + resolution: {integrity: sha512-B4pz9a8QJw7hOOcfxtuT26PSVcQvGHlZcXG34T5jh/zjJZJ4/7pW7mioW6Cyxfl/mv/7Rcow+hVgfYpCTAjTMw==} + engines: {node: '>= 8.0'} + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1505,6 +1516,13 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + big-integer@1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + + bignumber.js@9.1.1: + resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -2975,6 +2993,9 @@ packages: readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + readable-stream@2.3.7: + resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -4771,6 +4792,19 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 + '@vlasky/mysql@2.18.6': + dependencies: + bignumber.js: 9.1.1 + readable-stream: 2.3.7 + safe-buffer: 5.2.1 + sqlstring: 2.3.3 + + '@vlasky/zongji@0.5.9': + dependencies: + '@vlasky/mysql': 2.18.6 + big-integer: 1.6.51 + iconv-lite: 0.6.3 + abbrev@1.1.1: {} abort-controller@3.0.0: @@ -4906,6 +4940,10 @@ snapshots: dependencies: is-windows: 1.0.2 + big-integer@1.6.51: {} + + bignumber.js@9.1.1: {} + binary-extensions@2.3.0: {} bl@4.1.0: @@ -6532,6 +6570,16 @@ snapshots: isarray: 0.0.1 string_decoder: 0.10.31 + readable-stream@2.3.7: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 From 78e4f23935c8ed6c9967bb22de6cdf4d134e94dc Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Mon, 19 Aug 2024 16:32:45 +0200 Subject: [PATCH 111/298] track binlog positions in replicated GTID identifier --- .../src/api/MySQLRouteAPIAdapter.ts | 16 +- .../src/utils/replication/GTID.ts | 143 ++++++++++++++++++ .../src/utils/replication/gtid-compare.ts | 31 ---- .../src/utils/replication/gtid-distance.ts | 64 -------- .../src/utils/replication/gtid-position.ts | 55 ------- .../src/utils/replication/head.ts | 26 ++++ .../utils/replication/replication-utils.ts | 6 +- 7 files changed, 177 insertions(+), 164 deletions(-) create mode 100644 modules/module-mysql/src/utils/replication/GTID.ts delete mode 100644 modules/module-mysql/src/utils/replication/gtid-compare.ts delete mode 100644 modules/module-mysql/src/utils/replication/gtid-distance.ts delete mode 100644 modules/module-mysql/src/utils/replication/gtid-position.ts create mode 100644 modules/module-mysql/src/utils/replication/head.ts diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index e9d76c112..7fc781ff7 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -245,17 +245,10 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { async getReplicationLag(options: api.ReplicationLagOptions): Promise { const { last_checkpoint_identifier } = options; - const [[currentHead]] = await this.retriedQuery({ - query: `SELECT @@GLOBAL.gtid_executed as GTID;` - }); - - const lag = await replication_utils.getGTIDDistance({ - db: this.pool, - // This is a comparable identifier, need the raw GTID - start_gtid: replication_utils.extractGTIDComparable(last_checkpoint_identifier), - end_gtid: currentHead.GTID - }); + const current = replication_utils.ReplicatedGTID.fromSerialized(last_checkpoint_identifier); + const head = await replication_utils.readMasterGtid(this.pool); + const lag = await current.distanceTo(this.pool, head); if (lag == null) { throw new Error(`Could not determine replication lag`); } @@ -264,7 +257,8 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async getReplicationHead(): Promise { - return replication_utils.readMasterComparableGtid(this.pool); + const result = await replication_utils.readMasterGtid(this.pool); + return result.comparable; } async getConnectionSchema(): Promise { diff --git a/modules/module-mysql/src/utils/replication/GTID.ts b/modules/module-mysql/src/utils/replication/GTID.ts new file mode 100644 index 000000000..1984abe61 --- /dev/null +++ b/modules/module-mysql/src/utils/replication/GTID.ts @@ -0,0 +1,143 @@ +import mysql from 'mysql2/promise'; +import { retriedQuery } from '../mysql_utils.js'; + +export type BinLogPosition = { + filename: string; + offset: number; +}; + +export type ReplicatedGTIDSpecification = { + raw_gtid: string; + /** + * The position of this GTID + */ + current_position?: BinLogPosition; + /** + * The position to start from for the next replication event. + */ + next_position?: BinLogPosition; +}; + +/** + * A wrapper around the MySQL GTID value. + * This adds and tracks additional metadata such as the BinLog filename + * and position where this GTID could be located. + */ +export class ReplicatedGTID { + static deserialize(comparable: string): ReplicatedGTIDSpecification { + const components = comparable.split('|'); + const currentPosition: BinLogPosition | undefined = components[2] + ? { + filename: components[2], + offset: parseInt(components[3]) + } + : undefined; + + const nextPosition: BinLogPosition | undefined = components[4] + ? { + filename: components[4], + offset: parseInt(components[5]) + } + : undefined; + return { + raw_gtid: components[1], + current_position: currentPosition, + next_position: nextPosition + }; + } + + static fromSerialized(comparable: string): ReplicatedGTID { + return new ReplicatedGTID(ReplicatedGTID.deserialize(comparable)); + } + + /** + * Special case for the zero GTID which means no transactions have been executed. + */ + static ZERO = new ReplicatedGTID({ raw_gtid: '0:0' }); + + constructor(protected options: ReplicatedGTIDSpecification) {} + + /** + * Get the BinLog position of this GTID event + */ + get currentPosition() { + return this.options.current_position; + } + + /** + * Get the BinLog position of the next replication event + */ + get nextPosition() { + return this.options.next_position; + } + + /** + * Get the raw Global Transaction ID. This of the format `server_id:transaction_ranges` + */ + get raw() { + return this.options.raw_gtid; + } + + /** + * Transforms a GTID into a comparable string format, ensuring lexicographical + * order aligns with the GTID's relative age. This assumes that all GTIDs + * have the same server ID. + * + * @param gtid - The GTID string in the format `server_id:transaction_ranges` + * @returns A comparable string in the format + * `padded_end_transaction|raw_gtid|current_binlog_filename|current_binlog_position|next_binlog_filename|next_binlog_position` + */ + get comparable() { + const { raw, currentPosition, nextPosition } = this; + const [, transactionRanges] = this.raw.split(':'); + + let maxTransactionId = 0; + + for (const range of transactionRanges.split(',')) { + const [start, end] = range.split('-'); + maxTransactionId = Math.max(maxTransactionId, parseInt(start, 10), parseInt(end || start, 10)); + } + + const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); + return [ + paddedTransactionId, + raw, + currentPosition?.filename ?? '', + currentPosition?.offset ?? '', + nextPosition?.filename ?? '', + nextPosition?.offset ?? '' + ].join('|'); + } + + /** + * Calculates the distance in bytes from this GTID to the provided argument. + */ + async distanceTo(db: mysql.Pool, to: ReplicatedGTID): Promise { + const [logFiles] = await retriedQuery({ + db, + query: `SHOW BINARY LOGS;` + }); + + // Default to the first file for the start to handle the zero GTID case. + const startFileIndex = Math.max( + logFiles.findIndex((f) => f['Log_name'] == this.currentPosition?.filename), + 0 + ); + const startFileEntry = logFiles[startFileIndex]; + + if (!startFileEntry) { + return null; + } + + // Default to the past the last file to cater for the HEAD case + const testEndFileIndex = logFiles.findIndex((f) => f['Log_name'] == to.currentPosition?.filename); + const endFileIndex = testEndFileIndex >= 0 ? testEndFileIndex : logFiles.length; + + return ( + startFileEntry['File_size'] - + (this?.currentPosition?.offset ?? 0) + + (to.currentPosition?.offset ?? 0) + + logFiles.slice(startFileIndex + 1, endFileIndex).reduce((sum, file) => sum + file['File_size'], 0) + ); + } +} diff --git a/modules/module-mysql/src/utils/replication/gtid-compare.ts b/modules/module-mysql/src/utils/replication/gtid-compare.ts deleted file mode 100644 index 432035cc7..000000000 --- a/modules/module-mysql/src/utils/replication/gtid-compare.ts +++ /dev/null @@ -1,31 +0,0 @@ -import mysql from 'mysql2/promise'; -/** - * Transforms a GTID into a comparable string format, ensuring lexicographical - * order aligns with the GTID's relative age. This assumes that all GTIDs - * have the same server ID. - * - * @param gtid - The GTID string in the format 'server_id:transaction_ranges' - * @returns A comparable string in the format 'padded_end_transaction|original_gtid' - */ -export function gtidMakeComparable(gtid: string): string { - const [serverId, transactionRanges] = gtid.split(':'); - - let maxTransactionId = 0; - - for (const range of transactionRanges.split(',')) { - const [start, end] = range.split('-'); - maxTransactionId = Math.max(maxTransactionId, parseInt(start, 10), parseInt(end || start, 10)); - } - - const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); - return `${paddedTransactionId}|${gtid}`; -} - -export function extractGTIDComparable(comparableGTID: string) { - return comparableGTID.split('|')[1]; -} - -export async function readMasterComparableGtid(db: mysql.Pool): Promise { - const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); - return gtidMakeComparable(gtidResult?.GTID ?? '0:0'); -} diff --git a/modules/module-mysql/src/utils/replication/gtid-distance.ts b/modules/module-mysql/src/utils/replication/gtid-distance.ts deleted file mode 100644 index 568e480c0..000000000 --- a/modules/module-mysql/src/utils/replication/gtid-distance.ts +++ /dev/null @@ -1,64 +0,0 @@ -import mysql from 'mysql2/promise'; -import { retriedQuery } from '../mysql_utils.js'; -import { getGTIDPositions } from './gtid-position.js'; - -export type GetGTIDDistanceOptions = { - db: mysql.Pool; - start_gtid: string; - end_gtid: string; - page_size?: number; -}; - -/** - * Get the distance in bytes between GTIDs - */ -export async function getGTIDDistance(options: GetGTIDDistanceOptions): Promise { - const { db, page_size = 1000, start_gtid, end_gtid } = options; - const [logFiles] = await retriedQuery({ - db, - query: `SHOW BINARY LOGS;` - }); - - const gtids = new Set(); - gtids.add(start_gtid); - gtids.add(end_gtid); - - const positions = await getGTIDPositions({ - db, - gtids, - page_size - }); - - const endResult = positions.get(end_gtid); - const startResult = positions.get(start_gtid); - - if (!endResult || !startResult) { - return null; - } - - if (endResult.filename == startResult.filename) { - return endResult.position - startResult.position; - } - - // The entries are on different files - const startFileIndex = logFiles.findIndex((f) => f['Log_name'] == startResult.filename); - const startFileEntry = logFiles[startFileIndex]; - - if (!startFileEntry) { - return null; - } - - const endFileIndex = logFiles.findIndex((f) => f['Log_name'] == endResult.filename); - const endFileEntry = logFiles[endFileIndex]; - - if (!endFileEntry) { - return null; - } - - return ( - startFileEntry['File_size'] - - startResult.position + - endResult.position + - logFiles.slice(startFileIndex + 1, endFileIndex).reduce((sum, file) => sum + file['File_size'], 0) - ); -} diff --git a/modules/module-mysql/src/utils/replication/gtid-position.ts b/modules/module-mysql/src/utils/replication/gtid-position.ts deleted file mode 100644 index 2429ee3b3..000000000 --- a/modules/module-mysql/src/utils/replication/gtid-position.ts +++ /dev/null @@ -1,55 +0,0 @@ -import mysql from 'mysql2/promise'; -import { retriedQuery } from '../mysql_utils.js'; - -export type GetGTIDPositionOptions = { - db: mysql.Pool; - gtids: Set; - page_size?: number; -}; - -export type GTIDPosition = { - filename: string; - position: number; -}; - -/** - * Given a set of GTID it will find the binlog files and offset positions. - */ -export async function getGTIDPositions(options: GetGTIDPositionOptions): Promise> { - const { db, gtids, page_size = 1000 } = options; - const [binLogFileResults] = await retriedQuery({ - db, - query: `SHOW BINARY LOGS` - }); - - const binLogFilenames: string[] = binLogFileResults.map((r) => r.Log_name); - const output: Map = new Map(); - - for (const filename of binLogFilenames.reverse()) { - let returnedCount = 0; - let pageIndex = 0; - do { - // Unfortunately this cannot be sorted in descending order - const [events] = await retriedQuery({ - db, - query: `SHOW BINLOG EVENTS IN ? FROM ? LIMIT ?`, - params: [filename, pageIndex++ * page_size, page_size] - }); - - returnedCount = events.length; - const matchedEvent = events.find((e) => e['Event_type'] == 'Previous_gtids' && gtids.has(e['Info'])); - - if (matchedEvent) { - output.set(matchedEvent['Previous_gtids'], { - filename, - position: matchedEvent['Pos'] - }); - - if (output.size == gtids.size) { - return output; - } - } - } while (returnedCount > 0); - } - return output; -} diff --git a/modules/module-mysql/src/utils/replication/head.ts b/modules/module-mysql/src/utils/replication/head.ts new file mode 100644 index 000000000..895534e05 --- /dev/null +++ b/modules/module-mysql/src/utils/replication/head.ts @@ -0,0 +1,26 @@ +import mysql from 'mysql2/promise'; +import { retriedQuery } from '../mysql_utils.js'; +import { ReplicatedGTID } from './GTID.js'; + +/** + * Gets the current master HEAD GTID + */ +export async function readMasterGtid(db: mysql.Pool): Promise { + // Get the GTID + const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); + + // Get the BinLog position if master + const [[masterResult]] = await retriedQuery({ + db, + query: `SHOW master STATUS` + }); + const position = { + filename: masterResult.File, + offset: parseInt(masterResult.Position) + }; + + return new ReplicatedGTID({ + next_position: position, + raw_gtid: gtidResult.GTID + }); +} diff --git a/modules/module-mysql/src/utils/replication/replication-utils.ts b/modules/module-mysql/src/utils/replication/replication-utils.ts index b410750f2..f29b6f1fc 100644 --- a/modules/module-mysql/src/utils/replication/replication-utils.ts +++ b/modules/module-mysql/src/utils/replication/replication-utils.ts @@ -1,5 +1,5 @@ export * from './configuration.js'; -export * from './gtid-compare.js'; -export * from './gtid-distance.js'; -export * from './gtid-position.js'; +export * from './GTID.js'; +export * from './head.js'; + export * from './schema.js'; From 7ef26f4106fc826731d6d66d78412ae974524ae4 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 20 Aug 2024 13:45:59 +0200 Subject: [PATCH 112/298] Introduces the AbstractReplicator concept as a more flexible alternative to the ReplicationAdapter --- .../src/replication/PgManager.ts | 4 + .../src/replication/WalStreamManager.ts | 230 ----------------- .../src/replication/WalStreamRunner.ts | 179 -------------- .../src/replication/replication-index.ts | 4 +- .../core => modules}/ReplicationModule.ts | 12 +- .../src/replication/AbstractReplicationJob.ts | 97 ++++++++ ...bleReplicator.ts => AbstractReplicator.ts} | 131 ++++++---- .../{core => }/ReplicationEngine.ts | 16 +- .../replication/core/ReplicationAdapter.ts | 166 ------------- .../src/replication/core/ReplicationEntity.ts | 58 ----- .../src/replication/core/ReplicationJob.ts | 232 ------------------ .../src/replication/core/Replicator.ts | 13 - .../src/replication/core/replication-utils.ts | 8 - .../src/replication/replication-index.ts | 12 +- 14 files changed, 210 insertions(+), 952 deletions(-) delete mode 100644 modules/module-postgres/src/replication/WalStreamManager.ts delete mode 100644 modules/module-postgres/src/replication/WalStreamRunner.ts rename packages/service-core/src/{replication/core => modules}/ReplicationModule.ts (90%) create mode 100644 packages/service-core/src/replication/AbstractReplicationJob.ts rename packages/service-core/src/replication/{core/PluggableReplicator.ts => AbstractReplicator.ts} (51%) rename packages/service-core/src/replication/{core => }/ReplicationEngine.ts (66%) delete mode 100644 packages/service-core/src/replication/core/ReplicationAdapter.ts delete mode 100644 packages/service-core/src/replication/core/ReplicationEntity.ts delete mode 100644 packages/service-core/src/replication/core/ReplicationJob.ts delete mode 100644 packages/service-core/src/replication/core/Replicator.ts delete mode 100644 packages/service-core/src/replication/core/replication-utils.ts diff --git a/modules/module-postgres/src/replication/PgManager.ts b/modules/module-postgres/src/replication/PgManager.ts index d611fe851..41b064110 100644 --- a/modules/module-postgres/src/replication/PgManager.ts +++ b/modules/module-postgres/src/replication/PgManager.ts @@ -14,6 +14,10 @@ export class PgManager { this.pool = pgwire.connectPgWirePool(this.options, poolOptions); } + public get connectionTag() { + return this.options.tag; + } + /** * Create a new replication connection. */ diff --git a/modules/module-postgres/src/replication/WalStreamManager.ts b/modules/module-postgres/src/replication/WalStreamManager.ts deleted file mode 100644 index d891dc028..000000000 --- a/modules/module-postgres/src/replication/WalStreamManager.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { hrtime } from 'node:process'; - -import * as pgwire from '@powersync/service-jpgwire'; -import { Replicator, SyncRulesProvider, DefaultErrorRateLimiter, storage } from '@powersync/service-core'; -import { WalStreamRunner } from './WalStreamRunner.js'; -import { PgManager } from './PgManager.js'; -import { container, logger } from '@powersync/lib-services-framework'; -import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; - -// 5 minutes -const PING_INTERVAL = 1_000_000_000n * 300n; - -export interface WalStreamManagerOptions { - id: string; - storageFactory: storage.StorageFactoryProvider; - syncRuleProvider: SyncRulesProvider; - connectionFactory: ConnectionManagerFactory; -} - -export class WalStreamManager implements Replicator { - private streams = new Map(); - private connectionManager: PgManager | null = null; - - private stopped = false; - - // First ping is only after 5 minutes, not when starting - private lastPing = hrtime.bigint(); - - /** - * This limits the effect of retries when there is a persistent issue. - */ - private rateLimiter = new DefaultErrorRateLimiter(); - - constructor(private options: WalStreamManagerOptions) {} - - public get id() { - return this.options.id; - } - - private get storage() { - return this.options.storageFactory.bucketStorage; - } - - private get syncRuleProvider() { - return this.options.syncRuleProvider; - } - - private get connectionFactory() { - return this.options.connectionFactory; - } - - start() { - this.connectionManager = this.connectionFactory.create({ maxSize: 1, idleTimeout: 30000 }); - this.runLoop().catch((e) => { - logger.error(`Fatal WalStream error`, e); - container.reporter.captureException(e); - setTimeout(() => { - process.exit(1); - }, 1000); - }); - } - - async stop() { - this.stopped = true; - let promises: Promise[] = []; - for (let stream of this.streams.values()) { - promises.push(stream.stop()); - } - promises.push(this.connectionManager!.end()); - await Promise.all(promises); - } - - private async runLoop() { - const configured_sync_rules = await this.syncRuleProvider.get(); - let configured_lock: storage.ReplicationLock | undefined = undefined; - if (configured_sync_rules != null) { - logger.info('Loading sync rules from configuration'); - try { - // Configure new sync rules, if it has changed. - // In that case, also immediately take out a lock, so that another process doesn't start replication on it. - const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { - lock: true - }); - if (lock) { - configured_lock = lock; - } - } catch (e) { - // Log, but continue with previous sync rules - logger.error(`Failed to load sync rules from configuration`, e); - } - } else { - logger.info('No sync rules configured - configure via API'); - } - while (!this.stopped) { - await container.probes.touch(); - try { - const pool = this.connectionManager?.pool; - if (pool) { - await this.refresh({ configured_lock }); - // The lock is only valid on the first refresh. - configured_lock = undefined; - - // TODO: Ping on all connections when we have multiple - // Perhaps WalStreamRunner would be a better place to do pings? - // We don't ping while in error retry back-off, to avoid having too - // many authentication failures. - if (this.rateLimiter.mayPing()) { - await this.ping(pool); - } - } - } catch (e) { - logger.error(`Failed to refresh wal streams`, e); - } - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - - /** - * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. - * - * The old WAL files are only deleted once no replication slot still references it. - * - * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot - * advancing**. - * - * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. - * - * **This may be a bug in pgwire or how we're using it. - */ - private async ping(db: pgwire.PgClient) { - const now = hrtime.bigint(); - if (now - this.lastPing >= PING_INTERVAL) { - try { - await db.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`); - } catch (e) { - logger.warn(`Failed to ping`, e); - } - this.lastPing = now; - } - } - - private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { - if (this.stopped) { - return; - } - - let configured_lock = options?.configured_lock; - - const existingStreams = new Map(this.streams.entries()); - const replicating = await this.storage.getReplicatingSyncRules(); - const newStreams = new Map(); - for (let syncRules of replicating) { - const existing = existingStreams.get(syncRules.id); - if (existing && !existing.stopped) { - // No change - existingStreams.delete(syncRules.id); - newStreams.set(syncRules.id, existing); - } else if (existing && existing.stopped) { - // Stopped (e.g. fatal error, slot rename). - // Remove from the list. Next refresh call will restart the stream. - existingStreams.delete(syncRules.id); - } else { - // New (or resume after restart) - try { - let lock: storage.ReplicationLock; - if (configured_lock?.sync_rules_id == syncRules.id) { - lock = configured_lock; - } else { - lock = await syncRules.lock(); - } - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const stream = new WalStreamRunner({ - factory: this.storage, - storage: storage, - connectionFactory: this.connectionFactory, - lock, - rateLimiter: this.rateLimiter - }); - newStreams.set(syncRules.id, stream); - stream.start(); - } catch (e) { - // Could be a sync rules parse error, - // for example from stricter validation that was added. - // This will be retried every couple of seconds. - // When new (valid) sync rules are deployed and processed, this one be disabled. - logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); - } - } - } - - this.streams = newStreams; - - // TODO: Should this termination be happening in the "background" instead? - // That becomes tricky to manage - - for (let stream of existingStreams.values()) { - // Old - stop and remove. - try { - await stream.terminate(); - } catch (e) { - // This will be retried - logger.warn(`Failed to terminate ${stream.slot_name}`, e); - } - } - - // Sync rules stopped previously or by a different process. - const stopped = await this.storage.getStoppedSyncRules(); - for (let syncRules of stopped) { - try { - const lock = await syncRules.lock(); - try { - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const stream = new WalStreamRunner({ - factory: this.storage, - storage: storage, - connectionFactory: this.connectionFactory, - lock - }); - await stream.terminate(); - } finally { - await lock.release(); - } - } catch (e) { - logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); - } - } - } -} diff --git a/modules/module-postgres/src/replication/WalStreamRunner.ts b/modules/module-postgres/src/replication/WalStreamRunner.ts deleted file mode 100644 index f462567c8..000000000 --- a/modules/module-postgres/src/replication/WalStreamRunner.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { MissingReplicationSlotError, WalStream } from './WalStream.js'; -import { container, logger } from '@powersync/lib-services-framework'; -import { PgManager } from './PgManager.js'; -import { ErrorRateLimiter, storage } from '@powersync/service-core'; -import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; - -export interface WalStreamRunnerOptions { - factory: storage.BucketStorageFactory; - storage: storage.SyncRulesBucketStorage; - connectionFactory: ConnectionManagerFactory; - lock: storage.ReplicationLock; - rateLimiter?: ErrorRateLimiter; -} - -export class WalStreamRunner { - private abortController = new AbortController(); - - private runPromise?: Promise; - - private connectionManager: PgManager | null = null; - - private rateLimiter?: ErrorRateLimiter; - - constructor(public options: WalStreamRunnerOptions) { - this.rateLimiter = options.rateLimiter; - } - - start() { - this.runPromise = this.run(); - } - - get slot_name() { - return this.options.storage.slot_name; - } - - get stopped() { - return this.abortController.signal.aborted; - } - - private get connectionFactory() { - return this.options.connectionFactory; - } - - async run() { - try { - await this.replicateLoop(); - } catch (e) { - // Fatal exception - container.reporter.captureException(e, { - metadata: { - replication_slot: this.slot_name - } - }); - logger.error(`Replication failed on ${this.slot_name}`, e); - - if (e instanceof MissingReplicationSlotError) { - // This stops replication on this slot, and creates a new slot - await this.options.storage.factory.slotRemoved(this.slot_name); - } - } finally { - this.abortController.abort(); - } - await this.options.lock.release(); - } - - async replicateLoop() { - while (!this.stopped) { - await this.replicateOnce(); - - if (!this.stopped) { - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - } - - async replicateOnce() { - // New connections on every iteration (every error with retry), - // otherwise we risk repeating errors related to the connection, - // such as caused by cached PG schemas. - this.connectionManager = this.connectionFactory.create({ - // Pool connections are only used intermittently. - idleTimeout: 30_000, - maxSize: 2 - }); - try { - await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); - if (this.stopped) { - return; - } - const stream = new WalStream({ - abort_signal: this.abortController.signal, - factory: this.options.factory, - storage: this.options.storage, - connections: this.connectionManager - }); - await stream.replicate(); - } catch (e) { - logger.error(`Replication error`, e); - if (e.cause != null) { - // Example: - // PgError.conn_ended: Unable to do postgres query on ended connection - // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) - // at stream.next () - // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) - // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) - // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) - // ... - // cause: TypeError: match is not iterable - // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) - // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) - // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) - // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) - // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) - // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) - // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), - // [Symbol(pg.ErrorCode)]: 'conn_ended', - // [Symbol(pg.ErrorResponse)]: undefined - // } - // Without this additional log, the cause would not be visible in the logs. - logger.error(`cause`, e.cause); - } - if (e instanceof MissingReplicationSlotError) { - throw e; - } else { - // Report the error if relevant, before retrying - container.reporter.captureException(e, { - metadata: { - replication_slot: this.slot_name - } - }); - // This sets the retry delay - this.rateLimiter?.reportError(e); - } - } finally { - if (this.connectionManager) { - await this.connectionManager.end(); - } - this.connectionManager = null; - } - } - - /** - * This will also release the lock if start() was called earlier. - */ - async stop(options?: { force?: boolean }) { - logger.info(`${this.slot_name} Stopping replication`); - // End gracefully - this.abortController.abort(); - - if (options?.force) { - // destroy() is more forceful. - await this.connectionManager?.destroy(); - } - await this.runPromise; - } - - /** - * Terminate this replication stream. This drops the replication slot and deletes the replication data. - * - * Stops replication if needed. - */ - async terminate(options?: { force?: boolean }) { - logger.info(`${this.slot_name} Terminating replication`); - await this.stop(options); - - const slotName = this.slot_name; - const connectionManager = this.connectionFactory.create({ maxSize: 1, idleTimeout: 30_000 }); - try { - await connectionManager.pool.query({ - statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', - params: [{ type: 'varchar', value: slotName }] - }); - } finally { - await connectionManager.end(); - } - - await this.options.storage.terminate(); - } -} diff --git a/modules/module-postgres/src/replication/replication-index.ts b/modules/module-postgres/src/replication/replication-index.ts index 150898d72..3782a2da2 100644 --- a/modules/module-postgres/src/replication/replication-index.ts +++ b/modules/module-postgres/src/replication/replication-index.ts @@ -2,5 +2,5 @@ export * from './PgRelation.js'; export * from './replication-utils.js'; export * from './WalConnection.js'; export * from './WalStream.js'; -export * from './WalStreamManager.js'; -export * from './WalStreamRunner.js'; +export * from './WalStreamReplicator.js'; +export * from './WalStreamReplicationJob.js'; diff --git a/packages/service-core/src/replication/core/ReplicationModule.ts b/packages/service-core/src/modules/ReplicationModule.ts similarity index 90% rename from packages/service-core/src/replication/core/ReplicationModule.ts rename to packages/service-core/src/modules/ReplicationModule.ts index 47d7d095b..335ff38cf 100644 --- a/packages/service-core/src/replication/core/ReplicationModule.ts +++ b/packages/service-core/src/modules/ReplicationModule.ts @@ -2,12 +2,12 @@ import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSync import * as t from 'ts-codec'; import * as types from '@powersync/service-types'; -import * as api from '../../api/api-index.js'; -import * as modules from '../../modules/modules-index.js'; -import * as system from '../../system/system-index.js'; -import { ReplicationAdapter } from './ReplicationAdapter.js'; +import * as api from '../api/api-index.js'; +import * as modules from './modules-index.js'; +import * as system from '../system/system-index.js'; +import { ReplicationAdapter } from '../replication/ReplicationAdapter.js'; import { logger, schema } from '@powersync/lib-services-framework'; -import { Replicator } from './Replicator.js'; +import { AbstractReplicator } from '../replication/AbstractReplicator.js'; export interface ReplicationModuleOptions extends modules.AbstractModuleOptions { type: string; @@ -44,7 +44,7 @@ export abstract class ReplicationModule extend /** * Create the ReplicationAdapter to be used by PowerSync replicator. */ - protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): Replicator; + protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): AbstractReplicator; /** * Register this module's replication adapters and sync API providers if the required configuration is present. diff --git a/packages/service-core/src/replication/AbstractReplicationJob.ts b/packages/service-core/src/replication/AbstractReplicationJob.ts new file mode 100644 index 000000000..a049e4825 --- /dev/null +++ b/packages/service-core/src/replication/AbstractReplicationJob.ts @@ -0,0 +1,97 @@ +import * as storage from '../storage/storage-index.js'; +import { ErrorRateLimiter } from './ErrorRateLimiter.js'; +import { container, logger } from '@powersync/lib-services-framework'; +import winston from 'winston'; + +export interface AbstractReplicationJobOptions { + id: string; + storage: storage.SyncRulesBucketStorage; + lock: storage.ReplicationLock; + rateLimiter?: ErrorRateLimiter; +} + +export abstract class AbstractReplicationJob { + protected logger: winston.Logger; + protected abortController = new AbortController(); + protected isReplicatingPromise: Promise | null = null; + + protected constructor(protected options: AbstractReplicationJobOptions) { + this.logger = logger.child({ name: `ReplicationJob: ${options.id}` }); + } + + abstract replicate(): Promise; + + /** + * Ensure the connection to the data source remains active + */ + abstract keepAlive(): Promise; + + /** + * Clean up any configuration or state for this replication on the datasource. + * This assumes that the replication is not currently active. + */ + abstract cleanUp(): Promise; + + /** + * Start the replication process + */ + public async start(): Promise { + try { + this.isReplicatingPromise = this.replicate(); + } catch (e) { + // TODO handle catch + // Fatal exception + container.reporter.captureException(e, { + metadata: { + replicator: this.id + // TODO We could allow extra metadata contributed from the adapter here + } + }); + logger.error(`Replication failed.`, e); + } finally { + this.abortController.abort(); + } + await this.options.lock.release(); + } + + /** + * Safely stop the replication process + */ + public async stop(): Promise { + logger.info(`Stopping ${this.id} replication job for sync rule iteration: ${this.storage.group_id}`); + this.abortController.abort(); + // TODO don't worry about exceptions, stopping is the important part + await this.isReplicatingPromise; + } + + /** + * Stop the replication if it is still running. + * Clean up any config on the datasource related to this replication job + */ + public async terminate(): Promise { + logger.info(`${this.id} Terminating replication`); + await this.stop(); + await this.cleanUp(); + await this.options.storage.terminate(); + } + + public get id() { + return this.options.id; + } + + protected get storage() { + return this.options.storage; + } + + protected get lock() { + return this.options.lock; + } + + protected get rateLimiter() { + return this.options.rateLimiter; + } + + public get isStopped(): boolean { + return this.abortController.signal.aborted; + } +} diff --git a/packages/service-core/src/replication/core/PluggableReplicator.ts b/packages/service-core/src/replication/AbstractReplicator.ts similarity index 51% rename from packages/service-core/src/replication/core/PluggableReplicator.ts rename to packages/service-core/src/replication/AbstractReplicator.ts index b4b924a9e..4f15c5e63 100644 --- a/packages/service-core/src/replication/core/PluggableReplicator.ts +++ b/packages/service-core/src/replication/AbstractReplicator.ts @@ -1,45 +1,75 @@ -import * as storage from '../../storage/storage-index.js'; +import { hrtime } from 'node:process'; +import * as storage from '../storage/storage-index.js'; -import { ReplicationAdapterFactory } from './ReplicationAdapter.js'; -import { BucketStorageFactory } from '../../storage/BucketStorage.js'; import { container, logger } from '@powersync/lib-services-framework'; -import { SyncRulesProvider } from '../../util/config/sync-rules/sync-rules-provider.js'; -import { ReplicationJob } from './ReplicationJob.js'; -import { Replicator } from './Replicator.js'; +import { SyncRulesProvider } from '../util/config/sync-rules/sync-rules-provider.js'; +import winston from 'winston'; +import { AbstractReplicationJob } from './AbstractReplicationJob.js'; +import { StorageFactoryProvider } from '../storage/storage-index.js'; +import { DefaultErrorRateLimiter, ErrorRateLimiter } from './ErrorRateLimiter.js'; + +// 5 minutes +const PING_INTERVAL = 1_000_000_000n * 300n; + +export interface CreateJobOptions { + lock: storage.ReplicationLock; + storage: storage.SyncRulesBucketStorage; +} -export interface PluggableReplicatorOptions { +export interface AbstractReplicatorOptions { id: string; - adapterFactory: ReplicationAdapterFactory; - storage: BucketStorageFactory; + storageFactory: StorageFactoryProvider; syncRuleProvider: SyncRulesProvider; } /** * A replicator manages the mechanics for replicating data from a data source to a storage bucket. - * This includes copying across the original data set and then keeping it in sync with the data source using ReplicationJobs. + * This includes copying across the original data set and then keeping it in sync with the data source using Replication Jobs. * It also handles any changes to the sync rules. */ -export class PluggableReplicator implements Replicator { - private replicationJobs = new Map(); +export abstract class AbstractReplicator { + protected logger: winston.Logger; + /** + * Map of replication jobs by sync rule id. Usually there is only one running job, but there could be two when + * transitioning to a new set of sync rules. + * @private + */ + private replicationJobs = new Map(); private stopped = false; - constructor(private options: PluggableReplicatorOptions) {} + // First ping is only after 5 minutes, not when starting + private lastPing = hrtime.bigint(); + + /** + * TODO: Remove Postgres coupling of the DefaultLimitter + * This limits the effect of retries when there is a persistent issue. + */ + private rateLimiter: ErrorRateLimiter; + + protected constructor(private options: AbstractReplicatorOptions) { + this.logger = logger.child({ name: `Replicator:${options.id}` }); + this.rateLimiter = this.createRateLimiter(); + } + + abstract createJob(options: CreateJobOptions): T; + + abstract createRateLimiter(): ErrorRateLimiter; public get id() { return this.options.id; } - private get adapterFactory() { - return this.options.adapterFactory; + protected get storage() { + return this.options.storageFactory.bucketStorage; } - private get storage() { - return this.options.storage; + protected get syncRuleProvider() { + return this.options.syncRuleProvider; } public async start(): Promise { this.runLoop().catch((e) => { - logger.error(`Data source fatal replication error: ${this.adapterFactory.name}`, e); + this.logger.error('Data source fatal replication error', e); container.reporter.captureException(e); setTimeout(() => { process.exit(1); @@ -57,10 +87,10 @@ export class PluggableReplicator implements Replicator { } private async runLoop() { - const syncRules = await this.options.syncRuleProvider.get(); + const syncRules = await this.syncRuleProvider.get(); let configuredLock: storage.ReplicationLock | undefined = undefined; if (syncRules != null) { - logger.info('Loaded sync rules'); + this.logger.info('Loaded sync rules'); try { // Configure new sync rules, if they have changed. // In that case, also immediately take out a lock, so that another process doesn't start replication on it. @@ -72,10 +102,10 @@ export class PluggableReplicator implements Replicator { } } catch (e) { // Log, but continue with previous sync rules - logger.error(`Failed to update sync rules from configuration`, e); + this.logger.error(`Failed to update sync rules from configuration`, e); } } else { - logger.info('No sync rules configured - configure via API'); + this.logger.info('No sync rules configured - configure via API'); } while (!this.stopped) { await container.probes.touch(); @@ -83,8 +113,21 @@ export class PluggableReplicator implements Replicator { await this.refresh({ configured_lock: configuredLock }); // The lock is only valid on the first refresh. configuredLock = undefined; + + // Ensure that the replication jobs' connections are kept alive. + // We don't ping while in error retry back-off, to avoid having too failures. + if (this.rateLimiter.mayPing()) { + const now = hrtime.bigint(); + if (now - this.lastPing >= PING_INTERVAL) { + for (const activeJob of this.replicationJobs.values()) { + await activeJob.keepAlive(); + } + + this.lastPing = now; + } + } } catch (e) { - logger.error(`Failed to refresh replication jobs: ${this.adapterFactory.name}`, e); + this.logger.error('Failed to refresh replication jobs', e); } await new Promise((resolve) => setTimeout(resolve, 5000)); } @@ -97,16 +140,16 @@ export class PluggableReplicator implements Replicator { let configuredLock = options?.configured_lock; - const existingJobs = new Map(this.replicationJobs.entries()); + const existingJobs = new Map(this.replicationJobs.entries()); const replicatingSyncRules = await this.storage.getReplicatingSyncRules(); - const newJobs = new Map(); + const newJobs = new Map(); for (let syncRules of replicatingSyncRules) { const existingJob = existingJobs.get(syncRules.id); - if (existingJob && !existingJob.isStopped()) { + if (existingJob && !existingJob.isStopped) { // No change existingJobs.delete(syncRules.id); newJobs.set(syncRules.id, existingJob); - } else if (existingJob && existingJob.isStopped()) { + } else if (existingJob && existingJob.isStopped) { // Stopped (e.g. fatal error). // Remove from the list. Next refresh call will restart the job. existingJobs.delete(syncRules.id); @@ -121,13 +164,11 @@ export class PluggableReplicator implements Replicator { } const parsed = syncRules.parsed(); const storage = this.storage.getInstance(parsed); - const newJob = new ReplicationJob({ - adapter: this.adapterFactory.create({ - syncRuleId: syncRules.id.toString() - }), - storage: storage, - lock: lock + const newJob = this.createJob({ + lock: lock, + storage: storage }); + newJobs.set(syncRules.id, newJob); newJob.start(); } catch (e) { @@ -135,7 +176,7 @@ export class PluggableReplicator implements Replicator { // for example from stricter validation that was added. // This will be retried every couple of seconds. // When new (valid) sync rules are deployed and processed, this one be disabled. - logger.error(`Failed to start replication for ${this.adapterFactory.name} with new sync rules`, e); + this.logger.error('Failed to start replication for with new sync rules', e); } } } @@ -149,7 +190,7 @@ export class PluggableReplicator implements Replicator { await job.terminate(); } catch (e) { // This will be retried - logger.warn(`Failed to terminate old ${this.adapterFactory.name} replication job}`, e); + this.logger.warn('Failed to terminate old replication job}', e); } } @@ -158,21 +199,25 @@ export class PluggableReplicator implements Replicator { for (let syncRules of stopped) { try { const lock = await syncRules.lock(); + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); try { - const tempAdapter = this.adapterFactory.create({ - syncRuleId: syncRules.id.toString() + // TODO: See if this should maybe just be a separate type of job + const terminationJob = this.createJob({ + lock: lock, + storage: storage }); - - await tempAdapter.cleanupReplication(); - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - await storage.terminate(); + await terminationJob.terminate(); } finally { await lock.release(); } } catch (e) { - logger.warn(`Failed to terminate ${syncRules.id}`, e); + this.logger.warn(`Failed clean up replication config for sync rule: ${syncRules.id}`, e); } } } + + protected createJobId(syncRuleId: number) { + return `${this.id}-${syncRuleId}`; + } } diff --git a/packages/service-core/src/replication/core/ReplicationEngine.ts b/packages/service-core/src/replication/ReplicationEngine.ts similarity index 66% rename from packages/service-core/src/replication/core/ReplicationEngine.ts rename to packages/service-core/src/replication/ReplicationEngine.ts index 0f7478ff9..146168221 100644 --- a/packages/service-core/src/replication/core/ReplicationEngine.ts +++ b/packages/service-core/src/replication/ReplicationEngine.ts @@ -1,8 +1,6 @@ -import { ReplicationAdapter } from './ReplicationAdapter.js'; -import { Replicator } from './Replicator.js'; - -import * as storage from '../../storage/storage-index.js'; -import * as utils from '.././../util/util-index.js'; +import * as storage from '../storage/storage-index.js'; +import * as utils from '../util/util-index.js'; +import { AbstractReplicator } from './AbstractReplicator.js'; export interface ReplicationEngineOptions { storage: storage.StorageFactoryProvider; @@ -11,7 +9,7 @@ export interface ReplicationEngineOptions { export class ReplicationEngine { private readonly options: ReplicationEngineOptions; - private readonly replicators: Map = new Map(); + private readonly replicators: Map = new Map(); constructor(options: ReplicationEngineOptions) { this.options = options; @@ -22,9 +20,9 @@ export class ReplicationEngine { * * @param replicator */ - public register(replicator: Replicator) { + public register(replicator: AbstractReplicator) { if (this.replicators.has(replicator.id)) { - throw new Error(`Replicator for type ${replicator.id} already registered`); + throw new Error(`Replicator with id: ${replicator.id} already registered`); } this.replicators.set(replicator.id, replicator); } @@ -32,7 +30,7 @@ export class ReplicationEngine { /** * Start replication on all managed Replicators */ - public async start(): Promise { + public start(): void { for (const replicator of this.replicators.values()) { replicator.start(); } diff --git a/packages/service-core/src/replication/core/ReplicationAdapter.ts b/packages/service-core/src/replication/core/ReplicationAdapter.ts deleted file mode 100644 index a3c2d0d5b..000000000 --- a/packages/service-core/src/replication/core/ReplicationAdapter.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { SqliteRow, TablePattern } from '@powersync/service-sync-rules'; -import * as storage from '../../storage/storage-index.js'; -import { SourceEntityDescriptor } from '../../storage/SourceEntity.js'; - -/** - * Any additional configuration required for the ReplicationAdapters that is only available - * at runtime. - */ -export interface RuntimeConfiguration { - /** - * The unique identifier for the revision of the SyncRules that will be used for replication - */ - syncRuleId: string; -} - -export interface ReplicationAdapterFactory { - /** - * Unique name to identify the adapters in the PowerSync system for this datasource - * Suggestion: datasource type + datasource name ie. postgres-prod1 - */ - name: string; - create(configuration: RuntimeConfiguration): ReplicationAdapter; -} - -/** - * The ReplicationAdapter describes all the methods that are required by the - * PluggableReplicator to replicate data from a datasource into the PowerSync Bucket storage - */ -export interface ReplicationAdapter { - /** - * Unique name for the adapter, will usually be provided by the ReplicationAdapterFactory implementation, - * but can be overridden if necessary. - * When provided, name takes the form of ReplicationAdapterFactory.name + sync rule identifier. - */ - name: string; - - /** - * Confirm that the required configuration for replication on the datasource is in place. - * If it isn't, attempt to create it. - * If any configuration is missing or could not be created, an error should be thrown with the details. - */ - ensureConfiguration(options: EnsureConfigurationOptions): Promise; - - /** - * Get all the fully qualified entities that match the provided pattern - * @param pattern // TODO: Need something more generic than TablePattern - */ - resolveReplicationEntities(pattern: TablePattern): Promise; - - /** - * Get the number of entries for this Entity. - * Return -1n if unknown. - * - * @param entity - */ - count(entity: storage.SourceTable): Promise; - - /** - * Retrieve the initial snapshot data for the entity. Results should be passed onto the provided entryConsumer in batches. - * The snapshot should happen in an isolated transaction. Returns with the LSN from when the snapshot was started, when the operation is finished. - * This LSN will be used as the starting point for the replication stream. - * @param options - */ - initializeData(options: InitializeDataOptions): Promise; - - /** - * Start replicating data, assumes that initializeData has already finished running - * Stream any changes back on the provided changeListener - * @param options - */ - startReplication(options: StartReplicationOptions): Promise; - - /** - * Clean up any configuration or state for the replication with the given identifier on the datasource. - * This assumes that the replication is not currently active. - */ - cleanupReplication(): Promise; - - /** - * Return the LSN descriptor for this data source. - */ - lsnDescriptor(): LSNDescriptor; -} - -export interface EnsureConfigurationOptions { - abortSignal: AbortSignal; -} - -export interface InitializeDataBatch { - entries: SqliteRow[]; - entity: storage.SourceTable; - isLast: boolean; - fromLSN: string; -} - -export interface InitializeDataOptions { - entities: storage.SourceTable[]; - entryConsumer: (batch: InitializeDataBatch) => {}; - abortSignal: AbortSignal; -} - -export interface StartReplicationOptions { - entities: storage.SourceTable[]; - changeListener: (change: ReplicationMessage) => {}; - abortSignal: AbortSignal; -} - -export enum ReplicationMessageType { - CRUD = 'CRUD', - TRUNCATE = 'TRUNCATE', - SCHEMA_CHANGE = 'SCHEMA_CHANGE', - COMMIT = 'COMMIT', - KEEP_ALIVE = 'KEEP_ALIVE' -} - -export interface ReplicationMessage { - type: ReplicationMessageType; - payload: CrudOperation | SchemaUpdate | TruncateRequest | LSNUpdate; -} - -export type CrudOperation = { - /** - * The entity for which the change occurred - */ - entity: storage.SourceTable; - /** - * Description of the change that happened to the entry - */ - entry: storage.SaveOptions; -}; - -export type SchemaUpdate = { - /** - * Describes the new schema - */ - entityDescriptor: SourceEntityDescriptor; - // TODO: This shouldn't have to be here - connectionTag: string; -}; - -export type TruncateRequest = { - /** - * The entities that should be truncated - */ - entities: storage.SourceTable[]; -}; - -export type LSNUpdate = { - lsn: string; -}; - -/** - * Describes the LSN format for a data source, including a way to compare them. - */ -export interface LSNDescriptor { - /** - * The zero LSN for this data source where transactions start from. - */ - zeroLsn: string; - /** - * Compare two LSNs in this data source's format and return a number indicating their order. - * @param lsnA - * @param lsnB - */ - comparator: (lsnA: string, lsnB: string) => number; -} diff --git a/packages/service-core/src/replication/core/ReplicationEntity.ts b/packages/service-core/src/replication/core/ReplicationEntity.ts deleted file mode 100644 index 3ad13c818..000000000 --- a/packages/service-core/src/replication/core/ReplicationEntity.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { ColumnDescriptor } from '../../storage/SourceEntity.js'; - -/** - * Describes a replication entity, which is a logical representation of a table or collection in a data source. - */ -export interface ReplicationEntityDescriptor { - name: string; - - /** - * Fully qualified name of the entity, including any schema/namespace prefixes - */ - fullyQualifiedName: string; - - /** - * Identifier to uniquely identify the entity in PowerSync - */ - id: any; - /** - * The entity's native identifier in the data source (oid) - */ - dataSourceEntityId: string; - /** - * The field(s) that uniquely identify an entry/row in this replication entity - */ - primaryIdentifierFields: ColumnDescriptor[]; - - // Add any additional properties specific to the entity here - additionalProperties: Record; -} - -export class ReplicationEntity { - /** - * Defaults to true for tests. - */ - public syncData: boolean = true; - - /** - * Defaults to true for tests. - */ - public syncParameters: boolean = true; - - /** - * Indicates whether the snapshot of the entity has already been completed - */ - public snapshotComplete: boolean = false; - - public snapshotLSN: string | null = null; - - constructor(public descriptor: ReplicationEntityDescriptor) {} - - public hasPrimaryIdentifierFields(): boolean { - return this.descriptor.primaryIdentifierFields.length > 0; - } - - public syncAny() { - return this.syncData || this.syncParameters; - } -} diff --git a/packages/service-core/src/replication/core/ReplicationJob.ts b/packages/service-core/src/replication/core/ReplicationJob.ts deleted file mode 100644 index fb4b1ff15..000000000 --- a/packages/service-core/src/replication/core/ReplicationJob.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { - CrudOperation, - LSNUpdate, - ReplicationAdapter, - ReplicationMessage, - ReplicationMessageType, - SchemaUpdate, - TruncateRequest -} from './ReplicationAdapter.js'; -import { container, logger } from '@powersync/lib-services-framework'; -import { ErrorRateLimiter } from '../ErrorRateLimiter.js'; -import { Metrics } from '../../metrics/Metrics.js'; -import * as storage from '../../storage/storage-index.js'; -import * as util from './replication-utils.js'; - -export interface ReplicationJobOptions { - adapter: ReplicationAdapter; - storage: storage.SyncRulesBucketStorage; - lock: storage.ReplicationLock; - rateLimiter?: ErrorRateLimiter; -} - -export class ReplicationJob { - private readonly adapter: ReplicationAdapter; - private readonly storage: storage.SyncRulesBucketStorage; - - /** - * TODO: This will need to be properly sourced once we allow more than 1 connection - * @private - */ - private readonly connectionId = 1; - - private abortController = new AbortController(); - private isReplicatingPromise: Promise | undefined; - - constructor(private options: ReplicationJobOptions) { - this.adapter = options.adapter; - this.storage = options.storage; - } - - isStopped(): boolean { - return this.abortController.signal.aborted; - } - - async start() { - try { - this.isReplicatingPromise = this.replicateLoop(); - } catch (e) { - // Fatal exception - container.reporter.captureException(e, { - metadata: { - replicator: this.adapter.name - // TODO We could allow extra metadata contributed from the adapter here - } - }); - logger.error(`Replication failed on ${this.adapter.name}`, e); - } finally { - this.abortController.abort(); - } - await this.options.lock.release(); - } - - async replicateLoop() { - while (!this.isStopped) { - await this.replicate(); - - if (!this.isStopped) { - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - } - - async replicate() { - try { - await this.adapter.ensureConfiguration({ abortSignal: this.abortController.signal }); - const status = await this.storage.getStatus(); - const sourceEntities = await this.getSourceEntities(); - if (!(status.snapshot_done && status.checkpoint_lsn)) { - logger.info(`${this.adapter.name}: Starting initial replication...`); - await this.storage.clear(); - await this.storage.startBatch({}, async (batch) => { - await this.initializeData(sourceEntities, batch); - }); - } else { - logger.info(`${this.adapter.name}: Initial replication already done.`); - } - await this.startReplication(sourceEntities); - } catch (e) { - await this.storage.reportError(e); - throw e; - } - } - - private async getSourceEntities(): Promise { - const resolvedSourceEntities: storage.SourceTable[] = []; - - for (const sourceEntity of this.storage.sync_rules.getSourceTables()) { - const resolved = await this.adapter.resolveReplicationEntities(sourceEntity); - for (const resolvedSourceEntity of resolved) { - const result = await this.storage.resolveTable({ - group_id: this.storage.group_id, - connection_id: this.connectionId, - connection_tag: sourceEntity.connectionTag, - entity_descriptor: resolvedSourceEntity, - sync_rules: this.storage.sync_rules - }); - - resolvedSourceEntities.push(result.table); - } - } - - return resolvedSourceEntities; - } - - async initializeData(sourceEntities: storage.SourceTable[], storageBatch: storage.BucketStorageBatch) { - let at = 0; - let replicatedCount = 0; - let currentEntity: storage.SourceTable | null = null; - let estimatedCount = 0n; - - await this.adapter.initializeData({ - entities: sourceEntities, - abortSignal: this.abortController.signal, - entryConsumer: async (batch) => { - await util.touch(); - - if (batch.entity != currentEntity) { - logger.info(`${this.adapter.name}: Replicating: ${batch.entity.table}`); - currentEntity = batch.entity; - estimatedCount = await this.adapter.count(currentEntity); - } - - if (batch.entries.length > 0 && at - replicatedCount >= 5000) { - logger.info(`${this.adapter.name}: Replicating: ${currentEntity.table} Progress: ${at}/${estimatedCount}`); - } - - for (const entry of batch.entries) { - await storageBatch.save({ tag: 'insert', sourceTable: currentEntity, before: undefined, after: entry }); - } - at += batch.entries.length; - Metrics.getInstance().rows_replicated_total.add(batch.entries.length); - - if (batch.isLast) { - await storageBatch.markSnapshotDone([batch.entity], batch.fromLSN); - await storageBatch.commit(batch.fromLSN); - at = 0; - replicatedCount = 0; - currentEntity = null; - estimatedCount = -1n; - } - } - }); - } - - async startReplication(sourceEntities: storage.SourceTable[]) { - await this.storage.autoActivate(); - - await this.storage.startBatch({}, async (batch) => { - await this.adapter.startReplication({ - entities: sourceEntities, - abortSignal: this.abortController.signal, - changeListener: (update) => this.handeReplicationUpdate(update, batch) - }); - }); - } - - private async handeReplicationUpdate(message: ReplicationMessage, batch: storage.BucketStorageBatch): Promise { - switch (message.type) { - case ReplicationMessageType.CRUD: - const crudOperation = message.payload as CrudOperation; - await batch.save(crudOperation.entry); - Metrics.getInstance().rows_replicated_total.add(1); - return; - case ReplicationMessageType.TRUNCATE: - const truncateRequest = message.payload as TruncateRequest; - await batch.truncate(truncateRequest.entities); - return; - case ReplicationMessageType.SCHEMA_CHANGE: - const schemaUpdate = message.payload as SchemaUpdate; - await this.handleSchemaUpdate(batch, schemaUpdate); - break; - case ReplicationMessageType.COMMIT: - await batch.commit((message.payload as LSNUpdate).lsn!); - Metrics.getInstance().transactions_replicated_total.add(1); - return; - case ReplicationMessageType.KEEP_ALIVE: - await batch.keepalive((message.payload as LSNUpdate).lsn!); - return; - } - } - - async stop(): Promise { - logger.info(`Stopping ${this.adapter.name} replication job: ${this.storage.group_id}`); - // End gracefully - this.abortController.abort(); - await this.isReplicatingPromise; - } - - /** - * Terminate this replication. Cleans up any config for the replication and deletes the replicated data - * - * Stops replication if needed. - * TODO: Confirm if this is still needed at all. - */ - async terminate(): Promise { - logger.info(`Terminating ${this.adapter.name} replication job: ${this.storage.group_id}`); - await this.stop(); - await this.adapter.cleanupReplication(); - await this.options.storage.terminate(); - } - - async handleSchemaUpdate(storageBatch: storage.BucketStorageBatch, update: SchemaUpdate): Promise { - if (!update.entityDescriptor.objectId) { - throw new Error('relationId expected'); - } - const result = await this.storage.resolveTable({ - group_id: this.storage.group_id, - connection_id: this.connectionId, - connection_tag: update.connectionTag, - entity_descriptor: update.entityDescriptor, - sync_rules: this.storage.sync_rules - }); - - // Drop conflicting tables. This includes for example renamed tables. - await storageBatch.drop(result.dropTables); - - // Truncate this table, in case a previous snapshot was interrupted. - await storageBatch.truncate([result.table]); - - await this.initializeData([result.table], storageBatch); - } -} diff --git a/packages/service-core/src/replication/core/Replicator.ts b/packages/service-core/src/replication/core/Replicator.ts deleted file mode 100644 index 9bbca6d91..000000000 --- a/packages/service-core/src/replication/core/Replicator.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * A replicator manages the mechanics for replicating data from a data source to powersync. - * This includes copying across the original data set and then keeping it in sync with the data source. - * It should also handle any changes to the sync rules -> TODO: For now - */ -export interface Replicator { - /** - * Unique identifier for this replicator - */ - id: string; - start(): void; - stop(): Promise; -} diff --git a/packages/service-core/src/replication/core/replication-utils.ts b/packages/service-core/src/replication/core/replication-utils.ts deleted file mode 100644 index 5e2b5c45e..000000000 --- a/packages/service-core/src/replication/core/replication-utils.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { container } from '@powersync/lib-services-framework'; - -export async function touch() { - // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. - // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, - // or reduce PING_INTERVAL here. - return container.probes.touch(); -} diff --git a/packages/service-core/src/replication/replication-index.ts b/packages/service-core/src/replication/replication-index.ts index 7f4cf0983..4859e245d 100644 --- a/packages/service-core/src/replication/replication-index.ts +++ b/packages/service-core/src/replication/replication-index.ts @@ -1,7 +1,7 @@ export * from './ErrorRateLimiter.js'; - -export * from './core/ReplicationAdapter.js'; -export * from './core/ReplicationEngine.js'; -export * from './core/ReplicationEntity.js'; -export * from './core/ReplicationModule.js'; -export * from './core/Replicator.js'; +export * from './AbstractReplicationJob.js'; +export * from './AbstractReplicator.js'; +export * from './ReplicationAdapter.js'; +export * from './ReplicationEngine.js'; +export * from './ReplicationEntity.js'; +export * from '../modules/ReplicationModule.js'; From bef4198f2164c9563b0a90f7234ae702ebb4ba3c Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 21 Aug 2024 10:03:52 +0200 Subject: [PATCH 113/298] wip --- .../module-mysql/src/module/MySQLModule.ts | 5 +- .../src/replication/MSSQLReplicator.ts | 53 +- .../src/replication/MysqlBinLogStream.ts | 556 ++++++++++++++++++ .../replication/MysqlBinLogStreamManager.ts | 192 ++++++ .../replication/MysqlBinLogStreamRunner.ts | 113 ++++ modules/module-mysql/src/utils/mysql_utils.ts | 12 +- .../src/utils/replication/GTID.ts | 37 +- .../src/utils/replication/head.ts | 2 + 8 files changed, 928 insertions(+), 42 deletions(-) create mode 100644 modules/module-mysql/src/replication/MysqlBinLogStream.ts create mode 100644 modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts create mode 100644 modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts index f04ee117f..58223664e 100644 --- a/modules/module-mysql/src/module/MySQLModule.ts +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -5,15 +5,18 @@ import { MSSQLReplicator } from '../replication/MSSQLReplicator.js'; import * as types from '../types/types.js'; export class MySQLModule extends replication.ReplicationModule { + protected context: system.ServiceContext | null; constructor() { super({ name: 'MySQL', type: types.MYSQL_CONNECTION_TYPE, configSchema: types.MySQLConnectionConfig }); + this.context = null; } async initialize(context: system.ServiceContextContainer): Promise { + this.context = context; await super.initialize(context); // TODO move this to the binlog consumer @@ -30,7 +33,7 @@ export class MySQLModule extends replication.ReplicationModule { console.log('starting'); - const zongji = new ZongJi({ - host: this.config.hostname, - user: this.config.username, - password: this.config.password - // debug: true - }); - - zongji.on('binlog', function (evt: any) { - // evt.dump(); - console.log(evt); - }); - - zongji.start({ - includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'] - }); - - process.on('SIGINT', function () { - console.log('Got SIGINT.'); - zongji.stop(); - }); + this.manager = new MysqlBinLogStreamManager(this.serviceContext, this.pool); + + this.manager.start(); + // const zongji = new ZongJi({ + // host: this.config.hostname, + // user: this.config.username, + // password: this.config.password + // // debug: true + // }); + + // zongji.on('binlog', function (evt: any) { + // // evt.dump(); + // console.log(evt); + // }); + + // zongji.start({ + // includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows'] + // }); + + // process.on('SIGINT', function () { + // console.log('Got SIGINT.'); + // zongji.stop(); + // }); } async stop(): Promise { + await this.manager?.stop(); // throw new Error('Method not implemented.'); } } diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts new file mode 100644 index 000000000..e658a8a74 --- /dev/null +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -0,0 +1,556 @@ +import { logger } from '@powersync/lib-services-framework'; +import * as sync_rules from '@powersync/service-sync-rules'; + +import { storage } from '@powersync/service-core'; + +import mysql, { RowDataPacket } from 'mysql2/promise'; +// @ts-expect-error +import ZongJi from '@vlasky/zongji'; +import { NormalizedMySQLConnectionConfig } from '../types/types.js'; +import * as replication_utils from '../utils/replication/replication-utils.js'; + +export interface BinLogStreamOptions { + pool: mysql.Pool; + connection_config: NormalizedMySQLConnectionConfig; + + factory: storage.BucketStorageFactory; + storage: storage.SyncRulesBucketStorage; + abort_signal: AbortSignal; +} + +interface InitResult { + needsInitialSync: boolean; +} + +type Data = Record; +type EventType = 'insert' | 'update' | 'delete'; + +interface DatabaseEvent { + database: string; + table: string; + type: EventType; + ts: number; + xid: number; + xoffset?: number; + gtid: string; + commit?: boolean; + data: Data; + old?: Data; +} + +interface MysqlRelId { + schema: string; + name: string; +} + +/** + * MySQL does not have same relation structure. Just returning unique key as string. + * @param source + */ +function getMysqlRelId(source: MysqlRelId): string { + return `${source.schema}.${source.name}`; +} + +async function getReplicationKeyColumns(db: mysql.Pool, relId: MysqlRelId) { + const primaryKeyQuery = ` + SELECT s.COLUMN_NAME AS name, c.DATA_TYPE AS type + FROM INFORMATION_SCHEMA.STATISTICS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + AND s.INDEX_NAME = 'PRIMARY' + ORDER BY s.SEQ_IN_INDEX; + `; + const primaryKeyRows = await db.execute(primaryKeyQuery, [relId.schema, relId.name]); + + if (primaryKeyRows[0].length > 0) { + logger.info(`Found primary key, returning it: ${primaryKeyRows[0].reduce((a, b) => `${a}, "${b.name}"`, '')}`); + return { + columns: (primaryKeyRows[0] as RowDataPacket[]).map((row) => ({ + name: row.name as string, + // Ignoring MySQL types: we should check if they are used. + typeOid: -1 + })), + replicationIdentity: 'default' + }; + } + + // TODO: test code with tables with unique keys, compound key etc. + // No primary key, find the first valid unique key + const uniqueKeyQuery = ` + SELECT s.INDEX_NAME, s.COLUMN_NAME, c.DATA_TYPE, s.NON_UNIQUE, s.NULLABLE + FROM INFORMATION_SCHEMA.STATISTICS s + JOIN INFORMATION_SCHEMA.COLUMNS c + ON s.TABLE_SCHEMA = c.TABLE_SCHEMA + AND s.TABLE_NAME = c.TABLE_NAME + AND s.COLUMN_NAME = c.COLUMN_NAME + WHERE s.TABLE_SCHEMA = ? + AND s.TABLE_NAME = ? + AND s.INDEX_NAME != 'PRIMARY' + AND s.NON_UNIQUE = 0 + ORDER BY s.SEQ_IN_INDEX; + `; + const uniqueKeyRows = await db.execute(uniqueKeyQuery, [relId.schema, relId.name]); + + const currentUniqueKey = uniqueKeyRows[0]?.[0]?.INDEX_NAME ?? ''; + const uniqueKeyColumns: RowDataPacket[] = []; + for (const row of uniqueKeyRows[0]) { + if (row.INDEX_NAME === currentUniqueKey) { + uniqueKeyColumns.push(row); + } + } + if (uniqueKeyColumns.length > 0) { + logger.info('Found unique key, returning it'); + return { + columns: uniqueKeyColumns.map((col) => ({ + name: col.COLUMN_NAME as string, + // Ignoring MySQL types: we should check if they are used. + typeOid: -1 + })), + replicationIdentity: 'index' + }; + } + + logger.info('No unique key found, returning all columns'); + const allColumnsQuery = ` + SELECT COLUMN_NAME AS name + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = ? + AND TABLE_NAME = ? + ORDER BY ORDINAL_POSITION; + `; + const allColumnsRows = await db.execute(allColumnsQuery, [relId.schema, relId.name]); + + return { + columns: (allColumnsRows[0] as RowDataPacket[]).map((row) => ({ + name: row.name as string, + typeOid: -1 + })), + replicationIdentity: 'full' + }; +} + +export class MysqlBinLogStream { + sync_rules: sync_rules.SqlSyncRules; + group_id: number; + + connection_id = 1; + + private readonly storage: storage.SyncRulesBucketStorage; + + private slot_name: string; + + private abort_signal: AbortSignal; + + private pool: mysql.Pool; + private relation_cache = new Map(); + + constructor(protected options: BinLogStreamOptions) { + this.storage = options.storage; + this.sync_rules = options.storage.sync_rules; + this.group_id = options.storage.group_id; + this.slot_name = options.storage.slot_name; + this.pool = options.pool; + + this.abort_signal = options.abort_signal; + this.abort_signal.addEventListener( + 'abort', + () => { + // TODO close things + }, + { once: true } + ); + } + + get connectionTag() { + // TODO + return 'default'; + } + + get stopped() { + return this.abort_signal.aborted; + } + + async handleRelation(batch: storage.BucketStorageBatch, entity: storage.SourceEntityDescriptor, snapshot: boolean) { + const result = await this.storage.resolveTable({ + group_id: this.group_id, + connection_id: this.connection_id, + connection_tag: this.connectionTag, + entity_descriptor: entity, + sync_rules: this.sync_rules + }); + + // Drop conflicting tables. This includes for example renamed tables. + await batch.drop(result.dropTables); + + // Snapshot if: + // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) + // 2. Snapshot is not already done, AND: + // 3. The table is used in sync rules. + const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; + + if (shouldSnapshot) { + // Truncate this table, in case a previous snapshot was interrupted. + await batch.truncate([result.table]); + + // TODO: put zero GTID somewhere. + let gtid: replication_utils.ReplicatedGTID | null = null; + // Start the snapshot inside a transaction. + // We use a dedicated connection for this. + try { + await this.pool.query('BEGIN'); + try { + gtid = await replication_utils.readMasterGtid(this.pool); + await this.snapshotTable(batch, this.pool, result.table); + await this.pool.query('COMMIT'); + } catch (e) { + await this.pool.query('ROLLBACK'); + throw e; + } + } finally { + await this.pool.end(); + } + const [table] = await batch.markSnapshotDone([result.table], gtid.comparable); + return table; + } + + return result.table; + } + + async getQualifiedTableNames( + batch: storage.BucketStorageBatch, + db: mysql.Pool, + tablePattern: sync_rules.TablePattern + ): Promise { + if (tablePattern.connectionTag != this.connectionTag) { + return []; + } + + let tableRows: any[]; + const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; + if (tablePattern.isWildcard) { + const result = await db.query( + `SELECT TABLE_NAME +FROM information_schema.tables +WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?; +`, + [tablePattern.schema, tablePattern.tablePattern] + ); + tableRows = result[0]; + } else { + const result = await db.query( + `SELECT TABLE_NAME +FROM information_schema.tables +WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; +`, + [tablePattern.schema, tablePattern.tablePattern] + ); + tableRows = result[0]; + } + let result: storage.SourceTable[] = []; + + for (let row of tableRows) { + const name = row['TABLE_NAME'] as string; + if (prefix && !name.startsWith(prefix)) { + continue; + } + + const rs = await db.query( + `SELECT 1 +FROM information_schema.tables +WHERE table_schema = ? AND table_name = ? +AND table_type = 'BASE TABLE';`, + [tablePattern.schema, tablePattern.name] + ); + if (rs[0].length == 0) { + logger.info(`Skipping ${tablePattern.schema}.${name} - no table exists/is not a base table`); + continue; + } + + const cresult = await replication_utils.getReplicationIdentityColumns({ + db: this.pool, + schema: tablePattern.schema, + table_name: tablePattern.name + }); + + const table = await this.handleRelation( + batch, + { + name, + schema: tablePattern.schema, + objectId: getMysqlRelId(tablePattern), + replicationColumns: cresult.columns + }, + false + ); + + result.push(table); + } + return result; + } + + async initSlot(): Promise { + await replication_utils.checkSourceConfiguration(this.pool); + + const slotName = this.slot_name; + + const status = await this.storage.getStatus(); + if (status.snapshot_done && status.checkpoint_lsn) { + logger.info(`${slotName} Initial replication already done`); + // Success + logger.info(`MySQL not using Slots ${slotName} appears healthy`); + return { needsInitialSync: false }; + } + + return { needsInitialSync: true }; + } + + /** + * Start initial replication. + * + * If (partial) replication was done before on this slot, this clears the state + * and starts again from scratch. + */ + async startInitialReplication() { + const db = this.pool; + + const slotName = this.slot_name; + + await this.storage.clear(); + const headGTID = await replication_utils.readMasterGtid(db); + logger.info(`Using GTID:: '${headGTID}'`); + await db.query('BEGIN'); + try { + logger.info(`${slotName} Starting initial replication`); + await this.initialReplication(db, headGTID); + logger.info(`${slotName} Initial replication done`); + await db.query('COMMIT'); + } catch (e) { + await db.query('ROLLBACK'); + throw e; + } + } + + async initialReplication(db: mysql.Pool, gtid: replication_utils.ReplicatedGTID) { + // TODO fix database + const sourceTables = this.sync_rules + .getSourceTables() + .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); + await this.storage.startBatch({}, async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, db, tablePattern); + for (let table of tables) { + await this.snapshotTable(batch, db, table); + await batch.markSnapshotDone([table], gtid.comparable); + + // await touch(); + } + } + await batch.commit(gtid.comparable); + }); + } + + private transformMysqlToSynRulesRow(row: Record): sync_rules.SqliteRow { + for (let key in row) { + if (row[key] instanceof Date) { + row[key] = row[key].toISOString(); + } + } + return sync_rules.toSyncRulesRow(row); + } + + private async snapshotTable(batch: storage.BucketStorageBatch, db: mysql.Pool, table: storage.SourceTable) { + logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); + const results = await db.query(`SELECT * FROM ${table.schema}.${table.table}`); + for (let record of results[0]) { + await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + after: this.transformMysqlToSynRulesRow(record) + }); + } + await batch.flush(); + } + + private getTable(relationId: MysqlRelId): storage.SourceTable { + const resolvedRelationId = getMysqlRelId(relationId); + const table = this.relation_cache.get(resolvedRelationId); + if (table == null) { + // We should always receive a replication message before the relation is used. + // If we can't find it, it's a bug. + throw new Error(`Missing relation cache for ${resolvedRelationId}`); + } + return table; + } + + async writeChange(batch: storage.BucketStorageBatch, msg: DatabaseEvent): Promise { + if (msg.type == 'insert' || msg.type == 'update' || msg.type == 'delete') { + const table = this.getTable({ schema: msg.database, name: msg.table }); + if (msg.type == 'insert') { + // rows_replicated_total.add(1); + return await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + after: this.transformMysqlToSynRulesRow(msg.data) + }); + } else if (msg.type == 'update') { + // rows_replicated_total.add(1); + return await batch.save({ + tag: 'update', + sourceTable: table, + before: msg.old ? this.transformMysqlToSynRulesRow(msg.old) : undefined, + after: this.transformMysqlToSynRulesRow(msg.data) + }); + } else if (msg.type == 'delete') { + // rows_replicated_total.add(1); + return await batch.save({ + tag: 'delete', + sourceTable: table, + before: this.transformMysqlToSynRulesRow(msg.data), + after: undefined + }); + } + } + return null; + } + + async replicate() { + try { + // If anything errors here, the entire replication process is halted, and + // all connections automatically closed, including this one. + await this.initReplication(); + await this.streamChanges(); + } catch (e) { + await this.storage.reportError(e); + throw e; + } + } + + async initReplication() { + const result = await this.initSlot(); + await this.loadTables(); + if (result.needsInitialSync) { + await this.startInitialReplication(); + } + } + + private async loadTables() { + // TODO fix database + const sourceTables = this.sync_rules + .getSourceTables() + .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); + await this.storage.startBatch({}, async (batch) => { + for (let tablePattern of sourceTables) { + await this.getQualifiedTableNames(batch, this.pool, tablePattern); + } + }); + } + + async streamChanges() { + // Auto-activate as soon as initial replication is done + await this.storage.autoActivate(); + + const { checkpoint_lsn } = await this.storage.getStatus(); + const fromGTID = checkpoint_lsn + ? replication_utils.ReplicatedGTID.fromSerialized(checkpoint_lsn) + : await replication_utils.readMasterGtid(this.pool); + const binLogPositionState = fromGTID.nextPosition!; + + const zongji = new ZongJi({ + host: this.options.connection_config.hostname, + user: this.options.connection_config.username, + password: this.options.connection_config.password + }); + + zongji.on('binlog', async (evt: any) => { + console.log(evt, evt.getEventName()); + + // State machine + switch (evt.getEventName()) { + case 'unknown': + // This is probably the GTID event + break; + case 'rotate': + // Update the position + binLogPositionState.filename = evt.binlogName; + binLogPositionState.offset = evt.position; + break; + case 'writerows': + break; + case 'updaterows': + break; + case 'deleterows': + break; + case 'xid': + // Need to commit + break; + } + // logger.info(`mysqlbinlogstream: received message: '${evt}'`); + // let parsedMsg: DatabaseEvent = JSON.parse(evt); + // logger.info(`mysqlbinlogstream: commit: '${parsedMsg.commit}' xoffset:'${parsedMsg.xoffset}'`); + // // await this.writeChange(batch, parsedMsg); + + // const gtid = parsedMsg.gtid; //gtidMakeComparable(parsedMsg.gtid); + // if (parsedMsg.commit) { + // // await batch.commit(gtid); + // } + // logger.info(`${this.slot_name} replicating op GTID: '${gtid}'`); + // chunks_replicated_total.add(1); + }); + + zongji.start({ + includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'unknown'], + filename: binLogPositionState.filename, + position: binLogPositionState.offset + }); + + // Forever young + await new Promise((r) => { + this.abort_signal.addEventListener( + 'abort', + () => { + zongji.stop(); + r(); + }, + { once: true } + ); + }); + + // // Replication never starts in the middle of a transaction + // const finished = new Promise((resolve, reject) => { + // readStream.on('data', async (chunk: string) => { + // await touch(); + + // if (this.abort_signal.aborted) { + // logger.info('Aborted, closing stream'); + // readStream.close(); + // deleteFifoPipe(fifoPath); + // resolve(); + // return; + // } + // for (const msg of chunk.split('\n')) { + // if (msg.trim()) { + // logger.info(`mysqlbinlogstream: received message: '${msg}'`); + // let parsedMsg: DatabaseEvent = JSON.parse(msg); + // logger.info(`mysqlbinlogstream: commit: '${parsedMsg.commit}' xoffset:'${parsedMsg.xoffset}'`); + // await this.writeChange(batch, parsedMsg); + // const gtid = gtidMakeComparable(parsedMsg.gtid); + // if (parsedMsg.commit) { + // await batch.commit(gtid); + // } + // micro.logger.info(`${this.slot_name} replicating op GTID: '${gtid}'`); + // chunks_replicated_total.add(1); + // } + // } + // }); + // }); + + // await finished; + // }); + } +} diff --git a/modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts b/modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts new file mode 100644 index 000000000..1a69c980b --- /dev/null +++ b/modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts @@ -0,0 +1,192 @@ +import { logger } from '@powersync/lib-services-framework'; +import { storage, system, utils } from '@powersync/service-core'; +import mysql from 'mysql2/promise'; +import { hrtime } from 'node:process'; +import { normalizeConnectionConfig } from '../types/types.js'; +import { MysqlBinLogStreamRunner } from './MysqlBinLogStreamRunner.js'; + +// 5 minutes +const PING_INTERVAL = 1_000_000_000n * 300n; + +export class MysqlBinLogStreamManager { + private streams = new Map(); + + private stopped = false; + + // First ping is only after 5 minutes, not when starting + private lastPing = hrtime.bigint(); + + private storage: storage.BucketStorageFactory; + + constructor(protected serviceContext: system.ServiceContext, protected pool: mysql.Pool) { + this.storage = serviceContext.storage.bucketStorage; + } + + start() { + this.runLoop().catch((e) => { + console.error(e); + }); + } + + async stop() { + this.stopped = true; + let promises: Promise[] = []; + for (let stream of this.streams.values()) { + promises.push(stream.stop()); + } + await Promise.all(promises); + } + + private async runLoop() { + const configured_sync_rules = await utils.loadSyncRules(this.serviceContext.configuration); + let configured_lock: storage.ReplicationLock | undefined = undefined; + if (configured_sync_rules != null) { + logger.info('Loading sync rules from configuration'); + try { + // Configure new sync rules, if it has changed. + // In that case, also immediately take out a lock, so that another process doesn't start replication on it. + const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { + lock: true + }); + logger.info(`configureSyncRules: update: ${updated}, persisted_sync_rules: ${persisted_sync_rules}`); + if (lock) { + configured_lock = lock; + } + } catch (e) { + // Log, but continue with previous sync rules + logger.error(`Failed to load sync rules from configuration`, e); + } + } else { + logger.info('No sync rules configured - configure via API'); + } + while (!this.stopped) { + // await touch(); + try { + const { pool } = this; + if (pool) { + await this.refresh({ configured_lock }); + // The lock is only valid on the first refresh. + configured_lock = undefined; + + // TODO: Ping on all connections when we have multiple + // Perhaps MysqlWalStreamRunner would be a better place to do pings? + // We don't ping while in error retry back-off, to avoid having too + // many authentication failures. + // if (this.rateLimiter.mayPing()) { + // await this.ping(pool); + // } + } + } catch (e) { + logger.error(`Failed to refresh wal streams`, e); + } + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + + /** + * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. + * + * The old WAL files are only deleted once no replication slot still references it. + * + * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot + * advancing**. + * + * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. + * + * **This may be a bug in pgwire or how we're using it. + */ + private async ping(db: mysql.Pool) { + const now = hrtime.bigint(); + if (now - this.lastPing >= PING_INTERVAL) { + try { + await db.query('SELECT 1'); + } catch (e) { + logger.warn(`Failed to ping`, e); + } + this.lastPing = now; + } + } + + private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { + if (this.stopped) { + return; + } + + let configured_lock = options?.configured_lock; + + const existingStreams = new Map(this.streams.entries()); + const replicating = await this.storage.getReplicatingSyncRules(); + const newStreams = new Map(); + for (let syncRules of replicating) { + const existing = existingStreams.get(syncRules.id); + if (existing && !existing.stopped) { + // No change + existingStreams.delete(syncRules.id); + newStreams.set(syncRules.id, existing); + } else if (existing && existing.stopped) { + // Stopped (e.g. fatal error, slot rename). + // Remove from the list. Next refresh call will restart the stream. + existingStreams.delete(syncRules.id); + } else { + // New (or resume after restart) + try { + let lock: storage.ReplicationLock; + if (configured_lock?.sync_rules_id == syncRules.id) { + lock = configured_lock; + } else { + lock = await syncRules.lock(); + } + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + + const stream = new MysqlBinLogStreamRunner({ + connection_config: normalizeConnectionConfig(this.serviceContext.configuration.connections?.[0] as any), // TODO + factory: this.storage, + storage: storage, + source_db: this.pool, + lock + }); + newStreams.set(syncRules.id, stream); + stream.start(); + } catch (e) { + // Could be a sync rules parse error, + // for example from stricter validation that was added. + // This will be retried every couple of seconds. + // When new (valid) sync rules are deployed and processed, this one be disabled. + logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); + } + } + } + + this.streams = newStreams; + + for (let stream of existingStreams.values()) { + // Old - stop and remove. + await stream.terminate(); + } + + // Sync rules stopped previously or by a different process. + const stopped = await this.storage.getStoppedSyncRules(); + for (let syncRules of stopped) { + try { + const lock = await syncRules.lock(); + try { + const parsed = syncRules.parsed(); + const storage = this.storage.getInstance(parsed); + const stream = new MysqlBinLogStreamRunner({ + connection_config: normalizeConnectionConfig(this.serviceContext.configuration.connections?.[0] as any), // TODO + factory: this.storage, + storage: storage, + source_db: this.pool, + lock + }); + await stream.terminate(); + } finally { + await lock.release(); + } + } catch (e) { + logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); + } + } + } +} diff --git a/modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts b/modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts new file mode 100644 index 000000000..ccb55022c --- /dev/null +++ b/modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts @@ -0,0 +1,113 @@ +import { logger } from '@powersync/lib-services-framework'; +import { storage } from '@powersync/service-core'; +import mysql from 'mysql2/promise'; +import { NormalizedMySQLConnectionConfig } from '../types/types.js'; +import { MysqlBinLogStream } from './MysqlBinLogStream.js'; + +export interface BinLogStreamRunnerOptions { + factory: storage.BucketStorageFactory; + storage: storage.SyncRulesBucketStorage; + source_db: mysql.Pool; + lock: storage.ReplicationLock; + connection_config: NormalizedMySQLConnectionConfig; +} + +export class MysqlBinLogStreamRunner { + private abortController = new AbortController(); + + private runPromise?: Promise; + + constructor(public options: BinLogStreamRunnerOptions) {} + + start() { + this.runPromise = this.run(); + } + + get slot_name() { + return this.options.storage.slot_name; + } + + get stopped() { + return this.abortController.signal.aborted; + } + + async run() { + try { + await this.replicateLoop(); + } catch (e) { + // Fatal exception + // if (e instanceof MissingReplicationSlotError) { + // This stops replication on this slot, and creates a new slot + // await this.options.storage.factory.slotRemoved(this.slot_name); + // } + } finally { + this.abortController.abort(); + } + await this.options.lock.release(); + } + + async replicateLoop() { + while (!this.stopped) { + await this.replicateOnce(); + + if (!this.stopped) { + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + } + + async replicateOnce() { + // New connections on every iteration (every error with retry), + // otherwise we risk repeating errors related to the connection, + // such as caused by cached PG schemas. + try { + // await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); + if (this.stopped) { + return; + } + const stream = new MysqlBinLogStream({ + abort_signal: this.abortController.signal, + connection_config: this.options.connection_config, + factory: this.options.factory, + storage: this.options.storage, + pool: this.options.source_db + }); + await stream.replicate(); + } catch (e) { + logger.error(`Replication error`, e); + if (e.cause != null) { + logger.error(`cause`, e.cause); + } + } + } + + /** + * This will also release the lock if start() was called earlier. + */ + async stop(options?: { force?: boolean }) { + logger.info(`${this.slot_name} Stopping replication`); + // End gracefully + this.abortController.abort(); + + if (options?.force) { + // destroy() is more forceful. + this.options.source_db?.destroy(); + } + await this.runPromise; + } + + /** + * Terminate this replication stream. This drops the replication slot and deletes the replication data. + * + * Stops replication if needed. + */ + async terminate(options?: { force?: boolean }) { + logger.info(`${this.slot_name} Terminating replication`); + // if (!isNormalizedPostgresConnection(this.options.source_db)) { + // throw new Error('Replication only supported for normalized Postgres connections'); + // } + await this.stop(options); + await this.options.source_db.end(); + await this.options.storage.terminate(); + } +} diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 8f65bb500..06eeddac0 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -28,16 +28,18 @@ export async function retriedQuery(options: RetiredMySQLQueryOptions) { } export function createPool(config: types.ResolvedConnectionConfig) { + const sslOptions = { + ca: config.cacert, + key: config.client_private_key, + cert: config.client_certificate + }; + const hasSSLOptions = Object.values(sslOptions).some((v) => !!v); return mysql.createPool({ host: config.hostname, user: config.username, password: config.password, database: config.database, - ssl: { - ca: config.cacert, - key: config.client_private_key, - cert: config.client_certificate - } + ssl: hasSSLOptions ? sslOptions : undefined }); } diff --git a/modules/module-mysql/src/utils/replication/GTID.ts b/modules/module-mysql/src/utils/replication/GTID.ts index 1984abe61..b0e54b68c 100644 --- a/modules/module-mysql/src/utils/replication/GTID.ts +++ b/modules/module-mysql/src/utils/replication/GTID.ts @@ -9,9 +9,9 @@ export type BinLogPosition = { export type ReplicatedGTIDSpecification = { raw_gtid: string; /** - * The position of this GTID + * The position in a BinLog file where this transaction has been replicated in. */ - current_position?: BinLogPosition; + replicated_position?: BinLogPosition; /** * The position to start from for the next replication event. */ @@ -41,7 +41,7 @@ export class ReplicatedGTID { : undefined; return { raw_gtid: components[1], - current_position: currentPosition, + replicated_position: currentPosition, next_position: nextPosition }; } @@ -58,10 +58,10 @@ export class ReplicatedGTID { constructor(protected options: ReplicatedGTIDSpecification) {} /** - * Get the BinLog position of this GTID event + * Get the BinLog position of this GTID event if it has been replicated already. */ - get currentPosition() { - return this.options.current_position; + get replicatedPosition() { + return this.options.replicated_position; } /** @@ -88,7 +88,7 @@ export class ReplicatedGTID { * `padded_end_transaction|raw_gtid|current_binlog_filename|current_binlog_position|next_binlog_filename|next_binlog_position` */ get comparable() { - const { raw, currentPosition, nextPosition } = this; + const { raw, replicatedPosition: currentPosition, nextPosition } = this; const [, transactionRanges] = this.raw.split(':'); let maxTransactionId = 0; @@ -120,7 +120,7 @@ export class ReplicatedGTID { // Default to the first file for the start to handle the zero GTID case. const startFileIndex = Math.max( - logFiles.findIndex((f) => f['Log_name'] == this.currentPosition?.filename), + logFiles.findIndex((f) => f['Log_name'] == this.replicatedPosition?.filename), 0 ); const startFileEntry = logFiles[startFileIndex]; @@ -129,14 +129,27 @@ export class ReplicatedGTID { return null; } + /** + * Fall back to the next position for comparison if the replicated position is not present + */ + const endPosition = to.replicatedPosition ?? to.nextPosition; + // Default to the past the last file to cater for the HEAD case - const testEndFileIndex = logFiles.findIndex((f) => f['Log_name'] == to.currentPosition?.filename); - const endFileIndex = testEndFileIndex >= 0 ? testEndFileIndex : logFiles.length; + const testEndFileIndex = logFiles.findIndex((f) => f['Log_name'] == endPosition?.filename); + // If the endPosition is not defined and found. Fallback to the last file as the end + const endFileIndex = testEndFileIndex < 0 && !endPosition ? logFiles.length : logFiles.length - 1; + + const endFileEntry = logFiles[endFileIndex]; + + if (!endFileEntry) { + return null; + } return ( startFileEntry['File_size'] - - (this?.currentPosition?.offset ?? 0) + - (to.currentPosition?.offset ?? 0) + + (this?.replicatedPosition?.offset ?? 0) - + endFileEntry['File_size'] + + (endPosition?.offset ?? 0) + logFiles.slice(startFileIndex + 1, endFileIndex).reduce((sum, file) => sum + file['File_size'], 0) ); } diff --git a/modules/module-mysql/src/utils/replication/head.ts b/modules/module-mysql/src/utils/replication/head.ts index 895534e05..6c8e5d1aa 100644 --- a/modules/module-mysql/src/utils/replication/head.ts +++ b/modules/module-mysql/src/utils/replication/head.ts @@ -20,6 +20,8 @@ export async function readMasterGtid(db: mysql.Pool): Promise { }; return new ReplicatedGTID({ + // The head always points to the next position to start replication from + replicated_position: position, next_position: position, raw_gtid: gtidResult.GTID }); From fbd99a5ebbf0dfd55e2765fd8f08d1b3a0ecc4d6 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 21 Aug 2024 11:28:08 +0200 Subject: [PATCH 114/298] Cleaned up WalStream Replicator and ReplicationJobs Fixed test imports --- .../src/module/PostgresModule.ts | 34 ++-- .../replication/PostgresErrorRateLimiter.ts | 44 +++++ .../src/replication/WalStream.ts | 29 +--- .../replication/WalStreamReplicationJob.ts | 155 ++++++++++++++++++ .../src/replication/WalStreamReplicator.ts | 25 +++ .../module-postgres/test/src/pg_test.test.ts | 2 +- .../src/modules/AbstractModule.ts | 14 +- .../service-core/src/modules/ModuleManager.ts | 5 +- .../src/modules/ReplicationModule.ts | 104 ------------ .../src/replication/AbstractReplicationJob.ts | 5 +- .../src/replication/AbstractReplicator.ts | 19 +-- .../src/replication/ReplicationEngine.ts | 14 +- service/src/runners/stream-worker.ts | 5 +- 13 files changed, 280 insertions(+), 175 deletions(-) create mode 100644 modules/module-postgres/src/replication/PostgresErrorRateLimiter.ts create mode 100644 modules/module-postgres/src/replication/WalStreamReplicationJob.ts create mode 100644 modules/module-postgres/src/replication/WalStreamReplicator.ts delete mode 100644 packages/service-core/src/modules/ReplicationModule.ts diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index b48c2e3ab..0ab8325a4 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,26 +1,23 @@ -import { - api, - auth, - ConfigurationFileSyncRulesProvider, - replication, - Replicator, - system -} from '@powersync/service-core'; +import { api, auth, ConfigurationFileSyncRulesProvider, replication, system } from '@powersync/service-core'; import * as jpgwire from '@powersync/service-jpgwire'; -import { logger } from '@powersync/lib-services-framework'; import * as types from '../types/types.js'; import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; -import { WalStreamManager } from '../replication/WalStreamManager.js'; +import { WalStreamReplicator } from '../replication/WalStreamReplicator.js'; import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js'; +import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js'; export class PostgresModule extends replication.ReplicationModule { + private connectionFactories: Set; + constructor() { super({ name: 'Postgres', type: types.POSTGRES_CONNECTION_TYPE, configSchema: types.PostgresConnectionConfig }); + + this.connectionFactories = new Set(); } async initialize(context: system.ServiceContextContainer): Promise { @@ -45,16 +42,18 @@ export class PostgresModule extends replication.ReplicationModule {} + async teardown(): Promise { + for (const connectionFactory of this.connectionFactories) { + await connectionFactory.shutdown(); + } + } protected registerSupabaseAuth(context: system.ServiceContextContainer) { const { configuration } = context; @@ -82,7 +84,7 @@ export class PostgresModule extends replication.ReplicationModule !!c) diff --git a/modules/module-postgres/src/replication/PostgresErrorRateLimiter.ts b/modules/module-postgres/src/replication/PostgresErrorRateLimiter.ts new file mode 100644 index 000000000..9a86a0704 --- /dev/null +++ b/modules/module-postgres/src/replication/PostgresErrorRateLimiter.ts @@ -0,0 +1,44 @@ +import { setTimeout } from 'timers/promises'; +import { ErrorRateLimiter } from '@powersync/service-core'; + +export class PostgresErrorRateLimiter implements ErrorRateLimiter { + nextAllowed: number = Date.now(); + + async waitUntilAllowed(options?: { signal?: AbortSignal | undefined } | undefined): Promise { + const delay = Math.max(0, this.nextAllowed - Date.now()); + // Minimum delay between connections, even without errors + this.setDelay(500); + await setTimeout(delay, undefined, { signal: options?.signal }); + } + + mayPing(): boolean { + return Date.now() >= this.nextAllowed; + } + + reportError(e: any): void { + const message = (e.message as string) ?? ''; + if (message.includes('password authentication failed')) { + // Wait 15 minutes, to avoid triggering Supabase's fail2ban + this.setDelay(900_000); + } else if (message.includes('ENOTFOUND')) { + // DNS lookup issue - incorrect URI or deleted instance + this.setDelay(120_000); + } else if (message.includes('ECONNREFUSED')) { + // Could be fail2ban or similar + this.setDelay(120_000); + } else if ( + message.includes('Unable to do postgres query on ended pool') || + message.includes('Postgres unexpectedly closed connection') + ) { + // Connection timed out - ignore / immediately retry + // We don't explicitly set the delay to 0, since there could have been another error that + // we need to respect. + } else { + this.setDelay(30_000); + } + } + + private setDelay(delay: number) { + this.nextAllowed = Math.max(this.nextAllowed, Date.now() + delay); + } +} diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 7eed4257a..95823cee5 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -3,16 +3,15 @@ import * as util from '../utils/pgwire_utils.js'; import { container, errors, logger } from '@powersync/lib-services-framework'; import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; import { getPgOutputRelation, getRelId } from './PgRelation.js'; -import { WalConnection } from './WalConnection.js'; import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; import { checkSourceConfiguration, getReplicationIdentityColumns } from './replication-utils.js'; import { PgManager } from './PgManager.js'; export const ZERO_LSN = '00000000/00000000'; +export const PUBLICATION_NAME = 'powersync'; export interface WalStreamOptions { connections: PgManager; - factory: storage.BucketStorageFactory; storage: storage.SyncRulesBucketStorage; abort_signal: AbortSignal; } @@ -31,12 +30,11 @@ export class WalStream { sync_rules: SqlSyncRules; group_id: number; - wal_connection: WalConnection; connection_id = 1; private readonly storage: storage.SyncRulesBucketStorage; - private slot_name: string; + private readonly slot_name: string; private connections: PgManager; @@ -53,7 +51,6 @@ export class WalStream { this.slot_name = options.storage.slot_name; this.connections = options.connections; - this.wal_connection = new WalConnection({ db: this.connections.pool, sync_rules: this.sync_rules }); this.abort_signal = options.abort_signal; this.abort_signal.addEventListener( 'abort', @@ -79,14 +76,6 @@ export class WalStream { ); } - get publication_name() { - return this.wal_connection.publication_name; - } - - get connectionTag() { - return this.wal_connection.connectionTag; - } - get stopped() { return this.abort_signal.aborted; } @@ -97,7 +86,7 @@ export class WalStream { tablePattern: TablePattern ): Promise { const schema = tablePattern.schema; - if (tablePattern.connectionTag != this.connectionTag) { + if (tablePattern.connectionTag != this.connections.connectionTag) { return []; } @@ -149,13 +138,13 @@ export class WalStream { const rs = await db.query({ statement: `SELECT 1 FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, params: [ - { type: 'varchar', value: this.publication_name }, + { type: 'varchar', value: PUBLICATION_NAME }, { type: 'varchar', value: tablePattern.schema }, { type: 'varchar', value: name } ] }); if (rs.rows.length == 0) { - logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${this.publication_name} publication`); + logger.info(`Skipping ${tablePattern.schema}.${name} - not part of ${PUBLICATION_NAME} publication`); continue; } @@ -212,7 +201,7 @@ export class WalStream { 'publication_names', $2)`, params: [ { type: 'varchar', value: slotName }, - { type: 'varchar', value: this.publication_name } + { type: 'varchar', value: PUBLICATION_NAME } ] }); // Success @@ -369,7 +358,7 @@ WHERE oid = $1::regclass`, const estimatedCount = await this.estimatedCount(db, table); let at = 0; let lastLogIndex = 0; - const cursor = await db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); + const cursor = db.stream({ statement: `SELECT * FROM ${table.escapedIdentifier}` }); let columns: { i: number; name: string }[] = []; // pgwire streams rows in chunks. // These chunks can be quite small (as little as 16KB), so we don't flush chunks automatically. @@ -418,7 +407,7 @@ WHERE oid = $1::regclass`, const result = await this.storage.resolveTable({ group_id: this.group_id, connection_id: this.connection_id, - connection_tag: this.connectionTag, + connection_tag: this.connections.connectionTag, entity_descriptor: descriptor, sync_rules: this.sync_rules }); @@ -544,7 +533,7 @@ WHERE oid = $1::regclass`, slot: this.slot_name, options: { proto_version: '1', - publication_names: this.publication_name + publication_names: PUBLICATION_NAME } }); this.startedStreaming = true; diff --git a/modules/module-postgres/src/replication/WalStreamReplicationJob.ts b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts new file mode 100644 index 000000000..3c5731729 --- /dev/null +++ b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts @@ -0,0 +1,155 @@ +import { MissingReplicationSlotError, WalStream } from './WalStream.js'; +import { container } from '@powersync/lib-services-framework'; +import { PgManager } from './PgManager.js'; + +import { replication } from '@powersync/service-core'; +import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; + +export interface WalStreamReplicationJobOptions extends replication.AbstractReplicationJobOptions { + connectionFactory: ConnectionManagerFactory; +} + +export class WalStreamReplicationJob extends replication.AbstractReplicationJob { + private connectionFactory: ConnectionManagerFactory; + private connectionManager: PgManager; + + constructor(options: WalStreamReplicationJobOptions) { + super(options); + this.connectionFactory = options.connectionFactory; + this.connectionManager = this.connectionFactory.create({ + // Pool connections are only used intermittently. + idleTimeout: 30_000, + maxSize: 2 + }); + } + + async cleanUp(): Promise { + this.logger.info(`Cleaning up replication slot: ${this.slot_name}`); + + try { + await this.connectionManager.pool.query({ + statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', + params: [{ type: 'varchar', value: this.slot_name }] + }); + } finally { + await this.connectionManager.end(); + } + } + + /** + * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. + * + * The old WAL files are only deleted once no replication slot still references it. + * + * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot + * advancing**. + * + * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. + * + * **This may be a bug in pgwire or how we're using it. + */ + async keepAlive() { + try { + await this.connectionManager.pool.query(`SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')`); + } catch (e) { + this.logger.warn(`KeepAlive failed, unable to post to WAL`, e); + } + } + + get slot_name() { + return this.options.storage.slot_name; + } + + async replicate() { + try { + await this.replicateLoop(); + } catch (e) { + // Fatal exception + container.reporter.captureException(e, { + metadata: { + replication_slot: this.slot_name + } + }); + this.logger.error(`Replication failed on ${this.slot_name}`, e); + + if (e instanceof MissingReplicationSlotError) { + // This stops replication on this slot, and creates a new slot + await this.options.storage.factory.slotRemoved(this.slot_name); + } + } finally { + this.abortController.abort(); + } + } + + async replicateLoop() { + while (!this.isStopped) { + await this.replicateOnce(); + + if (!this.isStopped) { + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + } + + async replicateOnce() { + // New connections on every iteration (every error with retry), + // otherwise we risk repeating errors related to the connection, + // such as caused by cached PG schemas. + const connectionManager = this.connectionFactory.create({ + // Pool connections are only used intermittently. + idleTimeout: 30_000, + maxSize: 2 + }); + try { + await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); + if (this.isStopped) { + return; + } + const stream = new WalStream({ + abort_signal: this.abortController.signal, + storage: this.options.storage, + connections: this.connectionManager + }); + await stream.replicate(); + } catch (e) { + this.logger.error(`Replication error`, e); + if (e.cause != null) { + // Example: + // PgError.conn_ended: Unable to do postgres query on ended connection + // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) + // at stream.next () + // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) + // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) + // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) + // ... + // cause: TypeError: match is not iterable + // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) + // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) + // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) + // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) + // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) + // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), + // [Symbol(pg.ErrorCode)]: 'conn_ended', + // [Symbol(pg.ErrorResponse)]: undefined + // } + // Without this additional log, the cause would not be visible in the logs. + this.logger.error(`cause`, e.cause); + } + if (e instanceof MissingReplicationSlotError) { + throw e; + } else { + // Report the error if relevant, before retrying + container.reporter.captureException(e, { + metadata: { + replication_slot: this.slot_name + } + }); + // This sets the retry delay + this.rateLimiter?.reportError(e); + } + } finally { + await connectionManager.end(); + } + } +} diff --git a/modules/module-postgres/src/replication/WalStreamReplicator.ts b/modules/module-postgres/src/replication/WalStreamReplicator.ts new file mode 100644 index 000000000..95832ce81 --- /dev/null +++ b/modules/module-postgres/src/replication/WalStreamReplicator.ts @@ -0,0 +1,25 @@ +import { AbstractReplicatorOptions, replication } from '@powersync/service-core'; +import { WalStreamReplicationJob } from './WalStreamReplicationJob.js'; +import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; + +export interface WalStreamReplicatorOptions extends AbstractReplicatorOptions { + connectionFactory: ConnectionManagerFactory; +} + +export class WalStreamReplicator extends replication.AbstractReplicator { + private readonly connectionFactory: ConnectionManagerFactory; + + constructor(options: WalStreamReplicatorOptions) { + super(options); + this.connectionFactory = options.connectionFactory; + } + + createJob(options: replication.CreateJobOptions): WalStreamReplicationJob { + return new WalStreamReplicationJob({ + id: this.createJobId(options.storage.group_id), + storage: options.storage, + connectionFactory: this.connectionFactory, + lock: options.lock + }); + } +} diff --git a/modules/module-postgres/test/src/pg_test.test.ts b/modules/module-postgres/test/src/pg_test.test.ts index e97aea837..866adb3de 100644 --- a/modules/module-postgres/test/src/pg_test.test.ts +++ b/modules/module-postgres/test/src/pg_test.test.ts @@ -2,8 +2,8 @@ import { constructAfterRecord } from '@module/utils/pgwire_utils.js'; import * as pgwire from '@powersync/service-jpgwire'; import { SqliteRow } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; -// import { WalStream } from '../../src/replication/WalStream.js'; import { clearTestDb, connectPgPool, connectPgWire, TEST_URI } from './util.js'; +import { WalStream } from '@module/replication/WalStream.js'; describe('pg data types', () => { async function setupTable(db: pgwire.PgClient) { diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index 6a419d12b..c27dcba88 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -1,16 +1,16 @@ import { ServiceContextContainer } from '../system/ServiceContext.js'; +import { logger } from '@powersync/lib-services-framework'; +import winston from 'winston'; export interface AbstractModuleOptions { name: string; } export abstract class AbstractModule { - public name: string; - protected options: AbstractModuleOptions; + protected logger: winston.Logger; - protected constructor(options: AbstractModuleOptions) { - this.options = options; - this.name = options.name; + protected constructor(protected options: AbstractModuleOptions) { + this.logger = logger.child({ name: `Module:${options.name}` }); } /** @@ -28,4 +28,8 @@ export abstract class AbstractModule { * Terminate and clean up any resources managed by the module right away */ public abstract teardown(): Promise; + + public get name() { + return this.options.name; + } } diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index 4df30c926..42811748e 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -10,17 +10,20 @@ export class ModuleManager { public register(modules: AbstractModule[]) { for (const module of modules) { if (this.modules.has(module.name)) { - logger.warn(`Module ${module.name} already registered, skipping...`); + logger.warn(`Module ${module.name} already registered, skipping.`); continue; } this.modules.set(module.name, module); + logger.info(`Successfully registered Module ${module.name}.`); } } async initialize(serviceContext: system.ServiceContextContainer) { + logger.info(`Initializing modules...`); for (const module of this.modules.values()) { await module.initialize(serviceContext); } + logger.info(`Successfully Initialized modules.`); } async tearDown() { diff --git a/packages/service-core/src/modules/ReplicationModule.ts b/packages/service-core/src/modules/ReplicationModule.ts deleted file mode 100644 index 335ff38cf..000000000 --- a/packages/service-core/src/modules/ReplicationModule.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSyncConfig.js'; -import * as t from 'ts-codec'; - -import * as types from '@powersync/service-types'; -import * as api from '../api/api-index.js'; -import * as modules from './modules-index.js'; -import * as system from '../system/system-index.js'; -import { ReplicationAdapter } from '../replication/ReplicationAdapter.js'; -import { logger, schema } from '@powersync/lib-services-framework'; -import { AbstractReplicator } from '../replication/AbstractReplicator.js'; - -export interface ReplicationModuleOptions extends modules.AbstractModuleOptions { - type: string; - configSchema: t.AnyCodec; -} - -/** - * A replication module describes all the functionality that PowerSync requires to - * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. - */ -export abstract class ReplicationModule extends modules.AbstractModule { - protected type: string; - protected configSchema: t.AnyCodec; - - protected replicationAdapters: Set; - - /** - * @protected - * @param options - */ - protected constructor(protected options: ReplicationModuleOptions) { - super(options); - this.type = options.type; - this.configSchema = options.configSchema; - this.replicationAdapters = new Set(); - } - - /** - * Create the API adapter for the DataSource required by the sync API - * endpoints. - */ - protected abstract createRouteAPIAdapter(decodedConfig: TConfig): api.RouteAPI; - - /** - * Create the ReplicationAdapter to be used by PowerSync replicator. - */ - protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): AbstractReplicator; - - /** - * Register this module's replication adapters and sync API providers if the required configuration is present. - */ - public async initialize(context: system.ServiceContext): Promise { - if (!context.configuration.connections) { - // No data source configuration found in the config skip for now - return; - } - - const matchingConfig = context.configuration.connections.filter((dataSource) => dataSource.type === this.type); - if (!matchingConfig.length) { - // This module is needed given the config - return; - } - - if (matchingConfig.length > 1) { - logger.warning( - `Multiple data sources of type ${this.type} found in the configuration. Only the first will be used.` - ); - } - - try { - const baseMatchingConfig = matchingConfig[0] as TConfig; - // If validation fails, log the error and continue, no replication will happen for this data source - this.validateConfig(baseMatchingConfig); - const decodedConfig = this.configSchema.decode(baseMatchingConfig); - context.replicationEngine.register(this.createReplicator(decodedConfig, context)); - const apiAdapter = this.createRouteAPIAdapter(decodedConfig); - context.routerEngine.registerAPI(apiAdapter); - } catch (e) { - logger.error(e); - } - } - - private validateConfig(config: TConfig): void { - const validator = schema - .parseJSONSchema( - // This generates a schema for the encoded form of the codec - t.generateJSONSchema(this.configSchema, { - allowAdditional: true, - parsers: [types.configFile.portParser] - }) - ) - .validator(); - - const valid = validator.validate(config); - - if (!valid.valid) { - throw new Error(`Failed to validate Module ${this.name} configuration: ${valid.errors.join(', ')}`); - } - } - - protected getDefaultId(dataSourceName: string): string { - return `${this.type}-${dataSourceName}`; - } -} diff --git a/packages/service-core/src/replication/AbstractReplicationJob.ts b/packages/service-core/src/replication/AbstractReplicationJob.ts index a049e4825..0aafef5a5 100644 --- a/packages/service-core/src/replication/AbstractReplicationJob.ts +++ b/packages/service-core/src/replication/AbstractReplicationJob.ts @@ -19,6 +19,9 @@ export abstract class AbstractReplicationJob { this.logger = logger.child({ name: `ReplicationJob: ${options.id}` }); } + /** + * Copy the initial data set from the data source if required and then keep it in sync. + */ abstract replicate(): Promise; /** @@ -44,7 +47,6 @@ export abstract class AbstractReplicationJob { container.reporter.captureException(e, { metadata: { replicator: this.id - // TODO We could allow extra metadata contributed from the adapter here } }); logger.error(`Replication failed.`, e); @@ -60,7 +62,6 @@ export abstract class AbstractReplicationJob { public async stop(): Promise { logger.info(`Stopping ${this.id} replication job for sync rule iteration: ${this.storage.group_id}`); this.abortController.abort(); - // TODO don't worry about exceptions, stopping is the important part await this.isReplicatingPromise; } diff --git a/packages/service-core/src/replication/AbstractReplicator.ts b/packages/service-core/src/replication/AbstractReplicator.ts index 4f15c5e63..7b45021bf 100644 --- a/packages/service-core/src/replication/AbstractReplicator.ts +++ b/packages/service-core/src/replication/AbstractReplicator.ts @@ -6,7 +6,7 @@ import { SyncRulesProvider } from '../util/config/sync-rules/sync-rules-provider import winston from 'winston'; import { AbstractReplicationJob } from './AbstractReplicationJob.js'; import { StorageFactoryProvider } from '../storage/storage-index.js'; -import { DefaultErrorRateLimiter, ErrorRateLimiter } from './ErrorRateLimiter.js'; +import { ErrorRateLimiter } from './ErrorRateLimiter.js'; // 5 minutes const PING_INTERVAL = 1_000_000_000n * 300n; @@ -20,6 +20,10 @@ export interface AbstractReplicatorOptions { id: string; storageFactory: StorageFactoryProvider; syncRuleProvider: SyncRulesProvider; + /** + * This limits the effect of retries when there is a persistent issue. + */ + rateLimiter: ErrorRateLimiter; } /** @@ -40,21 +44,12 @@ export abstract class AbstractReplicator { this.runLoop().catch((e) => { this.logger.error('Data source fatal replication error', e); diff --git a/packages/service-core/src/replication/ReplicationEngine.ts b/packages/service-core/src/replication/ReplicationEngine.ts index 146168221..258c0da1a 100644 --- a/packages/service-core/src/replication/ReplicationEngine.ts +++ b/packages/service-core/src/replication/ReplicationEngine.ts @@ -1,20 +1,9 @@ -import * as storage from '../storage/storage-index.js'; -import * as utils from '../util/util-index.js'; import { AbstractReplicator } from './AbstractReplicator.js'; - -export interface ReplicationEngineOptions { - storage: storage.StorageFactoryProvider; - config: utils.SyncRulesConfig; -} +import { logger } from '@powersync/lib-services-framework'; export class ReplicationEngine { - private readonly options: ReplicationEngineOptions; private readonly replicators: Map = new Map(); - constructor(options: ReplicationEngineOptions) { - this.options = options; - } - /** * Register a Replicator with the engine * @@ -24,6 +13,7 @@ export class ReplicationEngine { if (this.replicators.has(replicator.id)) { throw new Error(`Replicator with id: ${replicator.id} already registered`); } + logger.info(`Successfully registered Replicator ${replicator.id} with ReplicationEngine`); this.replicators.set(replicator.id, replicator); } diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index f61cb39ae..0f58c6ede 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -7,10 +7,7 @@ import { MetricModes, registerMetrics } from '../metrics.js'; */ export const registerReplicationServices = (serviceContext: core.system.ServiceContextContainer) => { // Needs to be executed after shared registrations - const replication = new core.replication.ReplicationEngine({ - config: serviceContext.configuration.sync_rules, - storage: serviceContext.storage - }); + const replication = new core.replication.ReplicationEngine(); serviceContext.register(core.replication.ReplicationEngine, replication); serviceContext.lifeCycleEngine.withLifecycle(replication, { From dfa4c14d4a7f9f2d582e8f4c4ca63273bd6da4ab Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 21 Aug 2024 11:31:01 +0200 Subject: [PATCH 115/298] Removed no longer used Teardown function Moved Replication Module definition to the replication packaged --- modules/module-postgres/src/utils/teardown.ts | 68 ------------ .../src/replication/ErrorRateLimiter.ts | 44 -------- .../src/replication/ReplicationModule.ts | 103 ++++++++++++++++++ .../src/replication/replication-index.ts | 2 +- 4 files changed, 104 insertions(+), 113 deletions(-) delete mode 100644 modules/module-postgres/src/utils/teardown.ts create mode 100644 packages/service-core/src/replication/ReplicationModule.ts diff --git a/modules/module-postgres/src/utils/teardown.ts b/modules/module-postgres/src/utils/teardown.ts deleted file mode 100644 index 69c7b9a5d..000000000 --- a/modules/module-postgres/src/utils/teardown.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { logger } from '@powersync/lib-services-framework'; -import { storage } from '@powersync/service-core'; -import * as timers from 'timers/promises'; -import { ResolvedConnectionConfig } from '../types/types.js'; - -/** - * Terminate all replicating sync rules, deleting the replication slots. - * - * Retries lock and other errors for up to two minutes. - * - * This is a best-effort attempt. In some cases it may not be possible to delete the replication - * slot, such as when the postgres instance is unreachable. - */ -export async function terminateReplicators( - storageFactory: storage.BucketStorageFactory, - connection: ResolvedConnectionConfig -) { - const start = Date.now(); - while (Date.now() - start < 12_000) { - let retry = false; - const replicationRules = await storageFactory.getReplicatingSyncRules(); - for (let syncRules of replicationRules) { - try { - await terminateReplicator(storageFactory, connection, syncRules); - } catch (e) { - retry = true; - console.error(e); - logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); - } - } - if (!retry) { - break; - } - await timers.setTimeout(5_000); - } -} - -/** - * Attempt to terminate a single sync rules instance. - * - * This may fail with a lock error. - */ -async function terminateReplicator( - storageFactory: storage.BucketStorageFactory, - connection: ResolvedConnectionConfig, - syncRules: storage.PersistedSyncRulesContent -) { - // The lock may still be active if the current replication instance - // hasn't stopped yet. - const lock = await syncRules.lock(); - try { - const parsed = syncRules.parsed(); - const storage = storageFactory.getInstance(parsed); - - // const stream = new replication.WalStreamRunner({ - // factory: storageFactory, - // storage: storage, - // source_db: connection, - // lock - // }); - - // logger.info(`Terminating replication slot ${stream.slot_name}`); - // // await stream.terminate(); - // logger.info(`Terminated replication slot ${stream.slot_name}`); - } finally { - await lock.release(); - } -} diff --git a/packages/service-core/src/replication/ErrorRateLimiter.ts b/packages/service-core/src/replication/ErrorRateLimiter.ts index a94cbd9ed..700919ec1 100644 --- a/packages/service-core/src/replication/ErrorRateLimiter.ts +++ b/packages/service-core/src/replication/ErrorRateLimiter.ts @@ -1,50 +1,6 @@ -import { setTimeout } from 'timers/promises'; - export interface ErrorRateLimiter { waitUntilAllowed(options?: { signal?: AbortSignal }): Promise; reportError(e: any): void; mayPing(): boolean; } - -export class DefaultErrorRateLimiter implements ErrorRateLimiter { - nextAllowed: number = Date.now(); - - async waitUntilAllowed(options?: { signal?: AbortSignal | undefined } | undefined): Promise { - const delay = Math.max(0, this.nextAllowed - Date.now()); - // Minimum delay between connections, even without errors - this.setDelay(500); - await setTimeout(delay, undefined, { signal: options?.signal }); - } - - mayPing(): boolean { - return Date.now() >= this.nextAllowed; - } - - reportError(e: any): void { - const message = (e.message as string) ?? ''; - if (message.includes('password authentication failed')) { - // Wait 15 minutes, to avoid triggering Supabase's fail2ban - this.setDelay(900_000); - } else if (message.includes('ENOTFOUND')) { - // DNS lookup issue - incorrect URI or deleted instance - this.setDelay(120_000); - } else if (message.includes('ECONNREFUSED')) { - // Could be fail2ban or similar - this.setDelay(120_000); - } else if ( - message.includes('Unable to do postgres query on ended pool') || - message.includes('Postgres unexpectedly closed connection') - ) { - // Connection timed out - ignore / immediately retry - // We don't explicitly set the delay to 0, since there could have been another error that - // we need to respect. - } else { - this.setDelay(30_000); - } - } - - private setDelay(delay: number) { - this.nextAllowed = Math.max(this.nextAllowed, Date.now() + delay); - } -} diff --git a/packages/service-core/src/replication/ReplicationModule.ts b/packages/service-core/src/replication/ReplicationModule.ts new file mode 100644 index 000000000..ff5c402b3 --- /dev/null +++ b/packages/service-core/src/replication/ReplicationModule.ts @@ -0,0 +1,103 @@ +import { DataSourceConfig } from '@powersync/service-types/dist/config/PowerSyncConfig.js'; +import * as t from 'ts-codec'; + +import * as types from '@powersync/service-types'; +import * as api from '../api/api-index.js'; +import * as modules from '../modules/modules-index.js'; +import * as system from '../system/system-index.js'; +import { ReplicationAdapter } from './ReplicationAdapter.js'; +import { schema } from '@powersync/lib-services-framework'; +import { AbstractReplicator } from './AbstractReplicator.js'; + +export interface ReplicationModuleOptions extends modules.AbstractModuleOptions { + type: string; + configSchema: t.AnyCodec; +} + +/** + * A replication module describes all the functionality that PowerSync requires to + * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. + */ +export abstract class ReplicationModule extends modules.AbstractModule { + protected type: string; + protected configSchema: t.AnyCodec; + protected replicationAdapters: Set; + + /** + * @protected + * @param options + */ + protected constructor(options: ReplicationModuleOptions) { + super(options); + this.type = options.type; + this.configSchema = options.configSchema; + this.replicationAdapters = new Set(); + } + + /** + * Create the API adapter for the DataSource required by the sync API + * endpoints. + */ + protected abstract createRouteAPIAdapter(decodedConfig: TConfig): api.RouteAPI; + + /** + * Create the ReplicationAdapter to be used by PowerSync replicator. + */ + protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): AbstractReplicator; + + /** + * Register this module's replication adapters and sync API providers if the required configuration is present. + */ + public async initialize(context: system.ServiceContext): Promise { + if (!context.configuration.connections) { + // No data source configuration found in the config skip for now + return; + } + + const matchingConfig = context.configuration.connections.filter((dataSource) => dataSource.type === this.type); + if (!matchingConfig.length) { + // This module is needed given the config + return; + } + + if (matchingConfig.length > 1) { + this.logger.warning( + `Multiple data sources of type ${this.type} found in the configuration. Only the first will be used.` + ); + } + + try { + const baseMatchingConfig = matchingConfig[0] as TConfig; + // If validation fails, log the error and continue, no replication will happen for this data source + this.validateConfig(baseMatchingConfig); + const decodedConfig = this.configSchema.decode(baseMatchingConfig); + context.replicationEngine.register(this.createReplicator(decodedConfig, context)); + const apiAdapter = this.createRouteAPIAdapter(decodedConfig); + context.routerEngine.registerAPI(apiAdapter); + } catch (e) { + this.logger.error('Failed to initialize.', e); + } + } + + private validateConfig(config: TConfig): void { + const validator = schema + .parseJSONSchema( + // This generates a schema for the encoded form of the codec + t.generateJSONSchema(this.configSchema, { + allowAdditional: true, + parsers: [types.configFile.portParser] + }) + ) + .validator(); + + const valid = validator.validate(config); + + if (!valid.valid) { + throw new Error(`Failed to validate Module ${this.name} configuration: ${valid.errors.join(', ')}`); + } + } + + protected getDefaultId(dataSourceName: string): string { + return `${this.type}-${dataSourceName}`; + } +} diff --git a/packages/service-core/src/replication/replication-index.ts b/packages/service-core/src/replication/replication-index.ts index 4859e245d..ecf2fbb63 100644 --- a/packages/service-core/src/replication/replication-index.ts +++ b/packages/service-core/src/replication/replication-index.ts @@ -4,4 +4,4 @@ export * from './AbstractReplicator.js'; export * from './ReplicationAdapter.js'; export * from './ReplicationEngine.js'; export * from './ReplicationEntity.js'; -export * from '../modules/ReplicationModule.js'; +export * from './ReplicationModule.js'; From 2e1725d9af52f8ef92255dd7cd848d956d10fe15 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 21 Aug 2024 11:41:50 +0200 Subject: [PATCH 116/298] Fixed outdated comments and exporting only used Replication classes --- packages/service-core/src/replication/ReplicationModule.ts | 5 +---- packages/service-core/src/replication/replication-index.ts | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/service-core/src/replication/ReplicationModule.ts b/packages/service-core/src/replication/ReplicationModule.ts index ff5c402b3..39bb337be 100644 --- a/packages/service-core/src/replication/ReplicationModule.ts +++ b/packages/service-core/src/replication/ReplicationModule.ts @@ -5,7 +5,6 @@ import * as types from '@powersync/service-types'; import * as api from '../api/api-index.js'; import * as modules from '../modules/modules-index.js'; import * as system from '../system/system-index.js'; -import { ReplicationAdapter } from './ReplicationAdapter.js'; import { schema } from '@powersync/lib-services-framework'; import { AbstractReplicator } from './AbstractReplicator.js'; @@ -21,7 +20,6 @@ export interface ReplicationModuleOptions extends modules.AbstractModuleOptions export abstract class ReplicationModule extends modules.AbstractModule { protected type: string; protected configSchema: t.AnyCodec; - protected replicationAdapters: Set; /** * @protected @@ -31,7 +29,6 @@ export abstract class ReplicationModule extend super(options); this.type = options.type; this.configSchema = options.configSchema; - this.replicationAdapters = new Set(); } /** @@ -41,7 +38,7 @@ export abstract class ReplicationModule extend protected abstract createRouteAPIAdapter(decodedConfig: TConfig): api.RouteAPI; /** - * Create the ReplicationAdapter to be used by PowerSync replicator. + * Create the Replicator to be used by the ReplicationEngine. */ protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): AbstractReplicator; diff --git a/packages/service-core/src/replication/replication-index.ts b/packages/service-core/src/replication/replication-index.ts index ecf2fbb63..455ca913b 100644 --- a/packages/service-core/src/replication/replication-index.ts +++ b/packages/service-core/src/replication/replication-index.ts @@ -1,7 +1,5 @@ export * from './ErrorRateLimiter.js'; export * from './AbstractReplicationJob.js'; export * from './AbstractReplicator.js'; -export * from './ReplicationAdapter.js'; export * from './ReplicationEngine.js'; -export * from './ReplicationEntity.js'; export * from './ReplicationModule.js'; From dd3c5686f822d384c1eeede98d7427dab424c219 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 21 Aug 2024 13:34:25 +0200 Subject: [PATCH 117/298] Updated and re-enabled postgres tests --- modules/module-postgres/package.json | 4 ++- .../test/src/slow_tests.test.ts | 18 +++++----- modules/module-postgres/test/src/util.ts | 35 ++++++++++++++++++- .../test/src/wal_stream_utils.ts | 25 ++++++------- 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index f83742c66..7ee37df49 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -11,7 +11,8 @@ "type": "module", "scripts": { "build": "tsc -b", - "clean": "rm -rf ./lib && tsc -b --clean" + "clean": "rm -rf ./lib && tsc -b --clean", + "test": "vitest --no-threads" }, "exports": { ".": { @@ -33,6 +34,7 @@ "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87", + "mongodb": "^6.7.0", "jose": "^4.15.1", "ts-codec": "^1.2.2", "uuid": "^9.0.1", diff --git a/modules/module-postgres/test/src/slow_tests.test.ts b/modules/module-postgres/test/src/slow_tests.test.ts index f3e1a3d5d..aea33acf9 100644 --- a/modules/module-postgres/test/src/slow_tests.test.ts +++ b/modules/module-postgres/test/src/slow_tests.test.ts @@ -2,17 +2,17 @@ import * as bson from 'bson'; import * as mongo from 'mongodb'; import { afterEach, describe, expect, test } from 'vitest'; import { WalStream, WalStreamOptions } from '../../src/replication/WalStream.js'; -import { getClientCheckpoint } from '../../src/util/utils.js'; import { env } from './env.js'; -import { MONGO_STORAGE_FACTORY, StorageFactory, TEST_CONNECTION_OPTIONS, clearTestDb, connectPgPool } from './util.js'; +import { TEST_CONNECTION_OPTIONS, clearTestDb, connectPgPool, getClientCheckpoint } from './util.js'; import * as pgwire from '@powersync/service-jpgwire'; import { SqliteRow } from '@powersync/service-sync-rules'; -import { MongoBucketStorage } from '../../src/storage/MongoBucketStorage.js'; -import { PgManager } from '../../src/util/PgManager.js'; -import { mapOpEntry } from '@/storage/storage-index.js'; -import { reduceBucket, validateCompactedBucket, validateBucket } from './bucket_validation.js'; + +import { mapOpEntry, MongoBucketStorage } from '@/storage/storage-index.js'; import * as timers from 'node:timers/promises'; +import { MONGO_STORAGE_FACTORY, StorageFactory } from '@core-tests/util.js'; +import { PgManager } from '@module/replication/PgManager.js'; +import { reduceBucket, validateCompactedBucket } from '@core-tests/bucket_validation.js'; describe('slow tests - mongodb', function () { // These are slow, inconsistent tests. @@ -88,8 +88,7 @@ bucket_definitions: const options: WalStreamOptions = { abort_signal: abortController.signal, connections, - storage: storage, - factory: f + storage: storage }; walStream = new WalStream(options); @@ -266,8 +265,7 @@ bucket_definitions: const options: WalStreamOptions = { abort_signal: abortController.signal, connections, - storage: storage, - factory: f + storage: storage }; walStream = new WalStream(options); diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 4ef8f9945..38e9c12bb 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -1,8 +1,10 @@ import * as types from '@module/types/types.js'; import * as pg_utils from '@module/utils/pgwire_utils.js'; -import { Metrics } from '@powersync/service-core'; +import { BucketStorageFactory, Metrics, OpId } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import { env } from './env.js'; +import { pgwireRows } from '@powersync/service-jpgwire'; +import { logger } from '@powersync/lib-services-framework'; // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -54,3 +56,34 @@ export function connectPgPool() { const db = pgwire.connectPgWirePool(TEST_CONNECTION_OPTIONS); return db; } + +export async function getClientCheckpoint( + db: pgwire.PgClient, + bucketStorage: BucketStorageFactory, + options?: { timeout?: number } +): Promise { + const start = Date.now(); + + const [{ lsn }] = pgwireRows(await db.query(`SELECT pg_logical_emit_message(false, 'powersync', 'ping') as lsn`)); + + // This old API needs a persisted checkpoint id. + // Since we don't use LSNs anymore, the only way to get that is to wait. + + const timeout = options?.timeout ?? 50_000; + + logger.info(`Waiting for LSN checkpoint: ${lsn}`); + while (Date.now() - start < timeout) { + const cp = await bucketStorage.getActiveCheckpoint(); + if (!cp.hasSyncRules()) { + throw new Error('No sync rules available'); + } + if (cp.lsn >= lsn) { + logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`); + return cp.checkpoint; + } + + await new Promise((resolve) => setTimeout(resolve, 30)); + } + + throw new Error('Timeout while waiting for checkpoint'); +} diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index 0cae43030..6e855fd37 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -1,7 +1,9 @@ import { BucketStorageFactory, SyncRulesBucketStorage } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; -import { getClientCheckpoint } from '../../src/util/utils.js'; -import { TEST_CONNECTION_OPTIONS, clearTestDb } from './util.js'; +import { TEST_CONNECTION_OPTIONS, clearTestDb, getClientCheckpoint } from './util.js'; +import { WalStream, WalStreamOptions } from '@module/replication/WalStream.js'; +import { fromAsync } from '@core-tests/stream_utils.js'; +import { PgManager } from '@module/replication/PgManager.js'; /** * Tests operating on the wal stream need to configure the stream and manage asynchronous @@ -15,10 +17,10 @@ export function walStreamTest( ): () => Promise { return async () => { const f = await factory(); - const pool = pgwire.connectPgWirePool(TEST_CONNECTION_OPTIONS, {}); + const connectionManager = new PgManager(TEST_CONNECTION_OPTIONS, {}); - await clearTestDb(pool); - const context = new WalStreamTestContext(f, pool); + await clearTestDb(connectionManager.pool); + const context = new WalStreamTestContext(f, connectionManager); try { await test(context); } finally { @@ -34,16 +36,16 @@ export class WalStreamTestContext { public storage?: SyncRulesBucketStorage; private replicationConnection?: pgwire.PgConnection; - constructor(public factory: BucketStorageFactory, public connections: pgwire.PgClient) {} + constructor(public factory: BucketStorageFactory, public connectionManager: PgManager) {} async dispose() { this.abortController.abort(); await this.streamPromise; - this.connections.destroy(); + await this.connectionManager.destroy(); } get pool() { - return this.connections; + return this.connectionManager.pool; } async updateSyncRules(content: string) { @@ -61,8 +63,7 @@ export class WalStreamTestContext { } const options: WalStreamOptions = { storage: this.storage, - factory: this.factory, - connections: this.connections, + connections: this.connectionManager, abort_signal: this.abortController.signal }; this._walStream = new WalStream(options); @@ -70,7 +71,7 @@ export class WalStreamTestContext { } async replicateSnapshot() { - this.replicationConnection = await this.connections.replicationConnection(); + this.replicationConnection = await this.connectionManager.replicationConnection(); await this.walStream.initReplication(this.replicationConnection); await this.storage!.autoActivate(); } @@ -84,7 +85,7 @@ export class WalStreamTestContext { async getCheckpoint(options?: { timeout?: number }) { let checkpoint = await Promise.race([ - getClientCheckpoint(this.connections.pool, this.factory, { timeout: options?.timeout ?? 15_000 }), + getClientCheckpoint(this.pool, this.factory, { timeout: options?.timeout ?? 15_000 }), this.streamPromise ]); if (typeof checkpoint == undefined) { From 2e552c42cb47d03f133b1ffd5fa2f09464e55128 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 21 Aug 2024 13:39:13 +0200 Subject: [PATCH 118/298] Updated lock file --- pnpm-lock.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd0ec2222..00d2a1f1a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,6 +117,9 @@ importers: jose: specifier: ^4.15.1 version: 4.15.9 + mongodb: + specifier: ^6.7.0 + version: 6.8.0(socks@2.8.3) pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 @@ -4522,7 +4525,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4530,7 +4533,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) From ec4f71d0650e77ec42f87a56e7afdb456438d335 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 21 Aug 2024 18:19:24 +0200 Subject: [PATCH 119/298] more hacks to get writerows to work --- modules/module-mysql/package.json | 6 +- .../src/api/MySQLRouteAPIAdapter.ts | 3 +- .../src/replication/MysqlBinLogStream.ts | 197 +++++++++++------- .../src/utils/replication/GTID.ts | 94 ++++----- .../src/utils/replication/head.ts | 3 +- pnpm-lock.yaml | 57 +---- 6 files changed, 182 insertions(+), 178 deletions(-) diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 23bb0f21d..05ba74115 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -28,14 +28,16 @@ "@powersync/service-jsonbig": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@vlasky/zongji": "^0.5.9", + "@vlasky/zongji": "link:../../../zongji", + "async": "^3.2.4", "mysql2": "^3.11.0", "ts-codec": "^1.2.2", "uri-js": "^4.4.1", "uuid": "^9.0.1" }, "devDependencies": { - "@types/uuid": "^9.0.4", + "@types/async": "^3.2.24", + "@types/uuid": "^9.0.8", "typescript": "^5.2.2", "vite-tsconfig-paths": "^4.3.2", "vitest": "^0.34.6" diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 7fc781ff7..784951892 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -128,7 +128,8 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { * The default value of `public` is not valid. * We might need to implement this better where the original table patterns are created. */ - const mappedPatterns = tablePatterns.map((t) => new sync_rules.TablePattern(this.config.database, t.tablePattern)); + const mappedPatterns = tablePatterns; + // .map((t) => new sync_rules.TablePattern(this.config.database, t.tablePattern)); for (let tablePattern of mappedPatterns) { const schema = tablePattern.schema; diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index e658a8a74..5306484c6 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -1,5 +1,6 @@ import { logger } from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; +import async from 'async'; import { storage } from '@powersync/service-core'; @@ -335,10 +336,9 @@ AND table_type = 'BASE TABLE';`, } async initialReplication(db: mysql.Pool, gtid: replication_utils.ReplicatedGTID) { - // TODO fix database - const sourceTables = this.sync_rules - .getSourceTables() - .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); + const sourceTables = this.sync_rules.getSourceTables(); + // TODO fix database default schema if not provided explicitly in the sync rules + // .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); await this.storage.startBatch({}, async (batch) => { for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, db, tablePattern); @@ -376,25 +376,23 @@ AND table_type = 'BASE TABLE';`, await batch.flush(); } - private getTable(relationId: MysqlRelId): storage.SourceTable { - const resolvedRelationId = getMysqlRelId(relationId); - const table = this.relation_cache.get(resolvedRelationId); - if (table == null) { - // We should always receive a replication message before the relation is used. - // If we can't find it, it's a bug. - throw new Error(`Missing relation cache for ${resolvedRelationId}`); + async writeChange( + batch: storage.BucketStorageBatch, + msg: { + type: EventType; + data: Data; + previous_data?: Data; + database: string; + table: string; + sourceTable: storage.SourceTable; } - return table; - } - - async writeChange(batch: storage.BucketStorageBatch, msg: DatabaseEvent): Promise { + ): Promise { if (msg.type == 'insert' || msg.type == 'update' || msg.type == 'delete') { - const table = this.getTable({ schema: msg.database, name: msg.table }); if (msg.type == 'insert') { // rows_replicated_total.add(1); return await batch.save({ tag: 'insert', - sourceTable: table, + sourceTable: msg.sourceTable, before: undefined, after: this.transformMysqlToSynRulesRow(msg.data) }); @@ -402,15 +400,15 @@ AND table_type = 'BASE TABLE';`, // rows_replicated_total.add(1); return await batch.save({ tag: 'update', - sourceTable: table, - before: msg.old ? this.transformMysqlToSynRulesRow(msg.old) : undefined, + sourceTable: msg.sourceTable, + before: msg.previous_data ? this.transformMysqlToSynRulesRow(msg.previous_data) : undefined, after: this.transformMysqlToSynRulesRow(msg.data) }); } else if (msg.type == 'delete') { // rows_replicated_total.add(1); return await batch.save({ tag: 'delete', - sourceTable: table, + sourceTable: msg.sourceTable, before: this.transformMysqlToSynRulesRow(msg.data), after: undefined }); @@ -440,10 +438,9 @@ AND table_type = 'BASE TABLE';`, } private async loadTables() { + const sourceTables = this.sync_rules.getSourceTables(); // TODO fix database - const sourceTables = this.sync_rules - .getSourceTables() - .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); + // .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); await this.storage.startBatch({}, async (batch) => { for (let tablePattern of sourceTables) { await this.getQualifiedTableNames(batch, this.pool, tablePattern); @@ -459,66 +456,110 @@ AND table_type = 'BASE TABLE';`, const fromGTID = checkpoint_lsn ? replication_utils.ReplicatedGTID.fromSerialized(checkpoint_lsn) : await replication_utils.readMasterGtid(this.pool); - const binLogPositionState = fromGTID.nextPosition!; + const binLogPositionState = fromGTID.position; - const zongji = new ZongJi({ - host: this.options.connection_config.hostname, - user: this.options.connection_config.username, - password: this.options.connection_config.password - }); + await this.storage.startBatch({}, async (batch) => { + const zongji = new ZongJi({ + host: this.options.connection_config.hostname, + user: this.options.connection_config.username, + password: this.options.connection_config.password + }); - zongji.on('binlog', async (evt: any) => { - console.log(evt, evt.getEventName()); - - // State machine - switch (evt.getEventName()) { - case 'unknown': - // This is probably the GTID event - break; - case 'rotate': - // Update the position - binLogPositionState.filename = evt.binlogName; - binLogPositionState.offset = evt.position; - break; - case 'writerows': - break; - case 'updaterows': - break; - case 'deleterows': - break; - case 'xid': - // Need to commit - break; - } - // logger.info(`mysqlbinlogstream: received message: '${evt}'`); - // let parsedMsg: DatabaseEvent = JSON.parse(evt); - // logger.info(`mysqlbinlogstream: commit: '${parsedMsg.commit}' xoffset:'${parsedMsg.xoffset}'`); - // // await this.writeChange(batch, parsedMsg); - - // const gtid = parsedMsg.gtid; //gtidMakeComparable(parsedMsg.gtid); - // if (parsedMsg.commit) { - // // await batch.commit(gtid); - // } - // logger.info(`${this.slot_name} replicating op GTID: '${gtid}'`); - // chunks_replicated_total.add(1); - }); + let currentGTID: replication_utils.ReplicatedGTID | null = null; + + const queue = async.queue(async (evt: any) => { + console.log(evt, evt.getEventName()); + + // State machine + switch (evt.getEventName()) { + case 'gtidlog': + currentGTID = replication_utils.ReplicatedGTID.fromBinLogEvent({ + raw_gtid: { + server_id: evt.serverId, + transaction_range: evt.transactionRange + }, + position: { + filename: binLogPositionState.filename, + offset: evt.nextPosition + } + }); + break; + case 'rotate': + // Update the position + binLogPositionState.filename = evt.binlogName; + binLogPositionState.offset = evt.position; + break; + case 'writerows': + const tableInfo = evt.tableMap[evt.tableId]; + await this.writeChange(batch, { + type: 'insert', + data: evt.rows, + database: tableInfo.parentSchema, + table: tableInfo.tableName, + // TODO cleanup + sourceTable: ( + await this.storage.resolveTable({ + connection_id: this.connection_id, + connection_tag: this.connectionTag, + entity_descriptor: { + name: tableInfo.tableName, + objectId: evt.tableId, + schema: tableInfo.parentSchema, + replicationColumns: tableInfo.columns.map((c: any, index: number) => ({ + name: c.name, + type: tableInfo.columnSchemas[index].COLUMN_TYPE + })) + }, + group_id: this.group_id, + sync_rules: this.sync_rules + }) + ).table + }); + break; + case 'updaterows': + break; + case 'deleterows': + break; + case 'xid': + // Need to commit + await batch.commit(currentGTID!.comparable); + currentGTID = null; + break; + } + // chunks_replicated_total.add(1); + }, 1); - zongji.start({ - includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'unknown'], - filename: binLogPositionState.filename, - position: binLogPositionState.offset - }); + zongji.on('binlog', (evt: any) => { + queue.push(evt); + }); + + zongji.start({ + includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'], + excludeEvents: [], + filename: binLogPositionState.filename, + position: binLogPositionState.offset + }); - // Forever young - await new Promise((r) => { - this.abort_signal.addEventListener( - 'abort', - () => { + // Forever young + await new Promise((resolve, reject) => { + queue.error((error) => { zongji.stop(); - r(); - }, - { once: true } - ); + queue.kill(); + reject(error); + }); + this.abort_signal.addEventListener( + 'abort', + async () => { + zongji.stop(); + queue.kill(); + if (!queue.length) { + await queue.drain(); + } + resolve(); + }, + { once: true } + ); + }); }); // // Replication never starts in the middle of a transaction diff --git a/modules/module-mysql/src/utils/replication/GTID.ts b/modules/module-mysql/src/utils/replication/GTID.ts index b0e54b68c..a48f5b216 100644 --- a/modules/module-mysql/src/utils/replication/GTID.ts +++ b/modules/module-mysql/src/utils/replication/GTID.ts @@ -1,6 +1,6 @@ import mysql from 'mysql2/promise'; +import * as uuid from 'uuid'; import { retriedQuery } from '../mysql_utils.js'; - export type BinLogPosition = { filename: string; offset: number; @@ -9,13 +9,19 @@ export type BinLogPosition = { export type ReplicatedGTIDSpecification = { raw_gtid: string; /** - * The position in a BinLog file where this transaction has been replicated in. - */ - replicated_position?: BinLogPosition; - /** - * The position to start from for the next replication event. + * The (end) position in a BinLog file where this transaction has been replicated in. */ - next_position?: BinLogPosition; + position: BinLogPosition; +}; + +export type BinLogGTIDFormat = { + server_id: Buffer; + transaction_range: number; +}; + +export type BinLogGTIDEvent = { + raw_gtid: BinLogGTIDFormat; + position: BinLogPosition; }; /** @@ -26,23 +32,18 @@ export type ReplicatedGTIDSpecification = { export class ReplicatedGTID { static deserialize(comparable: string): ReplicatedGTIDSpecification { const components = comparable.split('|'); - const currentPosition: BinLogPosition | undefined = components[2] - ? { - filename: components[2], - offset: parseInt(components[3]) - } - : undefined; - - const nextPosition: BinLogPosition | undefined = components[4] - ? { - filename: components[4], - offset: parseInt(components[5]) - } - : undefined; + if (components.length < 3) { + throw new Error(`Invalid serialized GTID: ${comparable}`); + } + + const position: BinLogPosition = { + filename: components[2], + offset: parseInt(components[3]) + }; + return { raw_gtid: components[1], - replicated_position: currentPosition, - next_position: nextPosition + position }; } @@ -50,25 +51,27 @@ export class ReplicatedGTID { return new ReplicatedGTID(ReplicatedGTID.deserialize(comparable)); } + static fromBinLogEvent(event: BinLogGTIDEvent) { + const { raw_gtid, position } = event; + const stringGTID = `${uuid.stringify(raw_gtid.server_id)}:${raw_gtid.transaction_range}`; + return new ReplicatedGTID({ + raw_gtid: stringGTID, + position + }); + } + /** * Special case for the zero GTID which means no transactions have been executed. */ - static ZERO = new ReplicatedGTID({ raw_gtid: '0:0' }); + static ZERO = new ReplicatedGTID({ raw_gtid: '0:0', position: { filename: '', offset: 0 } }); constructor(protected options: ReplicatedGTIDSpecification) {} /** - * Get the BinLog position of this GTID event if it has been replicated already. - */ - get replicatedPosition() { - return this.options.replicated_position; - } - - /** - * Get the BinLog position of the next replication event + * Get the BinLog position of this replicated GTID event */ - get nextPosition() { - return this.options.next_position; + get position() { + return this.options.position; } /** @@ -85,10 +88,10 @@ export class ReplicatedGTID { * * @param gtid - The GTID string in the format `server_id:transaction_ranges` * @returns A comparable string in the format - * `padded_end_transaction|raw_gtid|current_binlog_filename|current_binlog_position|next_binlog_filename|next_binlog_position` + * `padded_end_transaction|raw_gtid|binlog_filename|binlog_position` */ get comparable() { - const { raw, replicatedPosition: currentPosition, nextPosition } = this; + const { raw, position } = this; const [, transactionRanges] = this.raw.split(':'); let maxTransactionId = 0; @@ -99,14 +102,11 @@ export class ReplicatedGTID { } const paddedTransactionId = maxTransactionId.toString().padStart(16, '0'); - return [ - paddedTransactionId, - raw, - currentPosition?.filename ?? '', - currentPosition?.offset ?? '', - nextPosition?.filename ?? '', - nextPosition?.offset ?? '' - ].join('|'); + return [paddedTransactionId, raw, position.filename, position.offset].join('|'); + } + + toString() { + return this.comparable; } /** @@ -120,7 +120,7 @@ export class ReplicatedGTID { // Default to the first file for the start to handle the zero GTID case. const startFileIndex = Math.max( - logFiles.findIndex((f) => f['Log_name'] == this.replicatedPosition?.filename), + logFiles.findIndex((f) => f['Log_name'] == this.position.filename), 0 ); const startFileEntry = logFiles[startFileIndex]; @@ -132,7 +132,7 @@ export class ReplicatedGTID { /** * Fall back to the next position for comparison if the replicated position is not present */ - const endPosition = to.replicatedPosition ?? to.nextPosition; + const endPosition = to.position; // Default to the past the last file to cater for the HEAD case const testEndFileIndex = logFiles.findIndex((f) => f['Log_name'] == endPosition?.filename); @@ -147,9 +147,9 @@ export class ReplicatedGTID { return ( startFileEntry['File_size'] - - (this?.replicatedPosition?.offset ?? 0) - + this.position.offset - endFileEntry['File_size'] + - (endPosition?.offset ?? 0) + + endPosition.offset + logFiles.slice(startFileIndex + 1, endFileIndex).reduce((sum, file) => sum + file['File_size'], 0) ); } diff --git a/modules/module-mysql/src/utils/replication/head.ts b/modules/module-mysql/src/utils/replication/head.ts index 6c8e5d1aa..83d4a2ec5 100644 --- a/modules/module-mysql/src/utils/replication/head.ts +++ b/modules/module-mysql/src/utils/replication/head.ts @@ -21,8 +21,7 @@ export async function readMasterGtid(db: mysql.Pool): Promise { return new ReplicatedGTID({ // The head always points to the next position to start replication from - replicated_position: position, - next_position: position, + position, raw_gtid: gtidResult.GTID }); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf61fda90..21415ba57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -112,8 +112,11 @@ importers: specifier: workspace:* version: link:../../packages/types '@vlasky/zongji': - specifier: ^0.5.9 - version: 0.5.9 + specifier: link:../../../zongji + version: link:../../../zongji + async: + specifier: ^3.2.4 + version: 3.2.5 mysql2: specifier: ^3.11.0 version: 3.11.0 @@ -127,8 +130,11 @@ importers: specifier: ^9.0.1 version: 9.0.1 devDependencies: + '@types/async': + specifier: ^3.2.24 + version: 3.2.24 '@types/uuid': - specifier: ^9.0.4 + specifier: ^9.0.8 version: 9.0.8 typescript: specifier: ^5.2.2 @@ -1352,14 +1358,6 @@ packages: '@vitest/utils@0.34.6': resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} - '@vlasky/mysql@2.18.6': - resolution: {integrity: sha512-c+qz/zzqecteQLchoje0E0rjLla935d6hHPpMKmfyQJnHlycLpR49ekS6s/zUAt8w0Um5hFglKXm4+PeJTVhaQ==} - engines: {node: '>= 0.6'} - - '@vlasky/zongji@0.5.9': - resolution: {integrity: sha512-B4pz9a8QJw7hOOcfxtuT26PSVcQvGHlZcXG34T5jh/zjJZJ4/7pW7mioW6Cyxfl/mv/7Rcow+hVgfYpCTAjTMw==} - engines: {node: '>= 8.0'} - abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1516,13 +1514,6 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - big-integer@1.6.51: - resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} - engines: {node: '>=0.6'} - - bignumber.js@9.1.1: - resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} - binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -2993,9 +2984,6 @@ packages: readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} - readable-stream@2.3.7: - resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} - readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -4792,19 +4780,6 @@ snapshots: loupe: 2.3.7 pretty-format: 29.7.0 - '@vlasky/mysql@2.18.6': - dependencies: - bignumber.js: 9.1.1 - readable-stream: 2.3.7 - safe-buffer: 5.2.1 - sqlstring: 2.3.3 - - '@vlasky/zongji@0.5.9': - dependencies: - '@vlasky/mysql': 2.18.6 - big-integer: 1.6.51 - iconv-lite: 0.6.3 - abbrev@1.1.1: {} abort-controller@3.0.0: @@ -4940,10 +4915,6 @@ snapshots: dependencies: is-windows: 1.0.2 - big-integer@1.6.51: {} - - bignumber.js@9.1.1: {} - binary-extensions@2.3.0: {} bl@4.1.0: @@ -6570,16 +6541,6 @@ snapshots: isarray: 0.0.1 string_decoder: 0.10.31 - readable-stream@2.3.7: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 From 7155d40300a59ef93ed47bd91d190f56e6980759 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 10:51:53 +0200 Subject: [PATCH 120/298] wip: before implementing update --- .../src/replication/MysqlBinLogStream.ts | 95 +++++++++++-------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index 5306484c6..46e29fec0 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -376,6 +376,26 @@ AND table_type = 'BASE TABLE';`, await batch.flush(); } + async writeChanges( + batch: storage.BucketStorageBatch, + msg: { + type: EventType; + data: Data[]; + previous_data?: Data; + database: string; + table: string; + sourceTable: storage.SourceTable; + } + ): Promise { + for (const row of msg.data) { + await this.writeChange(batch, { + ...msg, + data: row + }); + } + return null; + } + async writeChange( batch: storage.BucketStorageBatch, msg: { @@ -468,7 +488,8 @@ AND table_type = 'BASE TABLE';`, let currentGTID: replication_utils.ReplicatedGTID | null = null; const queue = async.queue(async (evt: any) => { - console.log(evt, evt.getEventName()); + console.log(evt.getEventName()); + const tableInfo = evt.tableMap?.[evt.tableId]; // State machine switch (evt.getEventName()) { @@ -490,8 +511,7 @@ AND table_type = 'BASE TABLE';`, binLogPositionState.offset = evt.position; break; case 'writerows': - const tableInfo = evt.tableMap[evt.tableId]; - await this.writeChange(batch, { + await this.writeChanges(batch, { type: 'insert', data: evt.rows, database: tableInfo.parentSchema, @@ -519,10 +539,43 @@ AND table_type = 'BASE TABLE';`, case 'updaterows': break; case 'deleterows': + await this.writeChanges(batch, { + type: 'delete', + data: evt.rows, + database: tableInfo.parentSchema, + table: tableInfo.tableName, + // TODO cleanup + sourceTable: ( + await this.storage.resolveTable({ + connection_id: this.connection_id, + connection_tag: this.connectionTag, + entity_descriptor: { + name: tableInfo.tableName, + objectId: evt.tableId, + schema: tableInfo.parentSchema, + replicationColumns: tableInfo.columns.map((c: any, index: number) => ({ + name: c.name, + type: tableInfo.columnSchemas[index].COLUMN_TYPE + })) + }, + group_id: this.group_id, + sync_rules: this.sync_rules + }) + ).table + }); + break; break; case 'xid': - // Need to commit - await batch.commit(currentGTID!.comparable); + // Need to commit with a replicated GTID with updated next position + await batch.commit( + new replication_utils.ReplicatedGTID({ + raw_gtid: currentGTID!.raw, + position: { + filename: binLogPositionState.filename, + offset: evt.nextPosition + } + }).comparable + ); currentGTID = null; break; } @@ -561,37 +614,5 @@ AND table_type = 'BASE TABLE';`, ); }); }); - - // // Replication never starts in the middle of a transaction - // const finished = new Promise((resolve, reject) => { - // readStream.on('data', async (chunk: string) => { - // await touch(); - - // if (this.abort_signal.aborted) { - // logger.info('Aborted, closing stream'); - // readStream.close(); - // deleteFifoPipe(fifoPath); - // resolve(); - // return; - // } - // for (const msg of chunk.split('\n')) { - // if (msg.trim()) { - // logger.info(`mysqlbinlogstream: received message: '${msg}'`); - // let parsedMsg: DatabaseEvent = JSON.parse(msg); - // logger.info(`mysqlbinlogstream: commit: '${parsedMsg.commit}' xoffset:'${parsedMsg.xoffset}'`); - // await this.writeChange(batch, parsedMsg); - // const gtid = gtidMakeComparable(parsedMsg.gtid); - // if (parsedMsg.commit) { - // await batch.commit(gtid); - // } - // micro.logger.info(`${this.slot_name} replicating op GTID: '${gtid}'`); - // chunks_replicated_total.add(1); - // } - // } - // }); - // }); - - // await finished; - // }); } } From df50b5ce3e6e822b1c3d9b14b0a6ee9b1b06364c Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 13:14:14 +0200 Subject: [PATCH 121/298] added zongji types --- .../src/replication/MysqlBinLogStream.ts | 94 +++++++++++------- .../src/replication/zongji/zongji-utils.ts | 32 ++++++ .../src/replication/zongji/zongji.d.ts | 98 +++++++++++++++++++ modules/module-mysql/tsconfig.json | 3 +- 4 files changed, 193 insertions(+), 34 deletions(-) create mode 100644 modules/module-mysql/src/replication/zongji/zongji-utils.ts create mode 100644 modules/module-mysql/src/replication/zongji/zongji.d.ts diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index 46e29fec0..9da1ae7c0 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -3,12 +3,13 @@ import * as sync_rules from '@powersync/service-sync-rules'; import async from 'async'; import { storage } from '@powersync/service-core'; - import mysql, { RowDataPacket } from 'mysql2/promise'; -// @ts-expect-error -import ZongJi from '@vlasky/zongji'; +import './zongji/zongji.js'; + +import ZongJi, { BinLogEvent } from '@vlasky/zongji'; import { NormalizedMySQLConnectionConfig } from '../types/types.js'; import * as replication_utils from '../utils/replication/replication-utils.js'; +import * as zongji_utils from './zongji/zongji-utils.js'; export interface BinLogStreamOptions { pool: mysql.Pool; @@ -381,16 +382,17 @@ AND table_type = 'BASE TABLE';`, msg: { type: EventType; data: Data[]; - previous_data?: Data; + previous_data?: Data[]; database: string; table: string; sourceTable: storage.SourceTable; } ): Promise { - for (const row of msg.data) { + for (const [index, row] of msg.data.entries()) { await this.writeChange(batch, { ...msg, - data: row + data: row, + previous_data: msg.previous_data?.[index] }); } return null; @@ -459,8 +461,6 @@ AND table_type = 'BASE TABLE';`, private async loadTables() { const sourceTables = this.sync_rules.getSourceTables(); - // TODO fix database - // .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); await this.storage.startBatch({}, async (batch) => { for (let tablePattern of sourceTables) { await this.getQualifiedTableNames(batch, this.pool, tablePattern); @@ -487,13 +487,10 @@ AND table_type = 'BASE TABLE';`, let currentGTID: replication_utils.ReplicatedGTID | null = null; - const queue = async.queue(async (evt: any) => { - console.log(evt.getEventName()); - const tableInfo = evt.tableMap?.[evt.tableId]; - + const queue = async.queue(async (evt: BinLogEvent) => { // State machine - switch (evt.getEventName()) { - case 'gtidlog': + switch (true) { + case zongji_utils.eventIsGTIDLog(evt): currentGTID = replication_utils.ReplicatedGTID.fromBinLogEvent({ raw_gtid: { server_id: evt.serverId, @@ -505,29 +502,32 @@ AND table_type = 'BASE TABLE';`, } }); break; - case 'rotate': + case zongji_utils.eventIsRotation(evt): // Update the position binLogPositionState.filename = evt.binlogName; binLogPositionState.offset = evt.position; break; - case 'writerows': + case zongji_utils.eventIsWriteMutation(evt): + // TODO, can multiple tables be present? + const writeTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { type: 'insert', data: evt.rows, - database: tableInfo.parentSchema, - table: tableInfo.tableName, + database: writeTableInfo.parentSchema, + table: writeTableInfo.tableName, // TODO cleanup sourceTable: ( await this.storage.resolveTable({ connection_id: this.connection_id, connection_tag: this.connectionTag, entity_descriptor: { - name: tableInfo.tableName, + name: writeTableInfo.tableName, objectId: evt.tableId, - schema: tableInfo.parentSchema, - replicationColumns: tableInfo.columns.map((c: any, index: number) => ({ + schema: writeTableInfo.parentSchema, + replicationColumns: writeTableInfo.columns.map((c: any, index: number) => ({ name: c.name, - type: tableInfo.columnSchemas[index].COLUMN_TYPE + type: writeTableInfo.columnSchemas[index].COLUMN_TYPE })) }, group_id: this.group_id, @@ -536,26 +536,54 @@ AND table_type = 'BASE TABLE';`, ).table }); break; - case 'updaterows': + case zongji_utils.eventIsUpdateMutation(evt): + const updateTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { + type: 'update', + data: evt.rows.map((row) => row.after), + previous_data: evt.rows.map((row) => row.before), + database: updateTableInfo.parentSchema, + table: updateTableInfo.tableName, + // TODO cleanup + sourceTable: ( + await this.storage.resolveTable({ + connection_id: this.connection_id, + connection_tag: this.connectionTag, + entity_descriptor: { + name: updateTableInfo.tableName, + objectId: evt.tableId, + schema: updateTableInfo.parentSchema, + replicationColumns: updateTableInfo.columns.map((c: any, index: number) => ({ + name: c.name, + type: updateTableInfo.columnSchemas[index].COLUMN_TYPE + })) + }, + group_id: this.group_id, + sync_rules: this.sync_rules + }) + ).table + }); break; - case 'deleterows': + case zongji_utils.eventIsDeleteMutation(evt): + // TODO, can multiple tables be present? + const deleteTableInfo = evt.tableMap[evt.tableId]; await this.writeChanges(batch, { type: 'delete', data: evt.rows, - database: tableInfo.parentSchema, - table: tableInfo.tableName, + database: deleteTableInfo.parentSchema, + table: deleteTableInfo.tableName, // TODO cleanup sourceTable: ( await this.storage.resolveTable({ connection_id: this.connection_id, connection_tag: this.connectionTag, entity_descriptor: { - name: tableInfo.tableName, + name: deleteTableInfo.tableName, objectId: evt.tableId, - schema: tableInfo.parentSchema, - replicationColumns: tableInfo.columns.map((c: any, index: number) => ({ + schema: deleteTableInfo.parentSchema, + replicationColumns: deleteTableInfo.columns.map((c: any, index: number) => ({ name: c.name, - type: tableInfo.columnSchemas[index].COLUMN_TYPE + type: deleteTableInfo.columnSchemas[index].COLUMN_TYPE })) }, group_id: this.group_id, @@ -564,8 +592,7 @@ AND table_type = 'BASE TABLE';`, ).table }); break; - break; - case 'xid': + case zongji_utils.eventIsXid(evt): // Need to commit with a replicated GTID with updated next position await batch.commit( new replication_utils.ReplicatedGTID({ @@ -577,9 +604,10 @@ AND table_type = 'BASE TABLE';`, }).comparable ); currentGTID = null; + // chunks_replicated_total.add(1); + // TODO update other metrics break; } - // chunks_replicated_total.add(1); }, 1); zongji.on('binlog', (evt: any) => { diff --git a/modules/module-mysql/src/replication/zongji/zongji-utils.ts b/modules/module-mysql/src/replication/zongji/zongji-utils.ts new file mode 100644 index 000000000..1e4126f96 --- /dev/null +++ b/modules/module-mysql/src/replication/zongji/zongji-utils.ts @@ -0,0 +1,32 @@ +import { + BinLogEvent, + BinLogGTIDLogEvent, + BinLogMutationEvent, + BinLogRotationEvent, + BinLogUpdateEvent, + BinLogXidEvent +} from '@vlasky/zongji'; + +export function eventIsGTIDLog(event: BinLogEvent): event is BinLogGTIDLogEvent { + return event.getEventName() == 'gtidlog'; +} + +export function eventIsXid(event: BinLogEvent): event is BinLogXidEvent { + return event.getEventName() == 'xid'; +} + +export function eventIsRotation(event: BinLogEvent): event is BinLogRotationEvent { + return event.getEventName() == 'rotate'; +} + +export function eventIsWriteMutation(event: BinLogEvent): event is BinLogMutationEvent { + return event.getEventName() == 'writerows'; +} + +export function eventIsDeleteMutation(event: BinLogEvent): event is BinLogMutationEvent { + return event.getEventName() == 'deleterows'; +} + +export function eventIsUpdateMutation(event: BinLogEvent): event is BinLogUpdateEvent { + return event.getEventName() == 'updaterows'; +} diff --git a/modules/module-mysql/src/replication/zongji/zongji.d.ts b/modules/module-mysql/src/replication/zongji/zongji.d.ts new file mode 100644 index 000000000..d9ff46691 --- /dev/null +++ b/modules/module-mysql/src/replication/zongji/zongji.d.ts @@ -0,0 +1,98 @@ +declare module '@vlasky/zongji' { + export type ZongjiOptions = { + host: string; + user: string; + password: string; + }; + + export type StartOptions = { + includeEvents?: string[]; + excludeEvents?: string[]; + /** + * BinLog position filename to start reading events from + */ + filename?: string; + /** + * BinLog position offset to start reading events from in file specified + */ + position?: number; + }; + + export type ColumnSchema = { + COLUMN_NAME: string; + COLLATION_NAME: string; + CHARACTER_SET_NAME: string; + COLUMN_COMMENT: string; + COLUMN_TYPE: string; + }; + + export type ColumnDefinition = { + name: string; + charset: string; + type: number; + metadata: Record; + }; + + export type TableMapEntry = { + columnSchemas: Array; + parentSchema: string; + tableName: string; + columns: Array; + }; + + export type BaseBinLogEvent = { + timestamp: number; + getEventName(): string; + + /** + * Next position in BinLog file to read from after + * this event. + */ + nextPosition: number; + /** + * Size of this event + */ + size: number; + flags: number; + useChecksum: boolean; + }; + + export type BinLogRotationEvent = BaseBinLogEvent & { + binlogName: string; + position: number; + }; + + export type BinLogGTIDLogEvent = BaseBinLogEvent & { + serverId: Buffer; + transactionRange: number; + }; + + export type BinLogXidEvent = BaseBinLogEvent & { + xid: number; + }; + + export type BinLogMutationEvent = BaseBinLogEvent & { + tableId: number; + numberOfColumns: number; + tableMap: Record; + rows: Array>; + }; + + export type BinLogUpdateEvent = Omit & { + rows: Array<{ + before: Record; + after: Record; + }>; + }; + + export type BinLogEvent = BinLogRotationEvent | BinLogGTIDLogEvent | BinLogXidEvent | BinLogMutationEvent; + + export default class ZongJi { + constructor(options: ZongjiOptions); + + start(options: StartOptions): void; + stop(): void; + + on(type: 'binlog' | string, callback: (event: BinLogEvent) => void); + } +} diff --git a/modules/module-mysql/tsconfig.json b/modules/module-mysql/tsconfig.json index 9ceadec40..0b03246f5 100644 --- a/modules/module-mysql/tsconfig.json +++ b/modules/module-mysql/tsconfig.json @@ -5,7 +5,8 @@ "outDir": "dist", "esModuleInterop": true, "skipLibCheck": true, - "sourceMap": true + "sourceMap": true, + "typeRoots": ["./node_modules/@types", "./src/replication/zongji.d.ts"] }, "include": ["src"], "references": [ From ff8e5573f14d66ccd534e03b23dd3df0e00b323f Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 13:23:45 +0200 Subject: [PATCH 122/298] update typescript --- modules/module-mysql/package.json | 2 +- .../src/replication/MysqlBinLogStream.ts | 1 - modules/module-postgres/package.json | 2 +- package.json | 2 +- packages/rsocket-router/package.json | 2 +- packages/service-core/package.json | 2 +- pnpm-lock.yaml | 68 +++++++++---------- service/package.json | 2 +- 8 files changed, 40 insertions(+), 41 deletions(-) diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 05ba74115..c3bf76b20 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@types/async": "^3.2.24", "@types/uuid": "^9.0.8", - "typescript": "^5.2.2", + "typescript": "^5.5.4", "vite-tsconfig-paths": "^4.3.2", "vitest": "^0.34.6" } diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index 9da1ae7c0..d73a2706b 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -4,7 +4,6 @@ import async from 'async'; import { storage } from '@powersync/service-core'; import mysql, { RowDataPacket } from 'mysql2/promise'; -import './zongji/zongji.js'; import ZongJi, { BinLogEvent } from '@vlasky/zongji'; import { NormalizedMySQLConnectionConfig } from '../types/types.js'; diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index f83742c66..7e208f259 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -40,7 +40,7 @@ }, "devDependencies": { "@types/uuid": "^9.0.4", - "typescript": "^5.2.2", + "typescript": "^5.5.4", "vitest": "^0.34.6", "vite-tsconfig-paths": "^4.3.2" } diff --git a/package.json b/package.json index 54bae275f..074d03072 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "semver": "^7.5.4", "tsc-watch": "^6.2.0", "ts-node-dev": "^2.0.0", - "typescript": "~5.2.2", + "typescript": "^5.5.4", "ws": "^8.2.3" } } diff --git a/packages/rsocket-router/package.json b/packages/rsocket-router/package.json index 90b95ceab..83d84c58b 100644 --- a/packages/rsocket-router/package.json +++ b/packages/rsocket-router/package.json @@ -29,7 +29,7 @@ "@types/ws": "~8.2.0", "bson": "^6.6.0", "rsocket-websocket-client": "1.0.0-alpha.3", - "typescript": "~5.2.2", + "typescript": "^5.5.4", "vitest": "^0.34.6" } } diff --git a/packages/service-core/package.json b/packages/service-core/package.json index 3357e6298..ff57675bd 100644 --- a/packages/service-core/package.json +++ b/packages/service-core/package.json @@ -50,7 +50,7 @@ "@types/uuid": "^9.0.4", "fastify": "4.23.2", "fastify-plugin": "^4.5.1", - "typescript": "^5.2.2", + "typescript": "^5.5.4", "vite-tsconfig-paths": "^4.3.2", "vitest": "^0.34.6" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21415ba57..9caa78435 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,13 +43,13 @@ importers: version: 7.6.2 ts-node-dev: specifier: ^2.0.0 - version: 2.0.0(@types/node@18.11.11)(typescript@5.2.2) + version: 2.0.0(@types/node@18.11.11)(typescript@5.5.4) tsc-watch: specifier: ^6.2.0 - version: 6.2.0(typescript@5.2.2) + version: 6.2.0(typescript@5.5.4) typescript: - specifier: ~5.2.2 - version: 5.2.2 + specifier: ^5.5.4 + version: 5.5.4 ws: specifier: ^8.2.3 version: 8.18.0 @@ -137,11 +137,11 @@ importers: specifier: ^9.0.8 version: 9.0.8 typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.5.4 + version: 5.5.4 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) + version: 4.3.2(typescript@5.5.4)(vite@5.3.3(@types/node@18.11.11)) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -186,11 +186,11 @@ importers: specifier: ^9.0.4 version: 9.0.8 typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.5.4 + version: 5.5.4 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) + version: 4.3.2(typescript@5.5.4)(vite@5.3.3(@types/node@18.11.11)) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -250,8 +250,8 @@ importers: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 typescript: - specifier: ~5.2.2 - version: 5.2.2 + specifier: ^5.5.4 + version: 5.5.4 vitest: specifier: ^0.34.6 version: 0.34.6 @@ -356,11 +356,11 @@ importers: specifier: ^4.5.1 version: 4.5.1 typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.5.4 + version: 5.5.4 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) + version: 4.3.2(typescript@5.5.4)(vite@5.3.3(@types/node@18.11.11)) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -509,10 +509,10 @@ importers: version: 16.14.20 ts-node: specifier: ^10.9.1 - version: 10.9.2(@types/node@18.11.11)(typescript@5.2.2) + version: 10.9.2(@types/node@18.11.11)(typescript@5.5.4) typescript: - specifier: ~5.2.2 - version: 5.2.2 + specifier: ^5.5.4 + version: 5.5.4 vitest: specifier: ^0.34.6 version: 0.34.6 @@ -3476,8 +3476,8 @@ packages: typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + typescript@5.5.4: + resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} engines: {node: '>=14.17'} hasBin: true @@ -4613,7 +4613,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4621,7 +4621,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -6941,7 +6941,7 @@ snapshots: ts-codec@1.2.2: {} - ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): + ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.5.4): dependencies: chokidar: 3.6.0 dynamic-dedupe: 0.3.0 @@ -6951,15 +6951,15 @@ snapshots: rimraf: 2.7.1 source-map-support: 0.5.21 tree-kill: 1.2.2 - ts-node: 10.9.2(@types/node@18.11.11)(typescript@5.2.2) + ts-node: 10.9.2(@types/node@18.11.11)(typescript@5.5.4) tsconfig: 7.0.0 - typescript: 5.2.2 + typescript: 5.5.4 transitivePeerDependencies: - '@swc/core' - '@swc/wasm' - '@types/node' - ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): + ts-node@10.9.2(@types/node@18.11.11)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -6973,21 +6973,21 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.2.2 + typescript: 5.5.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - tsc-watch@6.2.0(typescript@5.2.2): + tsc-watch@6.2.0(typescript@5.5.4): dependencies: cross-spawn: 7.0.3 node-cleanup: 2.1.2 ps-tree: 1.2.0 string-argv: 0.3.2 - typescript: 5.2.2 + typescript: 5.5.4 - tsconfck@3.1.1(typescript@5.2.2): + tsconfck@3.1.1(typescript@5.5.4): optionalDependencies: - typescript: 5.2.2 + typescript: 5.5.4 tsconfig@7.0.0: dependencies: @@ -7018,7 +7018,7 @@ snapshots: dependencies: is-typedarray: 1.0.0 - typescript@5.2.2: {} + typescript@5.5.4: {} ufo@1.5.3: {} @@ -7102,11 +7102,11 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)): + vite-tsconfig-paths@4.3.2(typescript@5.5.4)(vite@5.3.3(@types/node@18.11.11)): dependencies: debug: 4.3.5(supports-color@5.5.0) globrex: 0.1.2 - tsconfck: 3.1.1(typescript@5.2.2) + tsconfck: 3.1.1(typescript@5.5.4) optionalDependencies: vite: 5.3.3(@types/node@18.11.11) transitivePeerDependencies: diff --git a/service/package.json b/service/package.json index cd6b44490..85a211397 100644 --- a/service/package.json +++ b/service/package.json @@ -48,7 +48,7 @@ "nodemon": "^3.0.1", "npm-check-updates": "^16.14.4", "ts-node": "^10.9.1", - "typescript": "~5.2.2", + "typescript": "^5.5.4", "vitest": "^0.34.6" } } From 3aaa90e01b4b41d1d0285e7f0b4629c9edf2037e Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 13:35:20 +0200 Subject: [PATCH 123/298] shuffle files arround --- .../src/{utils/replication/GTID.ts => common/ReplicatedGTID.ts} | 0 .../configuration.ts => common/check-source-configuration.ts} | 0 .../replication/schema.ts => common/get-replication-columns.ts} | 0 .../src/{utils/replication/head.ts => common/read-master-gtid.ts} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename modules/module-mysql/src/{utils/replication/GTID.ts => common/ReplicatedGTID.ts} (100%) rename modules/module-mysql/src/{utils/replication/configuration.ts => common/check-source-configuration.ts} (100%) rename modules/module-mysql/src/{utils/replication/schema.ts => common/get-replication-columns.ts} (100%) rename modules/module-mysql/src/{utils/replication/head.ts => common/read-master-gtid.ts} (100%) diff --git a/modules/module-mysql/src/utils/replication/GTID.ts b/modules/module-mysql/src/common/ReplicatedGTID.ts similarity index 100% rename from modules/module-mysql/src/utils/replication/GTID.ts rename to modules/module-mysql/src/common/ReplicatedGTID.ts diff --git a/modules/module-mysql/src/utils/replication/configuration.ts b/modules/module-mysql/src/common/check-source-configuration.ts similarity index 100% rename from modules/module-mysql/src/utils/replication/configuration.ts rename to modules/module-mysql/src/common/check-source-configuration.ts diff --git a/modules/module-mysql/src/utils/replication/schema.ts b/modules/module-mysql/src/common/get-replication-columns.ts similarity index 100% rename from modules/module-mysql/src/utils/replication/schema.ts rename to modules/module-mysql/src/common/get-replication-columns.ts diff --git a/modules/module-mysql/src/utils/replication/head.ts b/modules/module-mysql/src/common/read-master-gtid.ts similarity index 100% rename from modules/module-mysql/src/utils/replication/head.ts rename to modules/module-mysql/src/common/read-master-gtid.ts From cc1ca9f405449dedd7b1023bfef0928e0fc4ca5d Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 13:35:28 +0200 Subject: [PATCH 124/298] update after shuffle --- .../src/api/MySQLRouteAPIAdapter.ts | 14 +++++------ .../module-mysql/src/common/ReplicatedGTID.ts | 5 ++-- .../src/common/check-source-configuration.ts | 6 ++--- .../module-mysql/src/common/common-index.ts | 4 ++++ .../src/common/get-replication-columns.ts | 8 +++---- .../src/common/read-master-gtid.ts | 7 +++--- .../src/replication/MysqlBinLogStream.ts | 24 +++++++++---------- .../utils/replication/replication-utils.ts | 5 ---- 8 files changed, 37 insertions(+), 36 deletions(-) create mode 100644 modules/module-mysql/src/common/common-index.ts delete mode 100644 modules/module-mysql/src/utils/replication/replication-utils.ts diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 784951892..a66430224 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -3,9 +3,9 @@ import { api, storage } from '@powersync/service-core'; import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; +import * as common from '../common/common-index.js'; import * as types from '../types/types.js'; import * as mysql_utils from '../utils/mysql_utils.js'; -import * as replication_utils from '../utils/replication/replication-utils.js'; type SchemaResult = { schema_name: string; @@ -45,7 +45,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { }; } try { - const errors = await replication_utils.checkSourceConfiguration(this.pool); + const errors = await common.checkSourceConfiguration(this.pool); if (errors.length) { return { ...base, @@ -198,10 +198,10 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { ): Promise { const { schema } = tablePattern; - let idColumnsResult: replication_utils.ReplicationIdentityColumnsResult | null = null; + let idColumnsResult: common.ReplicationIdentityColumnsResult | null = null; let idColumnsError: service_types.ReplicationError | null = null; try { - idColumnsResult = await replication_utils.getReplicationIdentityColumns({ + idColumnsResult = await common.getReplicationIdentityColumns({ db: this.pool, schema, table_name: tableName @@ -246,8 +246,8 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { async getReplicationLag(options: api.ReplicationLagOptions): Promise { const { last_checkpoint_identifier } = options; - const current = replication_utils.ReplicatedGTID.fromSerialized(last_checkpoint_identifier); - const head = await replication_utils.readMasterGtid(this.pool); + const current = common.ReplicatedGTID.fromSerialized(last_checkpoint_identifier); + const head = await common.readMasterGtid(this.pool); const lag = await current.distanceTo(this.pool, head); if (lag == null) { @@ -258,7 +258,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async getReplicationHead(): Promise { - const result = await replication_utils.readMasterGtid(this.pool); + const result = await common.readMasterGtid(this.pool); return result.comparable; } diff --git a/modules/module-mysql/src/common/ReplicatedGTID.ts b/modules/module-mysql/src/common/ReplicatedGTID.ts index a48f5b216..713f1f174 100644 --- a/modules/module-mysql/src/common/ReplicatedGTID.ts +++ b/modules/module-mysql/src/common/ReplicatedGTID.ts @@ -1,6 +1,7 @@ import mysql from 'mysql2/promise'; import * as uuid from 'uuid'; -import { retriedQuery } from '../mysql_utils.js'; +import * as mysql_utils from '../utils/mysql_utils.js'; + export type BinLogPosition = { filename: string; offset: number; @@ -113,7 +114,7 @@ export class ReplicatedGTID { * Calculates the distance in bytes from this GTID to the provided argument. */ async distanceTo(db: mysql.Pool, to: ReplicatedGTID): Promise { - const [logFiles] = await retriedQuery({ + const [logFiles] = await mysql_utils.retriedQuery({ db, query: `SHOW BINARY LOGS;` }); diff --git a/modules/module-mysql/src/common/check-source-configuration.ts b/modules/module-mysql/src/common/check-source-configuration.ts index 3ecca7269..74bd7d02d 100644 --- a/modules/module-mysql/src/common/check-source-configuration.ts +++ b/modules/module-mysql/src/common/check-source-configuration.ts @@ -1,9 +1,9 @@ import mysql from 'mysql2/promise'; -import { retriedQuery } from '../mysql_utils.js'; +import * as mysql_utils from '../utils/mysql_utils.js'; export async function checkSourceConfiguration(db: mysql.Pool) { const errors: string[] = []; - const [[result]] = await retriedQuery({ + const [[result]] = await mysql_utils.retriedQuery({ db, query: ` SELECT @@ -37,7 +37,7 @@ export async function checkSourceConfiguration(db: mysql.Pool) { errors.push('Binary log index file is not set. Please check your settings.'); } - const [[binLogFormatResult]] = await retriedQuery({ + const [[binLogFormatResult]] = await mysql_utils.retriedQuery({ db, query: `SHOW VARIABLES LIKE 'binlog_format';` }); diff --git a/modules/module-mysql/src/common/common-index.ts b/modules/module-mysql/src/common/common-index.ts new file mode 100644 index 000000000..0f8cf357f --- /dev/null +++ b/modules/module-mysql/src/common/common-index.ts @@ -0,0 +1,4 @@ +export * from './check-source-configuration.js'; +export * from './get-replication-columns.js'; +export * from './read-master-gtid.js'; +export * from './ReplicatedGTID.js'; diff --git a/modules/module-mysql/src/common/get-replication-columns.ts b/modules/module-mysql/src/common/get-replication-columns.ts index 84dbd35b7..563b8ce11 100644 --- a/modules/module-mysql/src/common/get-replication-columns.ts +++ b/modules/module-mysql/src/common/get-replication-columns.ts @@ -1,6 +1,6 @@ import { storage } from '@powersync/service-core'; import mysql from 'mysql2/promise'; -import { retriedQuery } from '../mysql_utils.js'; +import * as mysql_utils from '../utils/mysql_utils.js'; export type GetReplicationColumnsOptions = { db: mysql.Pool; @@ -18,7 +18,7 @@ export async function getReplicationIdentityColumns( options: GetReplicationColumnsOptions ): Promise { const { db, schema, table_name } = options; - const [primaryKeyColumns] = await retriedQuery({ + const [primaryKeyColumns] = await mysql_utils.retriedQuery({ db, query: ` SELECT @@ -54,7 +54,7 @@ export async function getReplicationIdentityColumns( // TODO: test code with tables with unique keys, compound key etc. // No primary key, find the first valid unique key - const [uniqueKeyColumns] = await retriedQuery({ + const [uniqueKeyColumns] = await mysql_utils.retriedQuery({ db, query: ` SELECT @@ -91,7 +91,7 @@ export async function getReplicationIdentityColumns( }; } - const [allColumns] = await retriedQuery({ + const [allColumns] = await mysql_utils.retriedQuery({ db, query: ` SELECT diff --git a/modules/module-mysql/src/common/read-master-gtid.ts b/modules/module-mysql/src/common/read-master-gtid.ts index 83d4a2ec5..6044bbe38 100644 --- a/modules/module-mysql/src/common/read-master-gtid.ts +++ b/modules/module-mysql/src/common/read-master-gtid.ts @@ -1,6 +1,7 @@ import mysql from 'mysql2/promise'; -import { retriedQuery } from '../mysql_utils.js'; -import { ReplicatedGTID } from './GTID.js'; +import * as mysql_utils from '../utils/mysql_utils.js'; + +import { ReplicatedGTID } from './ReplicatedGTID.js'; /** * Gets the current master HEAD GTID @@ -10,7 +11,7 @@ export async function readMasterGtid(db: mysql.Pool): Promise { const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); // Get the BinLog position if master - const [[masterResult]] = await retriedQuery({ + const [[masterResult]] = await mysql_utils.retriedQuery({ db, query: `SHOW master STATUS` }); diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index d73a2706b..58e38d996 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -6,8 +6,8 @@ import { storage } from '@powersync/service-core'; import mysql, { RowDataPacket } from 'mysql2/promise'; import ZongJi, { BinLogEvent } from '@vlasky/zongji'; +import * as common from '../common/common-index.js'; import { NormalizedMySQLConnectionConfig } from '../types/types.js'; -import * as replication_utils from '../utils/replication/replication-utils.js'; import * as zongji_utils from './zongji/zongji-utils.js'; export interface BinLogStreamOptions { @@ -198,13 +198,13 @@ export class MysqlBinLogStream { await batch.truncate([result.table]); // TODO: put zero GTID somewhere. - let gtid: replication_utils.ReplicatedGTID | null = null; + let gtid: common.ReplicatedGTID; // Start the snapshot inside a transaction. // We use a dedicated connection for this. try { await this.pool.query('BEGIN'); try { - gtid = await replication_utils.readMasterGtid(this.pool); + gtid = await common.readMasterGtid(this.pool); await this.snapshotTable(batch, this.pool, result.table); await this.pool.query('COMMIT'); } catch (e) { @@ -271,7 +271,7 @@ AND table_type = 'BASE TABLE';`, continue; } - const cresult = await replication_utils.getReplicationIdentityColumns({ + const cresult = await common.getReplicationIdentityColumns({ db: this.pool, schema: tablePattern.schema, table_name: tablePattern.name @@ -294,7 +294,7 @@ AND table_type = 'BASE TABLE';`, } async initSlot(): Promise { - await replication_utils.checkSourceConfiguration(this.pool); + await common.checkSourceConfiguration(this.pool); const slotName = this.slot_name; @@ -321,7 +321,7 @@ AND table_type = 'BASE TABLE';`, const slotName = this.slot_name; await this.storage.clear(); - const headGTID = await replication_utils.readMasterGtid(db); + const headGTID = await common.readMasterGtid(db); logger.info(`Using GTID:: '${headGTID}'`); await db.query('BEGIN'); try { @@ -335,7 +335,7 @@ AND table_type = 'BASE TABLE';`, } } - async initialReplication(db: mysql.Pool, gtid: replication_utils.ReplicatedGTID) { + async initialReplication(db: mysql.Pool, gtid: common.ReplicatedGTID) { const sourceTables = this.sync_rules.getSourceTables(); // TODO fix database default schema if not provided explicitly in the sync rules // .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); @@ -473,8 +473,8 @@ AND table_type = 'BASE TABLE';`, const { checkpoint_lsn } = await this.storage.getStatus(); const fromGTID = checkpoint_lsn - ? replication_utils.ReplicatedGTID.fromSerialized(checkpoint_lsn) - : await replication_utils.readMasterGtid(this.pool); + ? common.ReplicatedGTID.fromSerialized(checkpoint_lsn) + : await common.readMasterGtid(this.pool); const binLogPositionState = fromGTID.position; await this.storage.startBatch({}, async (batch) => { @@ -484,13 +484,13 @@ AND table_type = 'BASE TABLE';`, password: this.options.connection_config.password }); - let currentGTID: replication_utils.ReplicatedGTID | null = null; + let currentGTID: common.ReplicatedGTID | null = null; const queue = async.queue(async (evt: BinLogEvent) => { // State machine switch (true) { case zongji_utils.eventIsGTIDLog(evt): - currentGTID = replication_utils.ReplicatedGTID.fromBinLogEvent({ + currentGTID = common.ReplicatedGTID.fromBinLogEvent({ raw_gtid: { server_id: evt.serverId, transaction_range: evt.transactionRange @@ -594,7 +594,7 @@ AND table_type = 'BASE TABLE';`, case zongji_utils.eventIsXid(evt): // Need to commit with a replicated GTID with updated next position await batch.commit( - new replication_utils.ReplicatedGTID({ + new common.ReplicatedGTID({ raw_gtid: currentGTID!.raw, position: { filename: binLogPositionState.filename, diff --git a/modules/module-mysql/src/utils/replication/replication-utils.ts b/modules/module-mysql/src/utils/replication/replication-utils.ts deleted file mode 100644 index f29b6f1fc..000000000 --- a/modules/module-mysql/src/utils/replication/replication-utils.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './configuration.js'; -export * from './GTID.js'; -export * from './head.js'; - -export * from './schema.js'; From bb0be0fc26a0aa4fac3984cc6be24139aa6e9c3a Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 22 Aug 2024 15:15:51 +0200 Subject: [PATCH 125/298] Test: Try changing order of filter expression in github test action --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c02dd753..26b73d197 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,7 +76,7 @@ jobs: run: pnpm build - name: Test - run: pnpm test --filter !./modules + run: pnpm --filter !./modules test run-postgres-tests: name: Postgres Test From f1336aec297c9d0483a00256950eeee94021e28c Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 22 Aug 2024 15:57:20 +0200 Subject: [PATCH 126/298] Replication job start no longer immediately cancels --- .../src/replication/AbstractReplicationJob.ts | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/packages/service-core/src/replication/AbstractReplicationJob.ts b/packages/service-core/src/replication/AbstractReplicationJob.ts index 0aafef5a5..b8bef79b1 100644 --- a/packages/service-core/src/replication/AbstractReplicationJob.ts +++ b/packages/service-core/src/replication/AbstractReplicationJob.ts @@ -39,28 +39,26 @@ export abstract class AbstractReplicationJob { * Start the replication process */ public async start(): Promise { - try { - this.isReplicatingPromise = this.replicate(); - } catch (e) { - // TODO handle catch - // Fatal exception - container.reporter.captureException(e, { - metadata: { - replicator: this.id - } + this.isReplicatingPromise = this.replicate() + .catch((ex) => { + container.reporter.captureException(ex, { + metadata: { + replicator: this.id + } + }); + this.logger.error(`Replication failed.`, ex); + }) + .finally(async () => { + this.abortController.abort(); + await this.options.lock.release(); }); - logger.error(`Replication failed.`, e); - } finally { - this.abortController.abort(); - } - await this.options.lock.release(); } /** * Safely stop the replication process */ public async stop(): Promise { - logger.info(`Stopping ${this.id} replication job for sync rule iteration: ${this.storage.group_id}`); + this.logger.info(`Stopping ${this.id} replication job for sync rule iteration: ${this.storage.group_id}`); this.abortController.abort(); await this.isReplicatingPromise; } @@ -70,7 +68,7 @@ export abstract class AbstractReplicationJob { * Clean up any config on the datasource related to this replication job */ public async terminate(): Promise { - logger.info(`${this.id} Terminating replication`); + this.logger.info(`${this.id} Terminating replication`); await this.stop(); await this.cleanUp(); await this.options.storage.terminate(); From e2422e8f74b67fbc5742d6856203e61058de8cbc Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 15:58:27 +0200 Subject: [PATCH 127/298] use new replicator interfaces --- .../module-mysql/src/module/MySQLModule.ts | 26 ++- .../src/replication/BinLogReplicator.ts | 28 +++ .../src/replication/BinLogReplicatorJob.ts | 105 ++++++++++ .../src/replication/MSSQLReplicator.ts | 29 --- .../src/replication/MySQLErrorRateLimiter.ts | 44 ++++ .../src/replication/MysqlBinLogStream.ts | 1 - .../replication/MysqlBinLogStreamManager.ts | 192 ------------------ .../replication/MysqlBinLogStreamRunner.ts | 113 ----------- .../src/module/PostgresModule.ts | 6 +- .../src/replication/AbstractReplicationJob.ts | 30 ++- 10 files changed, 212 insertions(+), 362 deletions(-) create mode 100644 modules/module-mysql/src/replication/BinLogReplicator.ts create mode 100644 modules/module-mysql/src/replication/BinLogReplicatorJob.ts delete mode 100644 modules/module-mysql/src/replication/MSSQLReplicator.ts create mode 100644 modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts delete mode 100644 modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts delete mode 100644 modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts index 58223664e..20255187a 100644 --- a/modules/module-mysql/src/module/MySQLModule.ts +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -1,22 +1,20 @@ -import { api, replication, system } from '@powersync/service-core'; +import { api, replication, system, utils } from '@powersync/service-core'; import { MySQLRouteAPIAdapter } from '../api/MySQLRouteAPIAdapter.js'; -import { MSSQLReplicator } from '../replication/MSSQLReplicator.js'; +import { BinLogReplicator } from '../replication/BinLogReplicator.js'; +import { MySQLErrorRateLimiter } from '../replication/MySQLErrorRateLimiter.js'; import * as types from '../types/types.js'; export class MySQLModule extends replication.ReplicationModule { - protected context: system.ServiceContext | null; constructor() { super({ name: 'MySQL', type: types.MYSQL_CONNECTION_TYPE, configSchema: types.MySQLConnectionConfig }); - this.context = null; } async initialize(context: system.ServiceContextContainer): Promise { - this.context = context; await super.initialize(context); // TODO move this to the binlog consumer @@ -31,9 +29,21 @@ export class MySQLModule extends replication.ReplicationModule utils.loadSyncRules(context.configuration) + }, + storageFactory: context.storage + }); } /** diff --git a/modules/module-mysql/src/replication/BinLogReplicator.ts b/modules/module-mysql/src/replication/BinLogReplicator.ts new file mode 100644 index 000000000..2f0ebb663 --- /dev/null +++ b/modules/module-mysql/src/replication/BinLogReplicator.ts @@ -0,0 +1,28 @@ +import { replication } from '@powersync/service-core'; +import * as types from '../types/types.js'; +import { BinLogReplicatorJob } from './BinLogReplicatorJob.js'; + +export interface BinLogReplicatorOptions extends replication.AbstractReplicatorOptions { + /** + * Connection config required to a MySQL Pool + */ + connectionConfig: types.ResolvedConnectionConfig; +} + +export class BinLogReplicator extends replication.AbstractReplicator { + protected connectionConfig: types.ResolvedConnectionConfig; + + constructor(options: BinLogReplicatorOptions) { + super(options); + this.connectionConfig = options.connectionConfig; + } + + createJob(options: replication.CreateJobOptions): BinLogReplicatorJob { + return new BinLogReplicatorJob({ + id: this.createJobId(options.storage.group_id), + storage: options.storage, + lock: options.lock, + connectionConfig: this.connectionConfig + }); + } +} diff --git a/modules/module-mysql/src/replication/BinLogReplicatorJob.ts b/modules/module-mysql/src/replication/BinLogReplicatorJob.ts new file mode 100644 index 000000000..17df0eec6 --- /dev/null +++ b/modules/module-mysql/src/replication/BinLogReplicatorJob.ts @@ -0,0 +1,105 @@ +import { container } from '@powersync/lib-services-framework'; +import * as types from '../types/types.js'; + +import * as mysql_utils from '../utils/mysql_utils.js'; + +import { replication } from '@powersync/service-core'; +import { MysqlBinLogStream } from './MysqlBinLogStream.js'; + +export interface BinLogReplicationJobOptions extends replication.AbstractReplicationJobOptions { + /** + * Connection config required to create a MySQL Pool + */ + connectionConfig: types.ResolvedConnectionConfig; +} + +export class BinLogReplicatorJob extends replication.AbstractReplicationJob { + protected connectionConfig: types.ResolvedConnectionConfig; + + constructor(options: BinLogReplicationJobOptions) { + super(options); + this.connectionConfig = options.connectionConfig; + } + + get slot_name() { + return this.options.storage.slot_name; + } + + async cleanUp(): Promise { + // This MySQL module does not create anything which requires cleanup on the MySQL server. + } + + async keepAlive() {} + + async replicate() { + try { + await this.replicateLoop(); + } catch (e) { + // Fatal exception + container.reporter.captureException(e, { + metadata: { + replication_slot: this.slot_name + } + }); + this.logger.error(`Replication failed on ${this.slot_name}`, e); + + // Slot removal type error logic goes here + // if (e) { + // // This stops replication on this slot, and creates a new slot + // await this.options.storage.factory.slotRemoved(this.slot_name); + // } + } finally { + this.abortController.abort(); + } + } + + async replicateLoop() { + while (!this.isStopped) { + await this.replicateOnce(); + + if (!this.isStopped) { + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + } + + async replicateOnce() { + // New connections on every iteration (every error with retry), + // otherwise we risk repeating errors related to the connection, + // such as caused by cached PG schemas. + const pool = mysql_utils.createPool(this.connectionConfig); + try { + await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); + if (this.isStopped) { + return; + } + const stream = new MysqlBinLogStream({ + abort_signal: this.abortController.signal, + connection_config: this.connectionConfig, + pool, + storage: this.options.storage + }); + await stream.replicate(); + } catch (e) { + this.logger.error(`Replication error`, e); + if (e.cause != null) { + this.logger.error(`cause`, e.cause); + } + // TODO not recoverable error + if (false) { + throw e; + } else { + // Report the error if relevant, before retrying + container.reporter.captureException(e, { + metadata: { + replication_slot: this.slot_name + } + }); + // This sets the retry delay + this.rateLimiter?.reportError(e); + } + } finally { + await pool.end(); + } + } +} diff --git a/modules/module-mysql/src/replication/MSSQLReplicator.ts b/modules/module-mysql/src/replication/MSSQLReplicator.ts deleted file mode 100644 index 84c850536..000000000 --- a/modules/module-mysql/src/replication/MSSQLReplicator.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { replication, system } from '@powersync/service-core'; -import mysql_promise from 'mysql2/promise'; -import * as types from '../types/types.js'; -import * as mysql_utils from '../utils/mysql_utils.js'; -import { MysqlBinLogStreamManager } from './MysqlBinLogStreamManager.js'; - -export class MSSQLReplicator implements replication.Replicator { - id: string; - - protected pool: mysql_promise.Pool; - protected manager: MysqlBinLogStreamManager | null; - - constructor(protected config: types.ResolvedConnectionConfig, protected serviceContext: system.ServiceContext) { - this.id = 'mysql'; - this.pool = mysql_utils.createPool(config); - this.manager = null; - } - - async start(): Promise { - console.log('starting'); - this.manager = new MysqlBinLogStreamManager(this.serviceContext, this.pool); - - this.manager.start(); - } - async stop(): Promise { - await this.manager?.stop(); - // throw new Error('Method not implemented.'); - } -} diff --git a/modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts b/modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts new file mode 100644 index 000000000..aff96c3ab --- /dev/null +++ b/modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts @@ -0,0 +1,44 @@ +import { ErrorRateLimiter } from '@powersync/service-core'; +import { setTimeout } from 'timers/promises'; + +export class MySQLErrorRateLimiter implements ErrorRateLimiter { + nextAllowed: number = Date.now(); + + async waitUntilAllowed(options?: { signal?: AbortSignal | undefined } | undefined): Promise { + const delay = Math.max(0, this.nextAllowed - Date.now()); + // Minimum delay between connections, even without errors + this.setDelay(500); + await setTimeout(delay, undefined, { signal: options?.signal }); + } + + mayPing(): boolean { + return Date.now() >= this.nextAllowed; + } + + reportError(e: any): void { + const message = (e.message as string) ?? ''; + if (message.includes('password authentication failed')) { + // Wait 15 minutes, to avoid triggering Supabase's fail2ban + this.setDelay(900_000); + } else if (message.includes('ENOTFOUND')) { + // DNS lookup issue - incorrect URI or deleted instance + this.setDelay(120_000); + } else if (message.includes('ECONNREFUSED')) { + // Could be fail2ban or similar + this.setDelay(120_000); + } else if ( + message.includes('Unable to do postgres query on ended pool') || + message.includes('Postgres unexpectedly closed connection') + ) { + // Connection timed out - ignore / immediately retry + // We don't explicitly set the delay to 0, since there could have been another error that + // we need to respect. + } else { + this.setDelay(30_000); + } + } + + private setDelay(delay: number) { + this.nextAllowed = Math.max(this.nextAllowed, Date.now() + delay); + } +} diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index 58e38d996..34ff55019 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -14,7 +14,6 @@ export interface BinLogStreamOptions { pool: mysql.Pool; connection_config: NormalizedMySQLConnectionConfig; - factory: storage.BucketStorageFactory; storage: storage.SyncRulesBucketStorage; abort_signal: AbortSignal; } diff --git a/modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts b/modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts deleted file mode 100644 index 1a69c980b..000000000 --- a/modules/module-mysql/src/replication/MysqlBinLogStreamManager.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { logger } from '@powersync/lib-services-framework'; -import { storage, system, utils } from '@powersync/service-core'; -import mysql from 'mysql2/promise'; -import { hrtime } from 'node:process'; -import { normalizeConnectionConfig } from '../types/types.js'; -import { MysqlBinLogStreamRunner } from './MysqlBinLogStreamRunner.js'; - -// 5 minutes -const PING_INTERVAL = 1_000_000_000n * 300n; - -export class MysqlBinLogStreamManager { - private streams = new Map(); - - private stopped = false; - - // First ping is only after 5 minutes, not when starting - private lastPing = hrtime.bigint(); - - private storage: storage.BucketStorageFactory; - - constructor(protected serviceContext: system.ServiceContext, protected pool: mysql.Pool) { - this.storage = serviceContext.storage.bucketStorage; - } - - start() { - this.runLoop().catch((e) => { - console.error(e); - }); - } - - async stop() { - this.stopped = true; - let promises: Promise[] = []; - for (let stream of this.streams.values()) { - promises.push(stream.stop()); - } - await Promise.all(promises); - } - - private async runLoop() { - const configured_sync_rules = await utils.loadSyncRules(this.serviceContext.configuration); - let configured_lock: storage.ReplicationLock | undefined = undefined; - if (configured_sync_rules != null) { - logger.info('Loading sync rules from configuration'); - try { - // Configure new sync rules, if it has changed. - // In that case, also immediately take out a lock, so that another process doesn't start replication on it. - const { updated, persisted_sync_rules, lock } = await this.storage.configureSyncRules(configured_sync_rules!, { - lock: true - }); - logger.info(`configureSyncRules: update: ${updated}, persisted_sync_rules: ${persisted_sync_rules}`); - if (lock) { - configured_lock = lock; - } - } catch (e) { - // Log, but continue with previous sync rules - logger.error(`Failed to load sync rules from configuration`, e); - } - } else { - logger.info('No sync rules configured - configure via API'); - } - while (!this.stopped) { - // await touch(); - try { - const { pool } = this; - if (pool) { - await this.refresh({ configured_lock }); - // The lock is only valid on the first refresh. - configured_lock = undefined; - - // TODO: Ping on all connections when we have multiple - // Perhaps MysqlWalStreamRunner would be a better place to do pings? - // We don't ping while in error retry back-off, to avoid having too - // many authentication failures. - // if (this.rateLimiter.mayPing()) { - // await this.ping(pool); - // } - } - } catch (e) { - logger.error(`Failed to refresh wal streams`, e); - } - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - - /** - * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. - * - * The old WAL files are only deleted once no replication slot still references it. - * - * Unfortunately, when there are no changes to the db, the database creates new WAL files without the replication slot - * advancing**. - * - * As a workaround, we write a new message every couple of minutes, to make sure that the replication slot advances. - * - * **This may be a bug in pgwire or how we're using it. - */ - private async ping(db: mysql.Pool) { - const now = hrtime.bigint(); - if (now - this.lastPing >= PING_INTERVAL) { - try { - await db.query('SELECT 1'); - } catch (e) { - logger.warn(`Failed to ping`, e); - } - this.lastPing = now; - } - } - - private async refresh(options?: { configured_lock?: storage.ReplicationLock }) { - if (this.stopped) { - return; - } - - let configured_lock = options?.configured_lock; - - const existingStreams = new Map(this.streams.entries()); - const replicating = await this.storage.getReplicatingSyncRules(); - const newStreams = new Map(); - for (let syncRules of replicating) { - const existing = existingStreams.get(syncRules.id); - if (existing && !existing.stopped) { - // No change - existingStreams.delete(syncRules.id); - newStreams.set(syncRules.id, existing); - } else if (existing && existing.stopped) { - // Stopped (e.g. fatal error, slot rename). - // Remove from the list. Next refresh call will restart the stream. - existingStreams.delete(syncRules.id); - } else { - // New (or resume after restart) - try { - let lock: storage.ReplicationLock; - if (configured_lock?.sync_rules_id == syncRules.id) { - lock = configured_lock; - } else { - lock = await syncRules.lock(); - } - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - - const stream = new MysqlBinLogStreamRunner({ - connection_config: normalizeConnectionConfig(this.serviceContext.configuration.connections?.[0] as any), // TODO - factory: this.storage, - storage: storage, - source_db: this.pool, - lock - }); - newStreams.set(syncRules.id, stream); - stream.start(); - } catch (e) { - // Could be a sync rules parse error, - // for example from stricter validation that was added. - // This will be retried every couple of seconds. - // When new (valid) sync rules are deployed and processed, this one be disabled. - logger.error(`Failed to start replication for ${syncRules.slot_name}`, e); - } - } - } - - this.streams = newStreams; - - for (let stream of existingStreams.values()) { - // Old - stop and remove. - await stream.terminate(); - } - - // Sync rules stopped previously or by a different process. - const stopped = await this.storage.getStoppedSyncRules(); - for (let syncRules of stopped) { - try { - const lock = await syncRules.lock(); - try { - const parsed = syncRules.parsed(); - const storage = this.storage.getInstance(parsed); - const stream = new MysqlBinLogStreamRunner({ - connection_config: normalizeConnectionConfig(this.serviceContext.configuration.connections?.[0] as any), // TODO - factory: this.storage, - storage: storage, - source_db: this.pool, - lock - }); - await stream.terminate(); - } finally { - await lock.release(); - } - } catch (e) { - logger.warn(`Failed to terminate ${syncRules.slot_name}`, e); - } - } - } -} diff --git a/modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts b/modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts deleted file mode 100644 index ccb55022c..000000000 --- a/modules/module-mysql/src/replication/MysqlBinLogStreamRunner.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { logger } from '@powersync/lib-services-framework'; -import { storage } from '@powersync/service-core'; -import mysql from 'mysql2/promise'; -import { NormalizedMySQLConnectionConfig } from '../types/types.js'; -import { MysqlBinLogStream } from './MysqlBinLogStream.js'; - -export interface BinLogStreamRunnerOptions { - factory: storage.BucketStorageFactory; - storage: storage.SyncRulesBucketStorage; - source_db: mysql.Pool; - lock: storage.ReplicationLock; - connection_config: NormalizedMySQLConnectionConfig; -} - -export class MysqlBinLogStreamRunner { - private abortController = new AbortController(); - - private runPromise?: Promise; - - constructor(public options: BinLogStreamRunnerOptions) {} - - start() { - this.runPromise = this.run(); - } - - get slot_name() { - return this.options.storage.slot_name; - } - - get stopped() { - return this.abortController.signal.aborted; - } - - async run() { - try { - await this.replicateLoop(); - } catch (e) { - // Fatal exception - // if (e instanceof MissingReplicationSlotError) { - // This stops replication on this slot, and creates a new slot - // await this.options.storage.factory.slotRemoved(this.slot_name); - // } - } finally { - this.abortController.abort(); - } - await this.options.lock.release(); - } - - async replicateLoop() { - while (!this.stopped) { - await this.replicateOnce(); - - if (!this.stopped) { - await new Promise((resolve) => setTimeout(resolve, 5000)); - } - } - } - - async replicateOnce() { - // New connections on every iteration (every error with retry), - // otherwise we risk repeating errors related to the connection, - // such as caused by cached PG schemas. - try { - // await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); - if (this.stopped) { - return; - } - const stream = new MysqlBinLogStream({ - abort_signal: this.abortController.signal, - connection_config: this.options.connection_config, - factory: this.options.factory, - storage: this.options.storage, - pool: this.options.source_db - }); - await stream.replicate(); - } catch (e) { - logger.error(`Replication error`, e); - if (e.cause != null) { - logger.error(`cause`, e.cause); - } - } - } - - /** - * This will also release the lock if start() was called earlier. - */ - async stop(options?: { force?: boolean }) { - logger.info(`${this.slot_name} Stopping replication`); - // End gracefully - this.abortController.abort(); - - if (options?.force) { - // destroy() is more forceful. - this.options.source_db?.destroy(); - } - await this.runPromise; - } - - /** - * Terminate this replication stream. This drops the replication slot and deletes the replication data. - * - * Stops replication if needed. - */ - async terminate(options?: { force?: boolean }) { - logger.info(`${this.slot_name} Terminating replication`); - // if (!isNormalizedPostgresConnection(this.options.source_db)) { - // throw new Error('Replication only supported for normalized Postgres connections'); - // } - await this.stop(options); - await this.options.source_db.end(); - await this.options.storage.terminate(); - } -} diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 0ab8325a4..6e7298f6c 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,11 +1,11 @@ import { api, auth, ConfigurationFileSyncRulesProvider, replication, system } from '@powersync/service-core'; import * as jpgwire from '@powersync/service-jpgwire'; -import * as types from '../types/types.js'; import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; -import { WalStreamReplicator } from '../replication/WalStreamReplicator.js'; import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js'; import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js'; +import { WalStreamReplicator } from '../replication/WalStreamReplicator.js'; +import * as types from '../types/types.js'; export class PostgresModule extends replication.ReplicationModule { private connectionFactories: Set; @@ -23,11 +23,11 @@ export class PostgresModule extends replication.ReplicationModule { await super.initialize(context); - // Record replicated bytes using global jpgwire metrics. if (context.configuration.base_config.client_auth?.supabase) { this.registerSupabaseAuth(context); } + // Record replicated bytes using global jpgwire metrics. jpgwire.setMetricsRecorder({ addBytesRead(bytes) { context.metrics.data_replicated_bytes.add(bytes); diff --git a/packages/service-core/src/replication/AbstractReplicationJob.ts b/packages/service-core/src/replication/AbstractReplicationJob.ts index 0aafef5a5..f77a8f03a 100644 --- a/packages/service-core/src/replication/AbstractReplicationJob.ts +++ b/packages/service-core/src/replication/AbstractReplicationJob.ts @@ -1,7 +1,7 @@ -import * as storage from '../storage/storage-index.js'; -import { ErrorRateLimiter } from './ErrorRateLimiter.js'; import { container, logger } from '@powersync/lib-services-framework'; import winston from 'winston'; +import * as storage from '../storage/storage-index.js'; +import { ErrorRateLimiter } from './ErrorRateLimiter.js'; export interface AbstractReplicationJobOptions { id: string; @@ -39,21 +39,19 @@ export abstract class AbstractReplicationJob { * Start the replication process */ public async start(): Promise { - try { - this.isReplicatingPromise = this.replicate(); - } catch (e) { - // TODO handle catch - // Fatal exception - container.reporter.captureException(e, { - metadata: { - replicator: this.id - } + this.isReplicatingPromise = this.replicate() + .catch((ex) => { + container.reporter.captureException(ex, { + metadata: { + replicator: this.id + } + }); + logger.error(`Replication failed.`, ex); + }) + .finally(async () => { + this.abortController.abort(); + await this.options.lock.release(); }); - logger.error(`Replication failed.`, e); - } finally { - this.abortController.abort(); - } - await this.options.lock.release(); } /** From 56c3dbf8a5a2348d6f1aa50e610e8cf153960145 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 22 Aug 2024 16:14:50 +0200 Subject: [PATCH 128/298] Move connection factory shutdown Pass along publication name --- .../src/api/PostgresRouteAPIAdapter.ts | 2 +- modules/module-postgres/src/module/PostgresModule.ts | 11 +---------- modules/module-postgres/src/replication/WalStream.ts | 4 ++-- .../src/replication/WalStreamReplicator.ts | 5 +++++ modules/module-postgres/test/src/wal_stream_utils.ts | 2 +- 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 7294ac1a4..5b00fa04c 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -46,7 +46,7 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { } try { - await replication_utils.checkSourceConfiguration(this.pool); + await replication_utils.checkSourceConfiguration(this.pool, this.publication_name); } catch (e) { return { ...base, diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 0ab8325a4..447365b4f 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -8,16 +8,12 @@ import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactor import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js'; export class PostgresModule extends replication.ReplicationModule { - private connectionFactories: Set; - constructor() { super({ name: 'Postgres', type: types.POSTGRES_CONNECTION_TYPE, configSchema: types.PostgresConnectionConfig }); - - this.connectionFactories = new Set(); } async initialize(context: system.ServiceContextContainer): Promise { @@ -45,7 +41,6 @@ export class PostgresModule extends replication.ReplicationModule { - for (const connectionFactory of this.connectionFactories) { - await connectionFactory.shutdown(); - } - } + async teardown(): Promise {} protected registerSupabaseAuth(context: system.ServiceContextContainer) { const { configuration } = context; diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 95823cee5..d2c2dad28 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -167,7 +167,7 @@ export class WalStream { } async initSlot(): Promise { - await checkSourceConfiguration(this.connections.pool); + await checkSourceConfiguration(this.connections.pool, PUBLICATION_NAME); const slotName = this.slot_name; @@ -401,7 +401,7 @@ WHERE oid = $1::regclass`, } async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { - if (!descriptor.objectId) { + if (!descriptor.objectId && typeof descriptor.objectId != 'number') { throw new Error('objectId expected'); } const result = await this.storage.resolveTable({ diff --git a/modules/module-postgres/src/replication/WalStreamReplicator.ts b/modules/module-postgres/src/replication/WalStreamReplicator.ts index 95832ce81..8002b7806 100644 --- a/modules/module-postgres/src/replication/WalStreamReplicator.ts +++ b/modules/module-postgres/src/replication/WalStreamReplicator.ts @@ -22,4 +22,9 @@ export class WalStreamReplicator extends replication.AbstractReplicator { + await super.stop(); + await this.connectionFactory.shutdown(); + } } diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index 6e855fd37..6895b10dc 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -106,7 +106,7 @@ export class WalStreamTestContext { start ??= '0'; let checkpoint = await this.getCheckpoint(options); const map = new Map([[bucket, start]]); - const batch = await this.storage!.getBucketDataBatch(checkpoint, map); + const batch = this.storage!.getBucketDataBatch(checkpoint, map); const batches = await fromAsync(batch); return batches[0]?.batch.data ?? []; } From 9250b1912fba1421c73d8367e3502e4cacc739cd Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 16:18:36 +0200 Subject: [PATCH 129/298] move toSQLite function to common --- .../module-mysql/src/common/common-index.ts | 1 + .../src/common/mysql-to-sqlite.ts | 10 ++++++++++ .../src/replication/MysqlBinLogStream.ts | 19 +++++-------------- 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 modules/module-mysql/src/common/mysql-to-sqlite.ts diff --git a/modules/module-mysql/src/common/common-index.ts b/modules/module-mysql/src/common/common-index.ts index 0f8cf357f..5290bdb65 100644 --- a/modules/module-mysql/src/common/common-index.ts +++ b/modules/module-mysql/src/common/common-index.ts @@ -1,4 +1,5 @@ export * from './check-source-configuration.js'; export * from './get-replication-columns.js'; +export * from './mysql-to-sqlite.js'; export * from './read-master-gtid.js'; export * from './ReplicatedGTID.js'; diff --git a/modules/module-mysql/src/common/mysql-to-sqlite.ts b/modules/module-mysql/src/common/mysql-to-sqlite.ts new file mode 100644 index 000000000..3dd4c8ee1 --- /dev/null +++ b/modules/module-mysql/src/common/mysql-to-sqlite.ts @@ -0,0 +1,10 @@ +import * as sync_rules from '@powersync/service-sync-rules'; + +export function toSQLiteRow(row: Record): sync_rules.SqliteRow { + for (let key in row) { + if (row[key] instanceof Date) { + row[key] = row[key].toISOString(); + } + } + return sync_rules.toSyncRulesRow(row); +} diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index 34ff55019..9aeb598a3 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -352,15 +352,6 @@ AND table_type = 'BASE TABLE';`, }); } - private transformMysqlToSynRulesRow(row: Record): sync_rules.SqliteRow { - for (let key in row) { - if (row[key] instanceof Date) { - row[key] = row[key].toISOString(); - } - } - return sync_rules.toSyncRulesRow(row); - } - private async snapshotTable(batch: storage.BucketStorageBatch, db: mysql.Pool, table: storage.SourceTable) { logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); const results = await db.query(`SELECT * FROM ${table.schema}.${table.table}`); @@ -369,7 +360,7 @@ AND table_type = 'BASE TABLE';`, tag: 'insert', sourceTable: table, before: undefined, - after: this.transformMysqlToSynRulesRow(record) + after: common.toSQLiteRow(record) }); } await batch.flush(); @@ -414,22 +405,22 @@ AND table_type = 'BASE TABLE';`, tag: 'insert', sourceTable: msg.sourceTable, before: undefined, - after: this.transformMysqlToSynRulesRow(msg.data) + after: common.toSQLiteRow(msg.data) }); } else if (msg.type == 'update') { // rows_replicated_total.add(1); return await batch.save({ tag: 'update', sourceTable: msg.sourceTable, - before: msg.previous_data ? this.transformMysqlToSynRulesRow(msg.previous_data) : undefined, - after: this.transformMysqlToSynRulesRow(msg.data) + before: msg.previous_data ? common.toSQLiteRow(msg.previous_data) : undefined, + after: common.toSQLiteRow(msg.data) }); } else if (msg.type == 'delete') { // rows_replicated_total.add(1); return await batch.save({ tag: 'delete', sourceTable: msg.sourceTable, - before: this.transformMysqlToSynRulesRow(msg.data), + before: common.toSQLiteRow(msg.data), after: undefined }); } From bf2725afe51414189f29b010813aa15f83e90cf7 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 16:55:54 +0200 Subject: [PATCH 130/298] update types --- .../src/replication/MysqlBinLogStream.ts | 236 +++++------------- .../src/replication/WalStream.ts | 48 +++- .../service-core/src/storage/BucketStorage.ts | 14 +- .../service-core/test/src/compacting.test.ts | 15 +- .../test/src/data_storage.test.ts | 110 ++++---- packages/service-core/test/src/sync.test.ts | 20 +- 6 files changed, 179 insertions(+), 264 deletions(-) diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index 9aeb598a3..a98f9641a 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -13,36 +13,17 @@ import * as zongji_utils from './zongji/zongji-utils.js'; export interface BinLogStreamOptions { pool: mysql.Pool; connection_config: NormalizedMySQLConnectionConfig; - storage: storage.SyncRulesBucketStorage; abort_signal: AbortSignal; } -interface InitResult { - needsInitialSync: boolean; -} - -type Data = Record; -type EventType = 'insert' | 'update' | 'delete'; - -interface DatabaseEvent { - database: string; - table: string; - type: EventType; - ts: number; - xid: number; - xoffset?: number; - gtid: string; - commit?: boolean; - data: Data; - old?: Data; -} - interface MysqlRelId { schema: string; name: string; } +export type Data = Record; + /** * MySQL does not have same relation structure. Just returning unique key as string. * @param source @@ -51,98 +32,12 @@ function getMysqlRelId(source: MysqlRelId): string { return `${source.schema}.${source.name}`; } -async function getReplicationKeyColumns(db: mysql.Pool, relId: MysqlRelId) { - const primaryKeyQuery = ` - SELECT s.COLUMN_NAME AS name, c.DATA_TYPE AS type - FROM INFORMATION_SCHEMA.STATISTICS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? - AND s.TABLE_NAME = ? - AND s.INDEX_NAME = 'PRIMARY' - ORDER BY s.SEQ_IN_INDEX; - `; - const primaryKeyRows = await db.execute(primaryKeyQuery, [relId.schema, relId.name]); - - if (primaryKeyRows[0].length > 0) { - logger.info(`Found primary key, returning it: ${primaryKeyRows[0].reduce((a, b) => `${a}, "${b.name}"`, '')}`); - return { - columns: (primaryKeyRows[0] as RowDataPacket[]).map((row) => ({ - name: row.name as string, - // Ignoring MySQL types: we should check if they are used. - typeOid: -1 - })), - replicationIdentity: 'default' - }; - } - - // TODO: test code with tables with unique keys, compound key etc. - // No primary key, find the first valid unique key - const uniqueKeyQuery = ` - SELECT s.INDEX_NAME, s.COLUMN_NAME, c.DATA_TYPE, s.NON_UNIQUE, s.NULLABLE - FROM INFORMATION_SCHEMA.STATISTICS s - JOIN INFORMATION_SCHEMA.COLUMNS c - ON s.TABLE_SCHEMA = c.TABLE_SCHEMA - AND s.TABLE_NAME = c.TABLE_NAME - AND s.COLUMN_NAME = c.COLUMN_NAME - WHERE s.TABLE_SCHEMA = ? - AND s.TABLE_NAME = ? - AND s.INDEX_NAME != 'PRIMARY' - AND s.NON_UNIQUE = 0 - ORDER BY s.SEQ_IN_INDEX; - `; - const uniqueKeyRows = await db.execute(uniqueKeyQuery, [relId.schema, relId.name]); - - const currentUniqueKey = uniqueKeyRows[0]?.[0]?.INDEX_NAME ?? ''; - const uniqueKeyColumns: RowDataPacket[] = []; - for (const row of uniqueKeyRows[0]) { - if (row.INDEX_NAME === currentUniqueKey) { - uniqueKeyColumns.push(row); - } - } - if (uniqueKeyColumns.length > 0) { - logger.info('Found unique key, returning it'); - return { - columns: uniqueKeyColumns.map((col) => ({ - name: col.COLUMN_NAME as string, - // Ignoring MySQL types: we should check if they are used. - typeOid: -1 - })), - replicationIdentity: 'index' - }; - } - - logger.info('No unique key found, returning all columns'); - const allColumnsQuery = ` - SELECT COLUMN_NAME AS name - FROM INFORMATION_SCHEMA.COLUMNS - WHERE TABLE_SCHEMA = ? - AND TABLE_NAME = ? - ORDER BY ORDINAL_POSITION; - `; - const allColumnsRows = await db.execute(allColumnsQuery, [relId.schema, relId.name]); - - return { - columns: (allColumnsRows[0] as RowDataPacket[]).map((row) => ({ - name: row.name as string, - typeOid: -1 - })), - replicationIdentity: 'full' - }; -} - export class MysqlBinLogStream { sync_rules: sync_rules.SqlSyncRules; group_id: number; - connection_id = 1; - private readonly storage: storage.SyncRulesBucketStorage; - private slot_name: string; - private abort_signal: AbortSignal; private pool: mysql.Pool; @@ -152,22 +47,19 @@ export class MysqlBinLogStream { this.storage = options.storage; this.sync_rules = options.storage.sync_rules; this.group_id = options.storage.group_id; - this.slot_name = options.storage.slot_name; this.pool = options.pool; this.abort_signal = options.abort_signal; - this.abort_signal.addEventListener( - 'abort', - () => { - // TODO close things - }, - { once: true } - ); } get connectionTag() { - // TODO - return 'default'; + return this.options.connection_config.tag; + } + + get connectionId() { + // TODO this is currently hardcoded to 1 in most places + // return this.options.connection_config.id; + return 1; } get stopped() { @@ -177,7 +69,7 @@ export class MysqlBinLogStream { async handleRelation(batch: storage.BucketStorageBatch, entity: storage.SourceEntityDescriptor, snapshot: boolean) { const result = await this.storage.resolveTable({ group_id: this.group_id, - connection_id: this.connection_id, + connection_id: this.connectionId, connection_tag: this.connectionTag, entity_descriptor: entity, sync_rules: this.sync_rules @@ -196,22 +88,22 @@ export class MysqlBinLogStream { // Truncate this table, in case a previous snapshot was interrupted. await batch.truncate([result.table]); - // TODO: put zero GTID somewhere. let gtid: common.ReplicatedGTID; // Start the snapshot inside a transaction. // We use a dedicated connection for this. + const connection = await this.pool.getConnection(); try { - await this.pool.query('BEGIN'); + await connection.query('BEGIN'); try { gtid = await common.readMasterGtid(this.pool); - await this.snapshotTable(batch, this.pool, result.table); - await this.pool.query('COMMIT'); + await this.snapshotTable(batch, result.table); + await connection.query('COMMIT'); } catch (e) { - await this.pool.query('ROLLBACK'); + await connection.query('ROLLBACK'); throw e; } } finally { - await this.pool.end(); + await connection.end(); } const [table] = await batch.markSnapshotDone([result.table], gtid.comparable); return table; @@ -222,7 +114,6 @@ export class MysqlBinLogStream { async getQualifiedTableNames( batch: storage.BucketStorageBatch, - db: mysql.Pool, tablePattern: sync_rules.TablePattern ): Promise { if (tablePattern.connectionTag != this.connectionTag) { @@ -232,7 +123,7 @@ export class MysqlBinLogStream { let tableRows: any[]; const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; if (tablePattern.isWildcard) { - const result = await db.query( + const result = await this.pool.query( `SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?; @@ -241,7 +132,7 @@ WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?; ); tableRows = result[0]; } else { - const result = await db.query( + const result = await this.pool.query( `SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; @@ -258,7 +149,7 @@ WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; continue; } - const rs = await db.query( + const rs = await this.pool.query( `SELECT 1 FROM information_schema.tables WHERE table_schema = ? AND table_name = ? @@ -292,20 +183,18 @@ AND table_type = 'BASE TABLE';`, return result; } - async initSlot(): Promise { + /** + * Checks if the initial sync has been completed yet. + */ + protected async checkInitialReplicated(): Promise { await common.checkSourceConfiguration(this.pool); - const slotName = this.slot_name; - const status = await this.storage.getStatus(); if (status.snapshot_done && status.checkpoint_lsn) { - logger.info(`${slotName} Initial replication already done`); - // Success - logger.info(`MySQL not using Slots ${slotName} appears healthy`); - return { needsInitialSync: false }; + logger.info(`Initial replication already done. MySQL appears healthy`); + return true; } - - return { needsInitialSync: true }; + return false; } /** @@ -314,35 +203,31 @@ AND table_type = 'BASE TABLE';`, * If (partial) replication was done before on this slot, this clears the state * and starts again from scratch. */ - async startInitialReplication() { - const db = this.pool; - - const slotName = this.slot_name; - + async replicateInitial() { await this.storage.clear(); - const headGTID = await common.readMasterGtid(db); + const headGTID = await common.readMasterGtid(this.pool); logger.info(`Using GTID:: '${headGTID}'`); - await db.query('BEGIN'); + await this.pool.query('BEGIN'); try { - logger.info(`${slotName} Starting initial replication`); - await this.initialReplication(db, headGTID); - logger.info(`${slotName} Initial replication done`); - await db.query('COMMIT'); + logger.info(`Starting initial replication`); + await this.initialReplication(headGTID); + logger.info(`Initial replication done`); + await this.pool.query('COMMIT'); } catch (e) { - await db.query('ROLLBACK'); + await this.pool.query('ROLLBACK'); throw e; } } - async initialReplication(db: mysql.Pool, gtid: common.ReplicatedGTID) { + async initialReplication(gtid: common.ReplicatedGTID) { const sourceTables = this.sync_rules.getSourceTables(); // TODO fix database default schema if not provided explicitly in the sync rules // .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); await this.storage.startBatch({}, async (batch) => { for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, db, tablePattern); + const tables = await this.getQualifiedTableNames(batch, tablePattern); for (let table of tables) { - await this.snapshotTable(batch, db, table); + await this.snapshotTable(batch, table); await batch.markSnapshotDone([table], gtid.comparable); // await touch(); @@ -352,12 +237,13 @@ AND table_type = 'BASE TABLE';`, }); } - private async snapshotTable(batch: storage.BucketStorageBatch, db: mysql.Pool, table: storage.SourceTable) { - logger.info(`${this.slot_name} Replicating ${table.qualifiedName}`); - const results = await db.query(`SELECT * FROM ${table.schema}.${table.table}`); - for (let record of results[0]) { + private async snapshotTable(batch: storage.BucketStorageBatch, table: storage.SourceTable) { + logger.info(`Replicating ${table.qualifiedName}`); + // TODO this could be large. Should we chunk this + const [rows] = await this.pool.query(`SELECT * FROM ${table.schema}.${table.table}`); + for (const record of rows) { await batch.save({ - tag: 'insert', + tag: storage.SaveOperationTag.INSERT, sourceTable: table, before: undefined, after: common.toSQLiteRow(record) @@ -369,7 +255,7 @@ AND table_type = 'BASE TABLE';`, async writeChanges( batch: storage.BucketStorageBatch, msg: { - type: EventType; + type: storage.SaveOperationTag; data: Data[]; previous_data?: Data[]; database: string; @@ -390,7 +276,7 @@ AND table_type = 'BASE TABLE';`, async writeChange( batch: storage.BucketStorageBatch, msg: { - type: EventType; + type: storage.SaveOperationTag; data: Data; previous_data?: Data; database: string; @@ -402,7 +288,7 @@ AND table_type = 'BASE TABLE';`, if (msg.type == 'insert') { // rows_replicated_total.add(1); return await batch.save({ - tag: 'insert', + tag: storage.SaveOperationTag.INSERT, sourceTable: msg.sourceTable, before: undefined, after: common.toSQLiteRow(msg.data) @@ -410,7 +296,7 @@ AND table_type = 'BASE TABLE';`, } else if (msg.type == 'update') { // rows_replicated_total.add(1); return await batch.save({ - tag: 'update', + tag: storage.SaveOperationTag.UPDATE, sourceTable: msg.sourceTable, before: msg.previous_data ? common.toSQLiteRow(msg.previous_data) : undefined, after: common.toSQLiteRow(msg.data) @@ -418,7 +304,7 @@ AND table_type = 'BASE TABLE';`, } else if (msg.type == 'delete') { // rows_replicated_total.add(1); return await batch.save({ - tag: 'delete', + tag: storage.SaveOperationTag.DELETE, sourceTable: msg.sourceTable, before: common.toSQLiteRow(msg.data), after: undefined @@ -441,20 +327,18 @@ AND table_type = 'BASE TABLE';`, } async initReplication() { - const result = await this.initSlot(); - await this.loadTables(); - if (result.needsInitialSync) { - await this.startInitialReplication(); - } - } + const initialReplicationCompleted = await this.checkInitialReplicated(); - private async loadTables() { const sourceTables = this.sync_rules.getSourceTables(); await this.storage.startBatch({}, async (batch) => { for (let tablePattern of sourceTables) { - await this.getQualifiedTableNames(batch, this.pool, tablePattern); + await this.getQualifiedTableNames(batch, tablePattern); } }); + + if (!initialReplicationCompleted) { + await this.replicateInitial(); + } } async streamChanges() { @@ -501,14 +385,14 @@ AND table_type = 'BASE TABLE';`, const writeTableInfo = evt.tableMap[evt.tableId]; await this.writeChanges(batch, { - type: 'insert', + type: storage.SaveOperationTag.INSERT, data: evt.rows, database: writeTableInfo.parentSchema, table: writeTableInfo.tableName, // TODO cleanup sourceTable: ( await this.storage.resolveTable({ - connection_id: this.connection_id, + connection_id: this.connectionId, connection_tag: this.connectionTag, entity_descriptor: { name: writeTableInfo.tableName, @@ -528,7 +412,7 @@ AND table_type = 'BASE TABLE';`, case zongji_utils.eventIsUpdateMutation(evt): const updateTableInfo = evt.tableMap[evt.tableId]; await this.writeChanges(batch, { - type: 'update', + type: storage.SaveOperationTag.UPDATE, data: evt.rows.map((row) => row.after), previous_data: evt.rows.map((row) => row.before), database: updateTableInfo.parentSchema, @@ -536,7 +420,7 @@ AND table_type = 'BASE TABLE';`, // TODO cleanup sourceTable: ( await this.storage.resolveTable({ - connection_id: this.connection_id, + connection_id: this.connectionId, connection_tag: this.connectionTag, entity_descriptor: { name: updateTableInfo.tableName, @@ -557,14 +441,14 @@ AND table_type = 'BASE TABLE';`, // TODO, can multiple tables be present? const deleteTableInfo = evt.tableMap[evt.tableId]; await this.writeChanges(batch, { - type: 'delete', + type: storage.SaveOperationTag.DELETE, data: evt.rows, database: deleteTableInfo.parentSchema, table: deleteTableInfo.tableName, // TODO cleanup sourceTable: ( await this.storage.resolveTable({ - connection_id: this.connection_id, + connection_id: this.connectionId, connection_tag: this.connectionTag, entity_descriptor: { name: deleteTableInfo.tableName, diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 95823cee5..de71474e6 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -1,11 +1,11 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import * as util from '../utils/pgwire_utils.js'; import { container, errors, logger } from '@powersync/lib-services-framework'; +import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; +import * as util from '../utils/pgwire_utils.js'; +import { PgManager } from './PgManager.js'; import { getPgOutputRelation, getRelId } from './PgRelation.js'; -import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; import { checkSourceConfiguration, getReplicationIdentityColumns } from './replication-utils.js'; -import { PgManager } from './PgManager.js'; export const ZERO_LSN = '00000000/00000000'; export const PUBLICATION_NAME = 'powersync'; @@ -389,7 +389,12 @@ WHERE oid = $1::regclass`, for (let record of WalStream.getQueryData(rows)) { // This auto-flushes when the batch reaches its size limit - await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); + await batch.save({ + tag: storage.SaveOperationTag.INSERT, + sourceTable: table, + before: undefined, + after: record + }); } at += rows.length; Metrics.getInstance().rows_replicated_total.add(rows.length); @@ -471,29 +476,48 @@ WHERE oid = $1::regclass`, if (msg.lsn == null) { return null; } - if (msg.tag == 'insert' || msg.tag == 'update' || msg.tag == 'delete') { + if ( + msg.tag == storage.SaveOperationTag.INSERT || + msg.tag == storage.SaveOperationTag.UPDATE || + msg.tag == storage.SaveOperationTag.DELETE + ) { const table = this.getTable(getRelId(msg.relation)); if (!table.syncAny) { logger.debug(`Table ${table.qualifiedName} not used in sync rules - skipping`); return null; } - if (msg.tag == 'insert') { + if (msg.tag == storage.SaveOperationTag.INSERT) { Metrics.getInstance().rows_replicated_total.add(1); const baseRecord = util.constructAfterRecord(msg); - return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); - } else if (msg.tag == 'update') { + return await batch.save({ + tag: storage.SaveOperationTag.INSERT, + sourceTable: table, + before: undefined, + after: baseRecord + }); + } else if (msg.tag == storage.SaveOperationTag.UPDATE) { Metrics.getInstance().rows_replicated_total.add(1); // "before" may be null if the replica id columns are unchanged // It's fine to treat that the same as an insert. const before = util.constructBeforeRecord(msg); const after = util.constructAfterRecord(msg); - return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); - } else if (msg.tag == 'delete') { + return await batch.save({ + tag: storage.SaveOperationTag.UPDATE, + sourceTable: table, + before: before, + after: after + }); + } else if (msg.tag == storage.SaveOperationTag.DELETE) { Metrics.getInstance().rows_replicated_total.add(1); const before = util.constructBeforeRecord(msg)!; - return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); + return await batch.save({ + tag: storage.SaveOperationTag.DELETE, + sourceTable: table, + before: before, + after: undefined + }); } } else if (msg.tag == 'truncate') { let tables: storage.SourceTable[] = []; diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 8cf125149..dd221af76 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -8,8 +8,8 @@ import { ToastableSqliteRow } from '@powersync/service-sync-rules'; import * as util from '../util/util-index.js'; -import { SourceTable } from './SourceTable.js'; import { SourceEntityDescriptor } from './SourceEntity.js'; +import { SourceTable } from './SourceTable.js'; export interface BucketStorageFactory { /** @@ -358,15 +358,21 @@ export interface SaveBucketData { export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete; +export enum SaveOperationTag { + INSERT = 'insert', + UPDATE = 'update', + DELETE = 'delete' +} + export interface SaveInsert { - tag: 'insert'; + tag: SaveOperationTag.INSERT; sourceTable: SourceTable; before?: undefined; after: SqliteRow; } export interface SaveUpdate { - tag: 'update'; + tag: SaveOperationTag.UPDATE; sourceTable: SourceTable; /** @@ -383,7 +389,7 @@ export interface SaveUpdate { } export interface SaveDelete { - tag: 'delete'; + tag: SaveOperationTag.DELETE; sourceTable: SourceTable; before: SqliteRow; after?: undefined; diff --git a/packages/service-core/test/src/compacting.test.ts b/packages/service-core/test/src/compacting.test.ts index 8ae3ab588..559b92968 100644 --- a/packages/service-core/test/src/compacting.test.ts +++ b/packages/service-core/test/src/compacting.test.ts @@ -1,3 +1,4 @@ +import { SaveOperationTag } from '@/storage/BucketStorage.js'; import { MongoCompactOptions } from '@/storage/mongo/MongoCompactor.js'; import { SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; @@ -29,7 +30,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1' } @@ -37,7 +38,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2' } @@ -45,7 +46,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 't2' } @@ -119,7 +120,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1' } @@ -127,7 +128,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2' } @@ -135,7 +136,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 't1' } @@ -143,7 +144,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 't2' } diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index 45fce8c0b..edffb601c 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -1,4 +1,4 @@ -import { BucketDataBatchOptions } from '@/storage/BucketStorage.js'; +import { BucketDataBatchOptions, SaveOperationTag } from '@/storage/BucketStorage.js'; import { RequestParameters, SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { fromAsync, oneFromAsync } from './stream_utils.js'; @@ -25,7 +25,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2', id1: 'user3', @@ -36,7 +36,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1', id1: 'user1', @@ -68,7 +68,7 @@ bucket_definitions: const result1 = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'user1', group_id: 'group1' @@ -78,7 +78,7 @@ bucket_definitions: const result2 = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'user1', group_id: 'group2' @@ -116,7 +116,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1', group_id: 'group1', @@ -157,7 +157,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1', group_id: 'group1', @@ -167,7 +167,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 't1', group_id: 'group1', @@ -200,7 +200,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1' @@ -208,7 +208,7 @@ bucket_definitions: }); await batch.save({ sourceTable, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 'test1' } @@ -261,7 +261,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace1', userId: 'u1' @@ -307,7 +307,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace1', visibility: 'public' @@ -316,7 +316,7 @@ bucket_definitions: await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace2', visibility: 'private' @@ -325,7 +325,7 @@ bucket_definitions: await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace3', visibility: 'public' @@ -375,7 +375,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace1', visibility: 'public' @@ -384,7 +384,7 @@ bucket_definitions: await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace2', visibility: 'private' @@ -393,7 +393,7 @@ bucket_definitions: await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace3', user_id: 'u1', @@ -403,7 +403,7 @@ bucket_definitions: await batch.save({ sourceTable: WORKSPACE_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'workspace4', user_id: 'u2', @@ -457,7 +457,7 @@ bucket_definitions: const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', client_id: 'client1a', @@ -466,7 +466,7 @@ bucket_definitions: }); await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 'test1', client_id: 'client1b', @@ -476,7 +476,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test2', client_id: 'client2', @@ -515,7 +515,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1' @@ -528,7 +528,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 'test1' } @@ -540,7 +540,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 'test1' } @@ -590,7 +590,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1' @@ -603,7 +603,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 'test1', description: undefined @@ -612,7 +612,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 'test1', description: undefined @@ -621,7 +621,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 'test1' } @@ -633,7 +633,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 'test1', description: undefined @@ -642,7 +642,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 'test1', description: undefined @@ -651,7 +651,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 'test1' } @@ -704,7 +704,7 @@ bucket_definitions: await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2', id1: 'user3', @@ -745,7 +745,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1a' @@ -754,7 +754,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test2', description: 'test2a' @@ -770,7 +770,7 @@ bucket_definitions: // b await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1b' @@ -779,7 +779,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, before: { id: 'test1' }, @@ -791,7 +791,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, before: { id: 'test2' }, @@ -804,7 +804,7 @@ bucket_definitions: // c await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 'test2', description: 'test2c' @@ -814,7 +814,7 @@ bucket_definitions: // d await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test4', description: 'test4d' @@ -823,7 +823,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, before: { id: 'test4' }, @@ -879,7 +879,7 @@ bucket_definitions: const result1 = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1a' @@ -893,7 +893,7 @@ bucket_definitions: // Unchanged, but has a before id await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, before: { id: 'test1', description: 'test1a' @@ -909,7 +909,7 @@ bucket_definitions: // Delete await batch.save({ sourceTable, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 'test1', description: 'test1b' @@ -971,7 +971,7 @@ bucket_definitions: const result1 = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1a' @@ -985,7 +985,7 @@ bucket_definitions: // Unchanged, but has a before id await batch.save({ sourceTable, - tag: 'update', + tag: SaveOperationTag.UPDATE, before: { id: 'test1', description: 'test1a' @@ -1001,7 +1001,7 @@ bucket_definitions: // Delete await batch.save({ sourceTable, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 'test1', description: 'test1a' @@ -1061,7 +1061,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1' @@ -1070,7 +1070,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'large1', description: largeDescription @@ -1080,7 +1080,7 @@ bucket_definitions: // Large enough to split the returned batch await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'large2', description: largeDescription @@ -1089,7 +1089,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test3', description: 'test3' @@ -1153,7 +1153,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test1', description: 'test1' @@ -1162,7 +1162,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'large1', description: largeDescription @@ -1172,7 +1172,7 @@ bucket_definitions: // Large enough to split the returned batch await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'large2', description: largeDescription @@ -1181,7 +1181,7 @@ bucket_definitions: await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 'test3', description: 'test3' @@ -1241,7 +1241,7 @@ bucket_definitions: for (let i = 1; i <= 6; i++) { await batch.save({ sourceTable, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: `test${i}`, description: `test${i}` diff --git a/packages/service-core/test/src/sync.test.ts b/packages/service-core/test/src/sync.test.ts index 496837e1e..9d500dff9 100644 --- a/packages/service-core/test/src/sync.test.ts +++ b/packages/service-core/test/src/sync.test.ts @@ -1,5 +1,5 @@ // FIXME: This needs to come from a better place -import { ZERO_LSN } from '@/storage/storage-index.js'; +import { SaveOperationTag, ZERO_LSN } from '@/storage/storage-index.js'; import { RequestTracker } from '@/sync/RequestTracker.js'; import { streamResponse } from '@/sync/sync.js'; import { StreamingSyncLine } from '@/util/protocol-types.js'; @@ -45,7 +45,7 @@ function defineTests(factory: StorageFactory) { const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1', description: 'Test 1' @@ -54,7 +54,7 @@ function defineTests(factory: StorageFactory) { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2', description: 'Test 2' @@ -94,7 +94,7 @@ function defineTests(factory: StorageFactory) { const result = await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1', description: 'Test\n"string"', @@ -179,7 +179,7 @@ function defineTests(factory: StorageFactory) { await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1', description: 'Test 1' @@ -194,7 +194,7 @@ function defineTests(factory: StorageFactory) { await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2', description: 'Test 2' @@ -261,7 +261,7 @@ function defineTests(factory: StorageFactory) { await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1', description: 'Test 1' @@ -270,7 +270,7 @@ function defineTests(factory: StorageFactory) { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2', description: 'Test 2' @@ -309,7 +309,7 @@ function defineTests(factory: StorageFactory) { await storage.startBatch({}, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 't1', description: 'Test 1b' @@ -318,7 +318,7 @@ function defineTests(factory: StorageFactory) { await batch.save({ sourceTable: TEST_TABLE, - tag: 'update', + tag: SaveOperationTag.UPDATE, after: { id: 't2', description: 'Test 2b' From 4378c779f94065e7dba28f76c1c590a604c15fee Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Thu, 22 Aug 2024 17:18:07 +0200 Subject: [PATCH 131/298] move --- .../src/common/get-replication-columns.ts | 2 +- .../src/common/read-master-gtid.ts | 2 +- .../{ => stream}/MysqlBinLogStream.ts | 167 +++++++++--------- modules/module-mysql/src/utils/mysql_utils.ts | 2 +- 4 files changed, 84 insertions(+), 89 deletions(-) rename modules/module-mysql/src/replication/{ => stream}/MysqlBinLogStream.ts (91%) diff --git a/modules/module-mysql/src/common/get-replication-columns.ts b/modules/module-mysql/src/common/get-replication-columns.ts index 563b8ce11..63b58b53a 100644 --- a/modules/module-mysql/src/common/get-replication-columns.ts +++ b/modules/module-mysql/src/common/get-replication-columns.ts @@ -3,7 +3,7 @@ import mysql from 'mysql2/promise'; import * as mysql_utils from '../utils/mysql_utils.js'; export type GetReplicationColumnsOptions = { - db: mysql.Pool; + db: mysql.Connection; schema: string; table_name: string; }; diff --git a/modules/module-mysql/src/common/read-master-gtid.ts b/modules/module-mysql/src/common/read-master-gtid.ts index 6044bbe38..c80a1b32a 100644 --- a/modules/module-mysql/src/common/read-master-gtid.ts +++ b/modules/module-mysql/src/common/read-master-gtid.ts @@ -6,7 +6,7 @@ import { ReplicatedGTID } from './ReplicatedGTID.js'; /** * Gets the current master HEAD GTID */ -export async function readMasterGtid(db: mysql.Pool): Promise { +export async function readMasterGtid(db: mysql.Connection): Promise { // Get the GTID const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts similarity index 91% rename from modules/module-mysql/src/replication/MysqlBinLogStream.ts rename to modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts index a98f9641a..7e5737ff7 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts @@ -2,7 +2,7 @@ import { logger } from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import async from 'async'; -import { storage } from '@powersync/service-core'; +import { framework, storage } from '@powersync/service-core'; import mysql, { RowDataPacket } from 'mysql2/promise'; import ZongJi, { BinLogEvent } from '@vlasky/zongji'; @@ -96,7 +96,7 @@ export class MysqlBinLogStream { await connection.query('BEGIN'); try { gtid = await common.readMasterGtid(this.pool); - await this.snapshotTable(batch, result.table); + await this.snapshotTable(batch, connection, result.table); await connection.query('COMMIT'); } catch (e) { await connection.query('ROLLBACK'); @@ -198,7 +198,7 @@ AND table_type = 'BASE TABLE';`, } /** - * Start initial replication. + * Snapshots initial tables * * If (partial) replication was done before on this slot, this clears the state * and starts again from scratch. @@ -210,7 +210,18 @@ AND table_type = 'BASE TABLE';`, await this.pool.query('BEGIN'); try { logger.info(`Starting initial replication`); - await this.initialReplication(headGTID); + const sourceTables = this.sync_rules.getSourceTables(); + await this.storage.startBatch({}, async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, tablePattern); + for (let table of tables) { + await this.snapshotTable(batch, table); + await batch.markSnapshotDone([table], headGTID.comparable); + await framework.container.probes.touch(); + } + } + await batch.commit(headGTID.comparable); + }); logger.info(`Initial replication done`); await this.pool.query('COMMIT'); } catch (e) { @@ -219,28 +230,14 @@ AND table_type = 'BASE TABLE';`, } } - async initialReplication(gtid: common.ReplicatedGTID) { - const sourceTables = this.sync_rules.getSourceTables(); - // TODO fix database default schema if not provided explicitly in the sync rules - // .map((table) => new sync_rules.TablePattern('mydatabase', table.tablePattern)); - await this.storage.startBatch({}, async (batch) => { - for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, table); - await batch.markSnapshotDone([table], gtid.comparable); - - // await touch(); - } - } - await batch.commit(gtid.comparable); - }); - } - - private async snapshotTable(batch: storage.BucketStorageBatch, table: storage.SourceTable) { + private async snapshotTable( + batch: storage.BucketStorageBatch, + connection: mysql.Connection, + table: storage.SourceTable + ) { logger.info(`Replicating ${table.qualifiedName}`); // TODO this could be large. Should we chunk this - const [rows] = await this.pool.query(`SELECT * FROM ${table.schema}.${table.table}`); + const [rows] = await connection.query(`SELECT * FROM ${table.schema}.${table.table}`); for (const record of rows) { await batch.save({ tag: storage.SaveOperationTag.INSERT, @@ -252,68 +249,6 @@ AND table_type = 'BASE TABLE';`, await batch.flush(); } - async writeChanges( - batch: storage.BucketStorageBatch, - msg: { - type: storage.SaveOperationTag; - data: Data[]; - previous_data?: Data[]; - database: string; - table: string; - sourceTable: storage.SourceTable; - } - ): Promise { - for (const [index, row] of msg.data.entries()) { - await this.writeChange(batch, { - ...msg, - data: row, - previous_data: msg.previous_data?.[index] - }); - } - return null; - } - - async writeChange( - batch: storage.BucketStorageBatch, - msg: { - type: storage.SaveOperationTag; - data: Data; - previous_data?: Data; - database: string; - table: string; - sourceTable: storage.SourceTable; - } - ): Promise { - if (msg.type == 'insert' || msg.type == 'update' || msg.type == 'delete') { - if (msg.type == 'insert') { - // rows_replicated_total.add(1); - return await batch.save({ - tag: storage.SaveOperationTag.INSERT, - sourceTable: msg.sourceTable, - before: undefined, - after: common.toSQLiteRow(msg.data) - }); - } else if (msg.type == 'update') { - // rows_replicated_total.add(1); - return await batch.save({ - tag: storage.SaveOperationTag.UPDATE, - sourceTable: msg.sourceTable, - before: msg.previous_data ? common.toSQLiteRow(msg.previous_data) : undefined, - after: common.toSQLiteRow(msg.data) - }); - } else if (msg.type == 'delete') { - // rows_replicated_total.add(1); - return await batch.save({ - tag: storage.SaveOperationTag.DELETE, - sourceTable: msg.sourceTable, - before: common.toSQLiteRow(msg.data), - after: undefined - }); - } - } - return null; - } - async replicate() { try { // If anything errors here, the entire replication process is halted, and @@ -516,4 +451,64 @@ AND table_type = 'BASE TABLE';`, }); }); } + + private async writeChanges( + batch: storage.BucketStorageBatch, + msg: { + type: storage.SaveOperationTag; + data: Data[]; + previous_data?: Data[]; + database: string; + table: string; + sourceTable: storage.SourceTable; + } + ): Promise { + for (const [index, row] of msg.data.entries()) { + await this.writeChange(batch, { + ...msg, + data: row, + previous_data: msg.previous_data?.[index] + }); + } + return null; + } + + private async writeChange( + batch: storage.BucketStorageBatch, + msg: { + type: storage.SaveOperationTag; + data: Data; + previous_data?: Data; + database: string; + table: string; + sourceTable: storage.SourceTable; + } + ): Promise { + switch (msg.type) { + case storage.SaveOperationTag.INSERT: + return await batch.save({ + tag: storage.SaveOperationTag.INSERT, + sourceTable: msg.sourceTable, + before: undefined, + after: common.toSQLiteRow(msg.data) + }); + case storage.SaveOperationTag.UPDATE: + return await batch.save({ + tag: storage.SaveOperationTag.UPDATE, + sourceTable: msg.sourceTable, + before: msg.previous_data ? common.toSQLiteRow(msg.previous_data) : undefined, + after: common.toSQLiteRow(msg.data) + }); + + case storage.SaveOperationTag.DELETE: + return await batch.save({ + tag: storage.SaveOperationTag.DELETE, + sourceTable: msg.sourceTable, + before: common.toSQLiteRow(msg.data), + after: undefined + }); + default: + return null; + } + } } diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 06eeddac0..971f61f0d 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -3,7 +3,7 @@ import mysql from 'mysql2/promise'; import * as types from '../types/types.js'; export type RetiredMySQLQueryOptions = { - db: mysql.Pool; + db: mysql.Connection; query: string; params?: any[]; retries?: number; From 912de61136d59dbd0ebae97b6dd28e348cf10d15 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 23 Aug 2024 10:33:21 +0200 Subject: [PATCH 132/298] Made column type optional since for Postgres the column type is not propagated on WalStream schema updates. --- .github/workflows/test.yml | 2 +- .../src/replication/PgRelation.ts | 4 ++-- .../src/replication/replication-utils.ts | 23 +++++++++++-------- .../service-core/src/storage/SourceEntity.ts | 2 +- .../service-core/src/storage/mongo/models.ts | 2 +- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 26b73d197..0c02dd753 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,7 +76,7 @@ jobs: run: pnpm build - name: Test - run: pnpm --filter !./modules test + run: pnpm test --filter !./modules run-postgres-tests: name: Postgres Test diff --git a/modules/module-postgres/src/replication/PgRelation.ts b/modules/module-postgres/src/replication/PgRelation.ts index f2dfdc17a..8eebd0f03 100644 --- a/modules/module-postgres/src/replication/PgRelation.ts +++ b/modules/module-postgres/src/replication/PgRelation.ts @@ -9,7 +9,7 @@ export function getReplicaIdColumns(relation: PgoutputRelation): storage.ColumnD } else { return relation.columns .filter((c) => (c.flags & 0b1) != 0) - .map((c) => ({ name: c.name, typeId: c.typeOid, type: c.typeName ?? '' })); + .map((c) => ({ name: c.name, typeId: c.typeOid } satisfies storage.ColumnDescriptor)); } } export function getRelId(source: PgoutputRelation): number { @@ -27,5 +27,5 @@ export function getPgOutputRelation(source: PgoutputRelation): storage.SourceEnt schema: source.schema, objectId: getRelId(source), replicationColumns: getReplicaIdColumns(source) - }; + } satisfies storage.SourceEntityDescriptor; } diff --git a/modules/module-postgres/src/replication/replication-utils.ts b/modules/module-postgres/src/replication/replication-utils.ts index 1d014c686..a924574e9 100644 --- a/modules/module-postgres/src/replication/replication-utils.ts +++ b/modules/module-postgres/src/replication/replication-utils.ts @@ -28,7 +28,10 @@ export async function getPrimaryKeyColumns( }); return attrRows.rows.map((row) => { - return { name: row[0] as string, typeOid: row[1] as number, type: row[2] as string }; + return { + name: row[0] as string, + typeId: row[1] as number + } satisfies storage.ColumnDescriptor; }); } @@ -43,7 +46,10 @@ export async function getAllColumns(db: pgwire.PgClient, relationId: number): Pr params: [{ type: 'varchar', value: relationId }] }); return attrRows.rows.map((row) => { - return { name: row[0] as string, typeOid: row[1] as number, type: row[2] as string }; + return { + name: row[0] as string, + typeId: row[1] as number + } satisfies storage.ColumnDescriptor; }); } @@ -82,10 +88,7 @@ WHERE oid = $1::oid LIMIT 1`, } } -export async function checkSourceConfiguration(db: pgwire.PgClient) { - // TODO: configurable - const publication_name = 'powersync'; - +export async function checkSourceConfiguration(db: pgwire.PgClient, publicationName: string) { // Check basic config await pgwire_utils.retriedQuery( db, @@ -107,20 +110,20 @@ $$ LANGUAGE plpgsql;` // Check that publication exists const rs = await pgwire_utils.retriedQuery(db, { statement: `SELECT * FROM pg_publication WHERE pubname = $1`, - params: [{ type: 'varchar', value: publication_name }] + params: [{ type: 'varchar', value: publicationName }] }); const row = pgwire.pgwireRows(rs)[0]; if (row == null) { throw new Error( - `Publication '${publication_name}' does not exist. Run: \`CREATE PUBLICATION ${publication_name} FOR ALL TABLES\`, or read the documentation for details.` + `Publication '${publicationName}' does not exist. Run: \`CREATE PUBLICATION ${publicationName} FOR ALL TABLES\`, or read the documentation for details.` ); } if (row.pubinsert == false || row.pubupdate == false || row.pubdelete == false || row.pubtruncate == false) { throw new Error( - `Publication '${publication_name}' does not publish all changes. Create a publication using \`WITH (publish = "insert, update, delete, truncate")\` (the default).` + `Publication '${publicationName}' does not publish all changes. Create a publication using \`WITH (publish = "insert, update, delete, truncate")\` (the default).` ); } if (row.pubviaroot) { - throw new Error(`'${publication_name}' uses publish_via_partition_root, which is not supported.`); + throw new Error(`'${publicationName}' uses publish_via_partition_root, which is not supported.`); } } diff --git a/packages/service-core/src/storage/SourceEntity.ts b/packages/service-core/src/storage/SourceEntity.ts index 3d85d371e..2b0031831 100644 --- a/packages/service-core/src/storage/SourceEntity.ts +++ b/packages/service-core/src/storage/SourceEntity.ts @@ -3,7 +3,7 @@ export interface ColumnDescriptor { /** * The type of the column ie VARCHAR, INT, etc */ - type: string; + type?: string; /** * Some data sources have a type id that can be used to identify the type of the column */ diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index 3c88eab62..9b0191fda 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -61,7 +61,7 @@ export interface SourceTableDocument { schema_name: string; table_name: string; replica_id_columns: string[] | null; - replica_id_columns2: { name: string; type_oid: number | undefined; type: string }[] | undefined; + replica_id_columns2: { name: string; type_oid?: number; type?: string }[] | undefined; snapshot_done: boolean | undefined; } From dae46513b6341839fb724250f5f701aa2dabda5b Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 23 Aug 2024 10:42:36 +0200 Subject: [PATCH 133/298] Fixed (hopefully) the test filtering for the test github action --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0c02dd753..db6422888 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,7 +76,7 @@ jobs: run: pnpm build - name: Test - run: pnpm test --filter !./modules + run: pnpm test --filter='!./modules' run-postgres-tests: name: Postgres Test @@ -142,4 +142,4 @@ jobs: run: pnpm build - name: Test - run: pnpm test --filter ./modules/module-postgres + run: pnpm test --filter='./modules/module-postgres' From c08c4f7ba34946084d5bf91c5496ac905305f98e Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 23 Aug 2024 12:07:01 +0200 Subject: [PATCH 134/298] Corrected exclusion filter for test workflow --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index db6422888..ba0dd13d0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,7 +76,7 @@ jobs: run: pnpm build - name: Test - run: pnpm test --filter='!./modules' + run: pnpm test --filter '!./modules/*' run-postgres-tests: name: Postgres Test From af8db7d44a1451ac41ba6d87a07fd697aebf9c5a Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 23 Aug 2024 12:29:18 +0200 Subject: [PATCH 135/298] Added build:tests directive to postgres module --- modules/module-postgres/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index 7ee37df49..a6de29dad 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -11,6 +11,7 @@ "type": "module", "scripts": { "build": "tsc -b", + "build:tests": "tsc -b test/tsconfig.json", "clean": "rm -rf ./lib && tsc -b --clean", "test": "vitest --no-threads" }, From 4be18469259d3ea7ee593906a92aafd41d79fae0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 23 Aug 2024 14:44:23 +0200 Subject: [PATCH 136/298] Export populate_test_data --- modules/module-postgres/src/utils/utils-index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 modules/module-postgres/src/utils/utils-index.ts diff --git a/modules/module-postgres/src/utils/utils-index.ts b/modules/module-postgres/src/utils/utils-index.ts new file mode 100644 index 000000000..05f8d02cc --- /dev/null +++ b/modules/module-postgres/src/utils/utils-index.ts @@ -0,0 +1 @@ +export * from './populate_test_data.js'; From aefb056d749e4358f10205f5a38faa212f5e5001 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 23 Aug 2024 15:08:13 +0200 Subject: [PATCH 137/298] Export the test utils from postgres module --- modules/module-postgres/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/module-postgres/src/index.ts b/modules/module-postgres/src/index.ts index 04e83e4e6..c6cbaa1c3 100644 --- a/modules/module-postgres/src/index.ts +++ b/modules/module-postgres/src/index.ts @@ -1,5 +1,7 @@ import { PostgresModule } from './module/PostgresModule.js'; +export * from './utils/utils-index.js'; + export const module = new PostgresModule(); export default module; From d1be9323818f862f85afd87bc2952017ca5d4680 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 26 Aug 2024 14:15:40 +0200 Subject: [PATCH 138/298] Moved data generation method to test utils --- .../src/utils/populate_test_data.ts | 27 +------------------ .../test/src/large_batch.test.ts | 3 +-- modules/module-postgres/test/src/util.ts | 27 +++++++++++++++++++ 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/modules/module-postgres/src/utils/populate_test_data.ts b/modules/module-postgres/src/utils/populate_test_data.ts index be4c67729..0431bc301 100644 --- a/modules/module-postgres/src/utils/populate_test_data.ts +++ b/modules/module-postgres/src/utils/populate_test_data.ts @@ -1,5 +1,5 @@ import * as crypto from 'crypto'; -import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads'; +import { isMainThread, parentPort, workerData } from 'node:worker_threads'; import * as pgwire from '@powersync/service-jpgwire'; @@ -50,28 +50,3 @@ async function populateDataInner(options: PopulateDataOptions) { await initialDb.end(); return operation_count; } - -export async function populateData(options: PopulateDataOptions) { - const WORKER_TIMEOUT = 30_000; - - const worker = new Worker(new URL('./populate_test_data.js', import.meta.url), { - workerData: options - }); - const timeout = setTimeout(() => { - // Exits with code 1 below - worker.terminate(); - }, WORKER_TIMEOUT); - try { - return await new Promise((resolve, reject) => { - worker.on('message', resolve); - worker.on('error', reject); - worker.on('exit', (code) => { - if (code !== 0) { - reject(new Error(`Populating data failed with exit code ${code}`)); - } - }); - }); - } finally { - clearTimeout(timeout); - } -} diff --git a/modules/module-postgres/test/src/large_batch.test.ts b/modules/module-postgres/test/src/large_batch.test.ts index 6c1d373eb..167207f7a 100644 --- a/modules/module-postgres/test/src/large_batch.test.ts +++ b/modules/module-postgres/test/src/large_batch.test.ts @@ -1,8 +1,7 @@ import { MONGO_STORAGE_FACTORY, StorageFactory } from '@core-tests/util.js'; -import { populateData } from '@module/utils/populate_test_data.js'; import { describe, expect, test } from 'vitest'; import { env } from './env.js'; -import { TEST_CONNECTION_OPTIONS } from './util.js'; +import { populateData, TEST_CONNECTION_OPTIONS } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; describe('batch replication tests - mongodb', function () { diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 38e9c12bb..70fb87366 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -5,6 +5,8 @@ import * as pgwire from '@powersync/service-jpgwire'; import { env } from './env.js'; import { pgwireRows } from '@powersync/service-jpgwire'; import { logger } from '@powersync/lib-services-framework'; +import { Worker } from 'node:worker_threads'; +import { PopulateDataOptions } from '@module/utils/populate_test_data.js'; // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -87,3 +89,28 @@ export async function getClientCheckpoint( throw new Error('Timeout while waiting for checkpoint'); } + +export async function populateData(options: PopulateDataOptions) { + const WORKER_TIMEOUT = 30_000; + + const worker = new Worker(new URL('./populate_test_data.js', import.meta.url), { + workerData: options + }); + const timeout = setTimeout(() => { + // Exits with code 1 below + worker.terminate(); + }, WORKER_TIMEOUT); + try { + return await new Promise((resolve, reject) => { + worker.on('message', resolve); + worker.on('error', reject); + worker.on('exit', (code) => { + if (code !== 0) { + reject(new Error(`Populating data failed with exit code ${code}`)); + } + }); + }); + } finally { + clearTimeout(timeout); + } +} From 387b48554d5134fab644868a515d4e912e2c39fd Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 26 Aug 2024 14:27:34 +0200 Subject: [PATCH 139/298] Moved data generation utils to tests --- .../module-postgres/src/utils/utils-index.ts | 1 - .../test/src/large_batch.test.ts | 3 ++- .../utils => test/src}/populate_test_data.ts | 27 ++++++++++++++++++- modules/module-postgres/test/src/util.ts | 27 ------------------- 4 files changed, 28 insertions(+), 30 deletions(-) delete mode 100644 modules/module-postgres/src/utils/utils-index.ts rename modules/module-postgres/{src/utils => test/src}/populate_test_data.ts (68%) diff --git a/modules/module-postgres/src/utils/utils-index.ts b/modules/module-postgres/src/utils/utils-index.ts deleted file mode 100644 index 05f8d02cc..000000000 --- a/modules/module-postgres/src/utils/utils-index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './populate_test_data.js'; diff --git a/modules/module-postgres/test/src/large_batch.test.ts b/modules/module-postgres/test/src/large_batch.test.ts index 167207f7a..9e1a4eba9 100644 --- a/modules/module-postgres/test/src/large_batch.test.ts +++ b/modules/module-postgres/test/src/large_batch.test.ts @@ -1,8 +1,9 @@ import { MONGO_STORAGE_FACTORY, StorageFactory } from '@core-tests/util.js'; import { describe, expect, test } from 'vitest'; import { env } from './env.js'; -import { populateData, TEST_CONNECTION_OPTIONS } from './util.js'; +import { TEST_CONNECTION_OPTIONS } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; +import { populateData } from './populate_test_data.js'; describe('batch replication tests - mongodb', function () { // These are slow but consistent tests. diff --git a/modules/module-postgres/src/utils/populate_test_data.ts b/modules/module-postgres/test/src/populate_test_data.ts similarity index 68% rename from modules/module-postgres/src/utils/populate_test_data.ts rename to modules/module-postgres/test/src/populate_test_data.ts index 0431bc301..be4c67729 100644 --- a/modules/module-postgres/src/utils/populate_test_data.ts +++ b/modules/module-postgres/test/src/populate_test_data.ts @@ -1,5 +1,5 @@ import * as crypto from 'crypto'; -import { isMainThread, parentPort, workerData } from 'node:worker_threads'; +import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads'; import * as pgwire from '@powersync/service-jpgwire'; @@ -50,3 +50,28 @@ async function populateDataInner(options: PopulateDataOptions) { await initialDb.end(); return operation_count; } + +export async function populateData(options: PopulateDataOptions) { + const WORKER_TIMEOUT = 30_000; + + const worker = new Worker(new URL('./populate_test_data.js', import.meta.url), { + workerData: options + }); + const timeout = setTimeout(() => { + // Exits with code 1 below + worker.terminate(); + }, WORKER_TIMEOUT); + try { + return await new Promise((resolve, reject) => { + worker.on('message', resolve); + worker.on('error', reject); + worker.on('exit', (code) => { + if (code !== 0) { + reject(new Error(`Populating data failed with exit code ${code}`)); + } + }); + }); + } finally { + clearTimeout(timeout); + } +} diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 70fb87366..38e9c12bb 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -5,8 +5,6 @@ import * as pgwire from '@powersync/service-jpgwire'; import { env } from './env.js'; import { pgwireRows } from '@powersync/service-jpgwire'; import { logger } from '@powersync/lib-services-framework'; -import { Worker } from 'node:worker_threads'; -import { PopulateDataOptions } from '@module/utils/populate_test_data.js'; // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -89,28 +87,3 @@ export async function getClientCheckpoint( throw new Error('Timeout while waiting for checkpoint'); } - -export async function populateData(options: PopulateDataOptions) { - const WORKER_TIMEOUT = 30_000; - - const worker = new Worker(new URL('./populate_test_data.js', import.meta.url), { - workerData: options - }); - const timeout = setTimeout(() => { - // Exits with code 1 below - worker.terminate(); - }, WORKER_TIMEOUT); - try { - return await new Promise((resolve, reject) => { - worker.on('message', resolve); - worker.on('error', reject); - worker.on('exit', (code) => { - if (code !== 0) { - reject(new Error(`Populating data failed with exit code ${code}`)); - } - }); - }); - } finally { - clearTimeout(timeout); - } -} From 91d2cdbcecb43b0f9fa4c577e3506b11e17f6cfc Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 26 Aug 2024 14:31:10 +0200 Subject: [PATCH 140/298] Removed unused export --- modules/module-postgres/src/index.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/module-postgres/src/index.ts b/modules/module-postgres/src/index.ts index c6cbaa1c3..04e83e4e6 100644 --- a/modules/module-postgres/src/index.ts +++ b/modules/module-postgres/src/index.ts @@ -1,7 +1,5 @@ import { PostgresModule } from './module/PostgresModule.js'; -export * from './utils/utils-index.js'; - export const module = new PostgresModule(); export default module; From 3e961bd4a6ea28ba27df79cb819b601ccd2bf30f Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 26 Aug 2024 14:45:05 +0200 Subject: [PATCH 141/298] Updated test tsconfig to include jpgwire --- modules/module-postgres/test/tsconfig.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/module-postgres/test/tsconfig.json b/modules/module-postgres/test/tsconfig.json index a0f67051a..310ddbe33 100644 --- a/modules/module-postgres/test/tsconfig.json +++ b/modules/module-postgres/test/tsconfig.json @@ -23,6 +23,9 @@ }, { "path": "../../../packages/service-core/" + }, + { + "path": "../../../packages/jpgwire/" } ] } From 02fb3c688d3c07c65380acc05e44f828ce1a5cb1 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 26 Aug 2024 16:14:51 +0200 Subject: [PATCH 142/298] Try with outDir specified --- modules/module-postgres/test/tsconfig.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/module-postgres/test/tsconfig.json b/modules/module-postgres/test/tsconfig.json index 310ddbe33..529f82074 100644 --- a/modules/module-postgres/test/tsconfig.json +++ b/modules/module-postgres/test/tsconfig.json @@ -2,8 +2,8 @@ "extends": "../../../tsconfig.base.json", "compilerOptions": { "rootDir": "src", - "noEmit": true, "baseUrl": "./", + "outDir": "dist", "esModuleInterop": true, "skipLibCheck": true, "sourceMap": true, @@ -23,9 +23,6 @@ }, { "path": "../../../packages/service-core/" - }, - { - "path": "../../../packages/jpgwire/" } ] } From 8b9b90966973ea468cd143f7da351f1c4ff3a4d5 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 26 Aug 2024 16:31:27 +0200 Subject: [PATCH 143/298] Import from dist --- modules/module-postgres/test/src/large_batch.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/module-postgres/test/src/large_batch.test.ts b/modules/module-postgres/test/src/large_batch.test.ts index 9e1a4eba9..cf045f801 100644 --- a/modules/module-postgres/test/src/large_batch.test.ts +++ b/modules/module-postgres/test/src/large_batch.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from 'vitest'; import { env } from './env.js'; import { TEST_CONNECTION_OPTIONS } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; -import { populateData } from './populate_test_data.js'; +import { populateData } from 'dist/populate_test_data.js'; describe('batch replication tests - mongodb', function () { // These are slow but consistent tests. From 2f858f592c1d377b9676740f2137fd8cb8a7436a Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 26 Aug 2024 16:57:27 +0200 Subject: [PATCH 144/298] Revert file move and now using import from dist --- .../{test/src => src/utils}/populate_test_data.ts | 0 modules/module-postgres/test/src/large_batch.test.ts | 2 +- modules/module-postgres/test/tsconfig.json | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) rename modules/module-postgres/{test/src => src/utils}/populate_test_data.ts (100%) diff --git a/modules/module-postgres/test/src/populate_test_data.ts b/modules/module-postgres/src/utils/populate_test_data.ts similarity index 100% rename from modules/module-postgres/test/src/populate_test_data.ts rename to modules/module-postgres/src/utils/populate_test_data.ts diff --git a/modules/module-postgres/test/src/large_batch.test.ts b/modules/module-postgres/test/src/large_batch.test.ts index cf045f801..2d20534b3 100644 --- a/modules/module-postgres/test/src/large_batch.test.ts +++ b/modules/module-postgres/test/src/large_batch.test.ts @@ -3,7 +3,7 @@ import { describe, expect, test } from 'vitest'; import { env } from './env.js'; import { TEST_CONNECTION_OPTIONS } from './util.js'; import { walStreamTest } from './wal_stream_utils.js'; -import { populateData } from 'dist/populate_test_data.js'; +import { populateData } from '../../dist/utils/populate_test_data.js'; describe('batch replication tests - mongodb', function () { // These are slow but consistent tests. diff --git a/modules/module-postgres/test/tsconfig.json b/modules/module-postgres/test/tsconfig.json index 529f82074..a287a5814 100644 --- a/modules/module-postgres/test/tsconfig.json +++ b/modules/module-postgres/test/tsconfig.json @@ -3,7 +3,6 @@ "compilerOptions": { "rootDir": "src", "baseUrl": "./", - "outDir": "dist", "esModuleInterop": true, "skipLibCheck": true, "sourceMap": true, From e6da9737d4731ecb145d6db1cb1c9924bf713462 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 27 Aug 2024 10:23:29 +0200 Subject: [PATCH 145/298] cleanup --- .../src/api/MySQLRouteAPIAdapter.ts | 10 +---- .../module-mysql/src/common/common-index.ts | 1 + .../src/common/get-tables-from-pattern.ts | 44 +++++++++++++++++++ 3 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 modules/module-mysql/src/common/get-tables-from-pattern.ts diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index a66430224..1f55f19cb 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -123,15 +123,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { ): Promise { let result: api.PatternResult[] = []; - /** - * This is a hack. The schema should always be the database name in MySQL. - * The default value of `public` is not valid. - * We might need to implement this better where the original table patterns are created. - */ - const mappedPatterns = tablePatterns; - // .map((t) => new sync_rules.TablePattern(this.config.database, t.tablePattern)); - - for (let tablePattern of mappedPatterns) { + for (let tablePattern of tablePatterns) { const schema = tablePattern.schema; let patternResult: api.PatternResult = { schema: schema, diff --git a/modules/module-mysql/src/common/common-index.ts b/modules/module-mysql/src/common/common-index.ts index 5290bdb65..8fa5a7cef 100644 --- a/modules/module-mysql/src/common/common-index.ts +++ b/modules/module-mysql/src/common/common-index.ts @@ -1,5 +1,6 @@ export * from './check-source-configuration.js'; export * from './get-replication-columns.js'; +export * from './get-tables-from-pattern.js'; export * from './mysql-to-sqlite.js'; export * from './read-master-gtid.js'; export * from './ReplicatedGTID.js'; diff --git a/modules/module-mysql/src/common/get-tables-from-pattern.ts b/modules/module-mysql/src/common/get-tables-from-pattern.ts new file mode 100644 index 000000000..166bf93a0 --- /dev/null +++ b/modules/module-mysql/src/common/get-tables-from-pattern.ts @@ -0,0 +1,44 @@ +import * as sync_rules from '@powersync/service-sync-rules'; +import mysql from 'mysql2/promise'; + +export type GetDebugTablesInfoOptions = { + connection: mysql.Connection; + tablePattern: sync_rules.TablePattern; +}; + +export async function getTablesFromPattern(options: GetDebugTablesInfoOptions): Promise> { + const { connection, tablePattern } = options; + const schema = tablePattern.schema; + + if (tablePattern.isWildcard) { + const [results] = await connection.query( + `SELECT + TABLE_NAME AS table_name + FROM + INFORMATION_SCHEMA.TABLES + WHERE + TABLE_SCHEMA = ? + AND TABLE_NAME LIKE ?`, + [schema, tablePattern.tablePattern] + ); + + return new Set( + results + .filter((result) => result.table_name.startsWith(tablePattern.tablePrefix)) + .map((result) => result.table_name) + ); + } else { + const [[match]] = await connection.query( + `SELECT + TABLE_NAME AS table_name + FROM + INFORMATION_SCHEMA.TABLES + WHERE + TABLE_SCHEMA = ? + AND TABLE_NAME = ?`, + [tablePattern.schema, tablePattern.tablePattern] + ); + // Only return the first result + return new Set([match.table_name]); + } +} From e3bf11250d44847779a8dfefd0773fdd7a8ba61a Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 27 Aug 2024 10:25:46 +0200 Subject: [PATCH 146/298] fix build error --- .../module-mysql/src/replication/BinLogReplicatorJob.ts | 2 +- .../src/replication/stream/MysqlBinLogStream.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/module-mysql/src/replication/BinLogReplicatorJob.ts b/modules/module-mysql/src/replication/BinLogReplicatorJob.ts index 17df0eec6..890c1abc5 100644 --- a/modules/module-mysql/src/replication/BinLogReplicatorJob.ts +++ b/modules/module-mysql/src/replication/BinLogReplicatorJob.ts @@ -4,7 +4,7 @@ import * as types from '../types/types.js'; import * as mysql_utils from '../utils/mysql_utils.js'; import { replication } from '@powersync/service-core'; -import { MysqlBinLogStream } from './MysqlBinLogStream.js'; +import { MysqlBinLogStream } from './stream/MysqlBinLogStream.js'; export interface BinLogReplicationJobOptions extends replication.AbstractReplicationJobOptions { /** diff --git a/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts index 7e5737ff7..1c5abbcca 100644 --- a/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts @@ -6,9 +6,9 @@ import { framework, storage } from '@powersync/service-core'; import mysql, { RowDataPacket } from 'mysql2/promise'; import ZongJi, { BinLogEvent } from '@vlasky/zongji'; -import * as common from '../common/common-index.js'; -import { NormalizedMySQLConnectionConfig } from '../types/types.js'; -import * as zongji_utils from './zongji/zongji-utils.js'; +import * as common from '../../common/common-index.js'; +import { NormalizedMySQLConnectionConfig } from '../../types/types.js'; +import * as zongji_utils from '../zongji/zongji-utils.js'; export interface BinLogStreamOptions { pool: mysql.Pool; @@ -215,7 +215,7 @@ AND table_type = 'BASE TABLE';`, for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, tablePattern); for (let table of tables) { - await this.snapshotTable(batch, table); + await this.snapshotTable(batch, this.pool, table); await batch.markSnapshotDone([table], headGTID.comparable); await framework.container.probes.touch(); } From 76b0ce050fdded517a117cfdc1f5f1f76b94ca48 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 12:04:34 +0200 Subject: [PATCH 147/298] Ensure collections are created for tests --- packages/service-core/test/src/util.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 5e8428256..65de5219d 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -24,7 +24,6 @@ export const MONGO_STORAGE_FACTORY: StorageFactory = async () => { await db.clear(); return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); }; - export async function connectMongo() { // Short timeout for tests, to fail fast when the server is not available. // Slightly longer timeouts for CI, to avoid arbitrary test failures @@ -34,9 +33,30 @@ export async function connectMongo() { serverSelectionTimeoutMS: env.CI ? 15_000 : 2_500 }); const db = new PowerSyncMongo(client); + await ensureCollections(db.db); + return db; } +/** + * Ensure that all the required collections exist in MongoDB + * Some tests were only passing because the collections were created as part of inserts in other tests. + * Ensuring that the collections are in fact created removes that random element. + * + * @param db + */ +async function ensureCollections(db: mongo.Db) { + await db.createCollection('current_data'); + await db.createCollection('bucket_data'); + await db.createCollection('bucket_parameters'); + await db.createCollection('op_id_sequence'); + await db.createCollection('sync_rules'); + await db.createCollection('source_tables'); + await db.createCollection('write_checkpoints'); + await db.createCollection('instance'); + await db.createCollection('locks'); +} + export function makeTestTable(name: string, columns?: string[] | undefined) { const relId = hashData('table', name, (columns ?? ['id']).join(',')); const id = new bson.ObjectId('6544e3899293153fa7b38331'); From b9ad8aea8305c537243173bac8a057562783e6a7 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 14:02:25 +0200 Subject: [PATCH 148/298] Added dedicated BucketStorageFactory for Postgres tests Reverted auto collection creation for BucketStorageFactory in the service-core tests --- .../test/src/schema_changes.test.ts | 4 +-- modules/module-postgres/test/src/util.ts | 12 ++++++++- packages/service-core/test/src/util.ts | 25 ++----------------- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/modules/module-postgres/test/src/schema_changes.test.ts b/modules/module-postgres/test/src/schema_changes.test.ts index 9fb8d82b9..3e083120a 100644 --- a/modules/module-postgres/test/src/schema_changes.test.ts +++ b/modules/module-postgres/test/src/schema_changes.test.ts @@ -1,15 +1,15 @@ import { compareIds, putOp, removeOp } from '@core-tests/stream_utils.js'; -import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; import { BucketStorageFactory } from '@powersync/service-core'; import { describe, expect, test } from 'vitest'; import { walStreamTest } from './wal_stream_utils.js'; +import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js'; type StorageFactory = () => Promise; describe( 'schema changes', function () { - defineTests(MONGO_STORAGE_FACTORY); + defineTests(INITIALIZED_MONGO_STORAGE_FACTORY); }, { timeout: 20_000 } ); diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 38e9c12bb..196eca22b 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -1,10 +1,11 @@ import * as types from '@module/types/types.js'; import * as pg_utils from '@module/utils/pgwire_utils.js'; -import { BucketStorageFactory, Metrics, OpId } from '@powersync/service-core'; +import { BucketStorageFactory, Metrics, MongoBucketStorage, OpId } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import { env } from './env.js'; import { pgwireRows } from '@powersync/service-jpgwire'; import { logger } from '@powersync/lib-services-framework'; +import { connectMongo, StorageFactory } from '@core-tests/util.js'; // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -22,6 +23,15 @@ export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ sslmode: 'disable' }); +export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { + const db = await connectMongo(); + // None of the PG tests insert data into this collection, so it was never created + await db.db.createCollection('bucket_parameters'); + await db.clear(); + + return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); +}; + export async function clearTestDb(db: pgwire.PgClient) { await db.query( "select pg_drop_replication_slot(slot_name) from pg_replication_slots where active = false and slot_name like 'test_%'" diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 65de5219d..761217c05 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -24,6 +24,7 @@ export const MONGO_STORAGE_FACTORY: StorageFactory = async () => { await db.clear(); return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); }; + export async function connectMongo() { // Short timeout for tests, to fail fast when the server is not available. // Slightly longer timeouts for CI, to avoid arbitrary test failures @@ -32,29 +33,7 @@ export async function connectMongo() { socketTimeoutMS: env.CI ? 15_000 : 5_000, serverSelectionTimeoutMS: env.CI ? 15_000 : 2_500 }); - const db = new PowerSyncMongo(client); - await ensureCollections(db.db); - - return db; -} - -/** - * Ensure that all the required collections exist in MongoDB - * Some tests were only passing because the collections were created as part of inserts in other tests. - * Ensuring that the collections are in fact created removes that random element. - * - * @param db - */ -async function ensureCollections(db: mongo.Db) { - await db.createCollection('current_data'); - await db.createCollection('bucket_data'); - await db.createCollection('bucket_parameters'); - await db.createCollection('op_id_sequence'); - await db.createCollection('sync_rules'); - await db.createCollection('source_tables'); - await db.createCollection('write_checkpoints'); - await db.createCollection('instance'); - await db.createCollection('locks'); + return new PowerSyncMongo(client); } export function makeTestTable(name: string, columns?: string[] | undefined) { From 18bf9e0668767c85ca5407239e4982d3ec6f5897 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 15:59:37 +0200 Subject: [PATCH 149/298] Moved custom factory into the test it is being used at --- .../module-postgres/test/src/schema_changes.test.ts | 13 +++++++++++-- modules/module-postgres/test/src/util.ts | 12 +----------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/modules/module-postgres/test/src/schema_changes.test.ts b/modules/module-postgres/test/src/schema_changes.test.ts index 3e083120a..51288b919 100644 --- a/modules/module-postgres/test/src/schema_changes.test.ts +++ b/modules/module-postgres/test/src/schema_changes.test.ts @@ -1,11 +1,20 @@ import { compareIds, putOp, removeOp } from '@core-tests/stream_utils.js'; -import { BucketStorageFactory } from '@powersync/service-core'; +import { connectMongo } from '@core-tests/util.js'; +import { BucketStorageFactory, MongoBucketStorage } from '@powersync/service-core'; import { describe, expect, test } from 'vitest'; import { walStreamTest } from './wal_stream_utils.js'; -import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js'; type StorageFactory = () => Promise; +export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { + const db = await connectMongo(); + // None of the PG tests insert data into this collection, so it was never created + await db.db.createCollection('bucket_parameters'); + await db.clear(); + + return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); +}; + describe( 'schema changes', function () { diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 196eca22b..38e9c12bb 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -1,11 +1,10 @@ import * as types from '@module/types/types.js'; import * as pg_utils from '@module/utils/pgwire_utils.js'; -import { BucketStorageFactory, Metrics, MongoBucketStorage, OpId } from '@powersync/service-core'; +import { BucketStorageFactory, Metrics, OpId } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import { env } from './env.js'; import { pgwireRows } from '@powersync/service-jpgwire'; import { logger } from '@powersync/lib-services-framework'; -import { connectMongo, StorageFactory } from '@core-tests/util.js'; // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -23,15 +22,6 @@ export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ sslmode: 'disable' }); -export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { - const db = await connectMongo(); - // None of the PG tests insert data into this collection, so it was never created - await db.db.createCollection('bucket_parameters'); - await db.clear(); - - return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); -}; - export async function clearTestDb(db: pgwire.PgClient) { await db.query( "select pg_drop_replication_slot(slot_name) from pg_replication_slots where active = false and slot_name like 'test_%'" From 76c24e46536d917ecf264ff0801e1df44a92b878 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 16:14:51 +0200 Subject: [PATCH 150/298] Revert factory imports --- .../test/src/schema_changes.test.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/modules/module-postgres/test/src/schema_changes.test.ts b/modules/module-postgres/test/src/schema_changes.test.ts index 51288b919..2e8d86575 100644 --- a/modules/module-postgres/test/src/schema_changes.test.ts +++ b/modules/module-postgres/test/src/schema_changes.test.ts @@ -1,24 +1,25 @@ import { compareIds, putOp, removeOp } from '@core-tests/stream_utils.js'; -import { connectMongo } from '@core-tests/util.js'; -import { BucketStorageFactory, MongoBucketStorage } from '@powersync/service-core'; +import { BucketStorageFactory } from '@powersync/service-core'; import { describe, expect, test } from 'vitest'; import { walStreamTest } from './wal_stream_utils.js'; +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; type StorageFactory = () => Promise; -export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { - const db = await connectMongo(); - // None of the PG tests insert data into this collection, so it was never created - await db.db.createCollection('bucket_parameters'); - await db.clear(); +// export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { +// const db = await connectMongo(); - return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); -}; +// // None of the PG tests insert data into this collection, so it was never created +// await db.db.createCollection('bucket_parameters'); +// await db.clear(); + +// return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); +// }; describe( 'schema changes', function () { - defineTests(INITIALIZED_MONGO_STORAGE_FACTORY); + defineTests(MONGO_STORAGE_FACTORY); }, { timeout: 20_000 } ); From aad74281eae95b1918d96cb8607ecf581e1297e0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 16:51:04 +0200 Subject: [PATCH 151/298] Revert noEmit for postgres tests --- modules/module-postgres/test/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/module-postgres/test/tsconfig.json b/modules/module-postgres/test/tsconfig.json index a287a5814..18898c4ee 100644 --- a/modules/module-postgres/test/tsconfig.json +++ b/modules/module-postgres/test/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "rootDir": "src", "baseUrl": "./", + "noEmit": true, "esModuleInterop": true, "skipLibCheck": true, "sourceMap": true, From 79607c72dac87279bbc0b92cae51ee8b2cb7f00e Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 18:53:26 +0200 Subject: [PATCH 152/298] Trying moved factory again after tsconfig fix --- .../test/src/schema_changes.test.ts | 17 ++--------------- modules/module-postgres/test/src/util.ts | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/module-postgres/test/src/schema_changes.test.ts b/modules/module-postgres/test/src/schema_changes.test.ts index 2e8d86575..5318f7f10 100644 --- a/modules/module-postgres/test/src/schema_changes.test.ts +++ b/modules/module-postgres/test/src/schema_changes.test.ts @@ -1,25 +1,12 @@ import { compareIds, putOp, removeOp } from '@core-tests/stream_utils.js'; -import { BucketStorageFactory } from '@powersync/service-core'; import { describe, expect, test } from 'vitest'; import { walStreamTest } from './wal_stream_utils.js'; -import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; - -type StorageFactory = () => Promise; - -// export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { -// const db = await connectMongo(); - -// // None of the PG tests insert data into this collection, so it was never created -// await db.db.createCollection('bucket_parameters'); -// await db.clear(); - -// return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); -// }; +import { INITIALIZED_MONGO_STORAGE_FACTORY, StorageFactory } from './util.js'; describe( 'schema changes', function () { - defineTests(MONGO_STORAGE_FACTORY); + defineTests(INITIALIZED_MONGO_STORAGE_FACTORY); }, { timeout: 20_000 } ); diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 38e9c12bb..971064d71 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -1,10 +1,11 @@ import * as types from '@module/types/types.js'; import * as pg_utils from '@module/utils/pgwire_utils.js'; -import { BucketStorageFactory, Metrics, OpId } from '@powersync/service-core'; +import { BucketStorageFactory, Metrics, MongoBucketStorage, OpId } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import { env } from './env.js'; import { pgwireRows } from '@powersync/service-jpgwire'; import { logger } from '@powersync/lib-services-framework'; +import { connectMongo } from '@core-tests/util.js'; // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -22,6 +23,18 @@ export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ sslmode: 'disable' }); +export type StorageFactory = () => Promise; + +export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { + const db = await connectMongo(); + + // None of the PG tests insert data into this collection, so it was never created + await db.db.createCollection('bucket_parameters'); + await db.clear(); + + return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); +}; + export async function clearTestDb(db: pgwire.PgClient) { await db.query( "select pg_drop_replication_slot(slot_name) from pg_replication_slots where active = false and slot_name like 'test_%'" From e864a5683fa921c175e5cc8351e3f6f04ac43e92 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 19:03:50 +0200 Subject: [PATCH 153/298] Create test collection conditionally --- modules/module-postgres/test/src/util.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 971064d71..44e7cec62 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -29,7 +29,10 @@ export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { const db = await connectMongo(); // None of the PG tests insert data into this collection, so it was never created - await db.db.createCollection('bucket_parameters'); + if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) { + await db.db.createCollection('bucket_parameters'); + } + await db.clear(); return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); From 5bf7b3c05e1e531e21faa9063198b8c858b3a101 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 27 Aug 2024 22:55:51 +0200 Subject: [PATCH 154/298] Added a few more TODOs Cleanup Updated comments and docs where appropriate --- .../module-postgres/src/api/PostgresRouteAPIAdapter.ts | 3 +-- modules/module-postgres/src/module/PostgresModule.ts | 3 ++- .../module-postgres/src/replication/WalConnection.ts | 3 +-- packages/service-core/src/api/RouteAPI.ts | 1 + packages/service-core/src/modules/AbstractModule.ts | 6 ------ .../service-core/src/replication/ReplicationModule.ts | 4 ++-- packages/service-core/src/runner/teardown.ts | 10 +++++----- .../service-core/src/storage/MongoBucketStorage.ts | 1 + .../service-core/src/storage/StorageFactoryProvider.ts | 1 + .../service-core/src/storage/mongo/MongoBucketBatch.ts | 2 +- packages/service-core/src/storage/mongo/models.ts | 2 +- service/src/runners/server.ts | 4 ++-- 12 files changed, 18 insertions(+), 22 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 5b00fa04c..296ac4baf 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -166,8 +166,7 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { }); if (results.rows.length == 0) { // Table not found - const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null, sqlSyncRules); - patternResult.table = details; + patternResult.table = await this.getDebugTableInfo(tablePattern, tablePattern.name, null, sqlSyncRules); } else { const row = pgwire.pgwireRows(results)[0]; const name = row.table_name as string; diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 447365b4f..633fd6093 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -64,7 +64,8 @@ export class PostgresModule extends replication.ReplicationModule {} - protected registerSupabaseAuth(context: system.ServiceContextContainer) { + // TODO: This should rather be done by registering the key collector in some kind of auth engine + private registerSupabaseAuth(context: system.ServiceContextContainer) { const { configuration } = context; // Register the Supabase key collector(s) configuration.connections diff --git a/modules/module-postgres/src/replication/WalConnection.ts b/modules/module-postgres/src/replication/WalConnection.ts index a0c53e7a9..df2ba0d69 100644 --- a/modules/module-postgres/src/replication/WalConnection.ts +++ b/modules/module-postgres/src/replication/WalConnection.ts @@ -159,8 +159,7 @@ export class WalConnection { }); if (results.rows.length == 0) { // Table not found - const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); - patternResult.table = details; + patternResult.table = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); } else { const row = pgwire.pgwireRows(results)[0]; const name = row.table_name as string; diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index 559336715..b5ad5ccab 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -30,6 +30,7 @@ export interface RouteAPI { * @param tablePatterns A set of table patterns which typically come from * the tables listed in sync rules definitions. * + * @param sqlSyncRules * @returns A result of all the tables and columns which should be replicated * based off the input patterns. Certain tests are executed on the * tables to ensure syncing should function according to the input diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index c27dcba88..439622646 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -13,12 +13,6 @@ export abstract class AbstractModule { this.logger = logger.child({ name: `Module:${options.name}` }); } - /** - * Register any functionality on the {@link ServiceContextContainer}. - * Note this will be executed before the ServiceContext has been initialized. - * This can be used to register storage providers which will be created during initialization. - */ - /** * Initialize the module using any required services from the ServiceContext */ diff --git a/packages/service-core/src/replication/ReplicationModule.ts b/packages/service-core/src/replication/ReplicationModule.ts index 39bb337be..a42303811 100644 --- a/packages/service-core/src/replication/ReplicationModule.ts +++ b/packages/service-core/src/replication/ReplicationModule.ts @@ -32,7 +32,7 @@ export abstract class ReplicationModule extend } /** - * Create the API adapter for the DataSource required by the sync API + * Create the RouteAPI adapter for the DataSource required to service the sync API * endpoints. */ protected abstract createRouteAPIAdapter(decodedConfig: TConfig): api.RouteAPI; @@ -43,7 +43,7 @@ export abstract class ReplicationModule extend protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): AbstractReplicator; /** - * Register this module's replication adapters and sync API providers if the required configuration is present. + * Register this module's Replicators and RouteAPI adapters if the required configuration is present. */ public async initialize(context: system.ServiceContext): Promise { if (!context.configuration.connections) { diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index c4c449414..67b0662d3 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -13,11 +13,11 @@ export async function teardown(runnerConfig: utils.RunnerConfig) { const config = await utils.loadConfig(runnerConfig); const serviceContext = new system.ServiceContextContainer(config); - // TODO what should be registered on the teardown command here? - - const moduleManager = container.getImplementation(modules.ModuleManager); - await moduleManager.initialize(serviceContext); - await moduleManager.tearDown(); + // TODO Teardown stopped replication, clean up all replication slots and then wiped the MongoDB + // This will now have to take into account multiple modules + // const moduleManager = container.getImplementation(modules.ModuleManager); + // await moduleManager.initialize(serviceContext); + // await moduleManager.tearDown(); process.exit(0); } catch (e) { logger.error(`Teardown failure`, e); diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index 1f4cbbe8f..d1fd1fab0 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -32,6 +32,7 @@ export interface MongoBucketStorageOptions extends PowerSyncMongoOptions {} export class MongoBucketStorage implements BucketStorageFactory { private readonly client: mongo.MongoClient; private readonly session: mongo.ClientSession; + // TODO: This is still Postgres specific and needs to be reworked public readonly slot_name_prefix: string; private readonly storageCache = new LRUCache({ diff --git a/packages/service-core/src/storage/StorageFactoryProvider.ts b/packages/service-core/src/storage/StorageFactoryProvider.ts index c944f7ea9..96ac1d6b3 100644 --- a/packages/service-core/src/storage/StorageFactoryProvider.ts +++ b/packages/service-core/src/storage/StorageFactoryProvider.ts @@ -9,6 +9,7 @@ export type StorageFactoryProviderOptions = { }; export class StorageFactoryProvider { + // TODO: This will need to revisited when we actually support multiple storage providers. protected storageProviders: Map; protected generatedStorage: GeneratedStorage | null; diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index b7d18d914..ce1937380 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -25,7 +25,7 @@ const MAX_ROW_SIZE = 15 * 1024 * 1024; // In the future, we can investigate allowing multiple replication streams operating independently. const replicationMutex = new util.Mutex(); -// FIXME: This +// TODO: This needs to be provided by the replication Module, since LSNs are data source specific. export const ZERO_LSN = '00000000/00000000'; export class MongoBucketBatch implements BucketStorageBatch { diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index 9b0191fda..ef26564bc 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -57,7 +57,7 @@ export interface SourceTableDocument { _id: bson.ObjectId; group_id: number; connection_id: number; - relation_id: number | undefined | string; + relation_id: number | string | undefined; schema_name: string; table_name: string; replica_id_columns: string[] | null; diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index d20482d93..e6e290749 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -77,9 +77,9 @@ export const startServer = async (runnerConfig: core.utils.RunnerConfig) => { const moduleManager = container.getImplementation(core.modules.ModuleManager); await moduleManager.initialize(serviceContext); - logger.info('Starting service'); + logger.info('Starting service...'); await serviceContext.lifeCycleEngine.start(); - logger.info('service started'); + logger.info('Service started.'); await container.probes.ready(); From 6165c47a5eed9ed37e36ca50e8990a653238a49b Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 28 Aug 2024 11:03:02 +0200 Subject: [PATCH 155/298] Some more cleanup Renamed StorageFactoryProvider to StorageEngine --- packages/service-core/src/api/diagnostics.ts | 4 +-- .../src/replication/AbstractReplicator.ts | 6 ++-- .../service-core/src/routes/RouterEngine.ts | 7 ++-- .../src/routes/endpoints/checkpointing.ts | 12 ++----- .../src/routes/endpoints/sync-rules.ts | 17 ++++----- .../service-core/src/storage/BucketStorage.ts | 12 ------- ...ageFactoryProvider.ts => StorageEngine.ts} | 35 ++++++++----------- .../src/storage/StorageProvider.ts | 21 ++++++----- .../src/storage/mongo/MongoStorageProvider.ts | 19 ++++------ .../service-core/src/storage/storage-index.ts | 2 +- .../service-core/src/system/ServiceContext.ts | 8 ++--- service/src/metrics.ts | 4 +-- 12 files changed, 61 insertions(+), 86 deletions(-) rename packages/service-core/src/storage/{StorageFactoryProvider.ts => StorageEngine.ts} (50%) diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 9c4df90c6..d0b8a2ca3 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -53,11 +53,11 @@ export async function getSyncRulesStatus( } const { - storage: { bucketStorage } + storage: { activeBucketStorage } } = serviceContext; const api = serviceContext.routerEngine.getAPI(); - const systemStorage = live_status ? bucketStorage.getInstance(persisted) : undefined; + const systemStorage = live_status ? activeBucketStorage.getInstance(persisted) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; diff --git a/packages/service-core/src/replication/AbstractReplicator.ts b/packages/service-core/src/replication/AbstractReplicator.ts index 7b45021bf..cd6cd0e4d 100644 --- a/packages/service-core/src/replication/AbstractReplicator.ts +++ b/packages/service-core/src/replication/AbstractReplicator.ts @@ -5,7 +5,7 @@ import { container, logger } from '@powersync/lib-services-framework'; import { SyncRulesProvider } from '../util/config/sync-rules/sync-rules-provider.js'; import winston from 'winston'; import { AbstractReplicationJob } from './AbstractReplicationJob.js'; -import { StorageFactoryProvider } from '../storage/storage-index.js'; +import { StorageEngine } from '../storage/storage-index.js'; import { ErrorRateLimiter } from './ErrorRateLimiter.js'; // 5 minutes @@ -18,7 +18,7 @@ export interface CreateJobOptions { export interface AbstractReplicatorOptions { id: string; - storageFactory: StorageFactoryProvider; + storageEngine: StorageEngine; syncRuleProvider: SyncRulesProvider; /** * This limits the effect of retries when there is a persistent issue. @@ -55,7 +55,7 @@ export abstract class AbstractReplicator { const { service_context } = payload.context; const { - storage: { bucketStorage } + storage: { activeBucketStorage } } = service_context; - const sync_rules = await bucketStorage.getActiveSyncRulesContent(); + const sync_rules = await activeBucketStorage.getActiveSyncRulesContent(); if (!sync_rules) { throw new errors.JourneyError({ status: 422, @@ -111,7 +111,7 @@ export const currentSyncRules = routeDefinition({ }); } const info = await debugSyncRules(service_context, sync_rules.sync_rules_content); - const next = await bucketStorage.getNextSyncRulesContent(); + const next = await activeBucketStorage.getNextSyncRulesContent(); const next_info = next ? await debugSyncRules(service_context, next.sync_rules_content) : null; @@ -144,9 +144,9 @@ export const reprocessSyncRules = routeDefinition({ validator: schema.createTsCodecValidator(ReprocessSyncRulesRequest), handler: async (payload) => { const { - storage: { bucketStorage } + storage: { activeBucketStorage } } = payload.context.service_context; - const sync_rules = await bucketStorage.getActiveSyncRules(); + const sync_rules = await activeBucketStorage.getActiveSyncRules(); if (sync_rules == null) { throw new errors.JourneyError({ status: 422, @@ -155,7 +155,7 @@ export const reprocessSyncRules = routeDefinition({ }); } - const new_rules = await bucketStorage.updateSyncRules({ + const new_rules = await activeBucketStorage.updateSyncRules({ content: sync_rules.sync_rules.content }); return { @@ -180,9 +180,6 @@ async function debugSyncRules(serviceContext: system.ServiceContext, sync_rules: const source_table_patterns = rules.getSourceTables(); const api = serviceContext.routerEngine.getAPI(); - if (!api) { - throw new Error('No API handler found'); - } const resolved_tables = await api.getDebugTablesInfo(source_table_patterns, rules); diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 8cf125149..887bc1bee 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -156,18 +156,6 @@ export interface PersistedSyncRules { readonly slot_name: string; } -export class DefaultPersistedSyncRules implements PersistedSyncRules { - public readonly checkpoint_lsn: string | null; - - constructor(public readonly id: number, public readonly sync_rules: SqlSyncRules, checkpoint_lsn: string | null) { - this.checkpoint_lsn = checkpoint_lsn; - } - - get slot_name(): string { - return `powersync_${this.id}`; - } -} - export interface UpdateSyncRulesOptions { content: string; lock?: boolean; diff --git a/packages/service-core/src/storage/StorageFactoryProvider.ts b/packages/service-core/src/storage/StorageEngine.ts similarity index 50% rename from packages/service-core/src/storage/StorageFactoryProvider.ts rename to packages/service-core/src/storage/StorageEngine.ts index 96ac1d6b3..6a6950b7c 100644 --- a/packages/service-core/src/storage/StorageFactoryProvider.ts +++ b/packages/service-core/src/storage/StorageEngine.ts @@ -1,41 +1,36 @@ import { LifeCycledSystem } from '@powersync/lib-services-framework'; import { ResolvedPowerSyncConfig } from '../util/util-index.js'; import { BucketStorageFactory } from './BucketStorage.js'; -import { BucketStorageProvider, GeneratedStorage } from './StorageProvider.js'; +import { BucketStorageProvider, ActiveStorage } from './StorageProvider.js'; -export type StorageFactoryProviderOptions = { +export type StorageEngineOptions = { configuration: ResolvedPowerSyncConfig; - lifecycle_engine: LifeCycledSystem; + lifecycleEngine: LifeCycledSystem; }; -export class StorageFactoryProvider { +export class StorageEngine { // TODO: This will need to revisited when we actually support multiple storage providers. - protected storageProviders: Map; - protected generatedStorage: GeneratedStorage | null; - - constructor(options: StorageFactoryProviderOptions) { - this.storageProviders = new Map(); - this.generatedStorage = null; + protected storageProviders: Map = new Map(); + protected currentActiveStorage: ActiveStorage | null = null; + constructor(options: StorageEngineOptions) { // This will create the relevant storage provider when the system is started. - options.lifecycle_engine.withLifecycle(null, { + options.lifecycleEngine.withLifecycle(null, { start: async () => { const { configuration } = options; - const generatedStorage = await this.storageProviders.get(configuration.storage.type)!.generate({ - resolved_config: configuration + this.currentActiveStorage = await this.storageProviders.get(configuration.storage.type)!.getStorage({ + resolvedConfig: configuration }); - - this.generatedStorage = generatedStorage; }, - stop: () => this.generatedStorage?.disposer() + stop: () => this.currentActiveStorage?.disposer() }); } - get bucketStorage(): BucketStorageFactory { - if (!this.generatedStorage) { - throw new Error(`storage has not been initialized yet.`); + get activeBucketStorage(): BucketStorageFactory { + if (!this.currentActiveStorage) { + throw new Error(`No storage provider has been initialized yet.`); } - return this.generatedStorage.storage; + return this.currentActiveStorage.storage; } /** diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts index 122af4bd0..f41b8ac42 100644 --- a/packages/service-core/src/storage/StorageProvider.ts +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -1,19 +1,22 @@ -import { BucketStorageFactory } from '../storage/BucketStorage.js'; - +import { BucketStorageFactory } from './BucketStorage.js'; import * as util from '../util/util-index.js'; -export type GeneratedStorage = { +export interface ActiveStorage { storage: BucketStorageFactory; disposer: () => Promise; -}; +} -export type StorageGenerationParams = { - resolved_config: util.ResolvedPowerSyncConfig; -}; +export interface GetStorageOptions { + // TODO: This should just be the storage config. Update once the slot name prefix coupling has been removed from the storage + resolvedConfig: util.ResolvedPowerSyncConfig; +} export interface BucketStorageProvider { - // The storage type which should match the `type` field in the config + /** + * The storage type that this provider provides. + * The type should match the `type` field in the config. + */ type: string; - generate(storageConfig: StorageGenerationParams): Promise; + getStorage(options: GetStorageOptions): Promise; } diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts index 40e6d6a5c..c7fb238f0 100644 --- a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -1,29 +1,24 @@ -import { configFile } from '@powersync/service-types'; - import * as db from '../../db/db-index.js'; - import { MongoBucketStorage } from '../MongoBucketStorage.js'; -import { BucketStorageProvider, GeneratedStorage, StorageGenerationParams } from '../StorageProvider.js'; +import { BucketStorageProvider, ActiveStorage, GetStorageOptions } from '../StorageProvider.js'; import { PowerSyncMongo } from './db.js'; -export type MongoStorageConfig = configFile.StorageConfig; - export class MongoStorageProvider implements BucketStorageProvider { get type() { return 'mongodb'; } - async generate(params: StorageGenerationParams): Promise { - const { resolved_config } = params; - const { storage } = resolved_config; - const client = db.mongo.createMongoClient(resolved_config.storage); + async getStorage(options: GetStorageOptions): Promise { + const { resolvedConfig } = options; + + const client = db.mongo.createMongoClient(resolvedConfig.storage); - const database = new PowerSyncMongo(client, { database: storage.database }); + const database = new PowerSyncMongo(client, { database: resolvedConfig.storage.database }); return { storage: new MongoBucketStorage(database, { // TODO currently need the entire resolved config for this - slot_name_prefix: params.resolved_config.slot_name_prefix + slot_name_prefix: resolvedConfig.slot_name_prefix }), disposer: () => client.close() }; diff --git a/packages/service-core/src/storage/storage-index.ts b/packages/service-core/src/storage/storage-index.ts index 3917b87e5..3c137b8d1 100644 --- a/packages/service-core/src/storage/storage-index.ts +++ b/packages/service-core/src/storage/storage-index.ts @@ -2,7 +2,7 @@ export * from './BucketStorage.js'; export * from './MongoBucketStorage.js'; export * from './SourceEntity.js'; export * from './SourceTable.js'; -export * from './StorageFactoryProvider.js'; +export * from './StorageEngine.js'; export * from './mongo/db.js'; export * from './mongo/models.js'; diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index 9fd7f2362..4ddd0ff1c 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -12,7 +12,7 @@ export interface ServiceContext { metrics: metrics.Metrics; replicationEngine: replication.ReplicationEngine; routerEngine: routes.RouterEngine; - storage: storage.StorageFactoryProvider; + storage: storage.StorageEngine; } /** @@ -22,13 +22,13 @@ export interface ServiceContext { */ export class ServiceContextContainer implements ServiceContext { lifeCycleEngine: LifeCycledSystem; - storage: storage.StorageFactoryProvider; + storage: storage.StorageEngine; constructor(public configuration: utils.ResolvedPowerSyncConfig) { this.lifeCycleEngine = new LifeCycledSystem(); - this.storage = new storage.StorageFactoryProvider({ + this.storage = new storage.StorageEngine({ configuration, - lifecycle_engine: this.lifeCycleEngine + lifecycleEngine: this.lifeCycleEngine }); // Mongo storage is available as an option by default diff --git a/service/src/metrics.ts b/service/src/metrics.ts index adf608dc5..e231448f5 100644 --- a/service/src/metrics.ts +++ b/service/src/metrics.ts @@ -16,7 +16,7 @@ export const registerMetrics = async (options: MetricsRegistrationOptions) => { // This requires an instantiated bucket storage, which is only created when the lifecycle starts service_context.lifeCycleEngine.withLifecycle(null, { start: async () => { - const instanceId = await service_context.storage.bucketStorage.getPowerSyncInstanceId(); + const instanceId = await service_context.storage.activeBucketStorage.getPowerSyncInstanceId(); await core.metrics.Metrics.initialise({ powersync_instance_id: instanceId, disable_telemetry_sharing: service_context.configuration.telemetry.disable_telemetry_sharing, @@ -32,7 +32,7 @@ export const registerMetrics = async (options: MetricsRegistrationOptions) => { } if (modes.includes(MetricModes.REPLICATION)) { - instance.configureReplicationMetrics(service_context.storage.bucketStorage); + instance.configureReplicationMetrics(service_context.storage.activeBucketStorage); } }, stop: () => service_context.metrics.shutdown() From 101b2b1454e9292fda78b1782ccc462554373572 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 28 Aug 2024 11:59:04 +0200 Subject: [PATCH 156/298] Consolidated getDebugTableInfo Fixed route storage references post rename --- .../src/api/PostgresRouteAPIAdapter.ts | 89 ++------ .../src/module/PostgresModule.ts | 2 +- .../src/replication/WalConnection.ts | 173 ---------------- .../src/replication/replication-index.ts | 1 - .../src/replication/replication-utils.ts | 192 +++++++++++++++++- .../test/src/validation.test.ts | 13 +- .../test/src/wal_stream_utils.ts | 10 +- .../src/routes/endpoints/admin.ts | 14 +- .../src/routes/endpoints/socket-route.ts | 6 +- .../src/routes/endpoints/sync-stream.ts | 4 +- 10 files changed, 233 insertions(+), 271 deletions(-) delete mode 100644 modules/module-postgres/src/replication/WalConnection.ts diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 296ac4baf..cfd11b132 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -1,4 +1,4 @@ -import { api, storage } from '@powersync/service-core'; +import { api } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import * as sync_rules from '@powersync/service-sync-rules'; @@ -6,13 +6,15 @@ import * as service_types from '@powersync/service-types'; import * as replication_utils from '../replication/replication-utils.js'; import * as types from '../types/types.js'; import * as pg_utils from '../utils/pgwire_utils.js'; +import { getDebugTableInfo } from '../replication/replication-utils.js'; +import { PUBLICATION_NAME } from '../replication/WalStream.js'; export class PostgresRouteAPIAdapter implements api.RouteAPI { protected pool: pgwire.PgClient; connectionTag: string; // TODO this should probably be configurable one day - publication_name = 'powersync'; + publicationName = PUBLICATION_NAME; constructor(protected config: types.ResolvedConnectionConfig) { this.pool = pgwire.connectPgWirePool(config, { @@ -46,7 +48,7 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { } try { - await replication_utils.checkSourceConfiguration(this.pool, this.publication_name); + await replication_utils.checkSourceConfiguration(this.pool, this.publicationName); } catch (e) { return { ...base, @@ -184,80 +186,15 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { relationId: number | null, syncRules: sync_rules.SqlSyncRules ): Promise { - const schema = tablePattern.schema; - let id_columns_result: replication_utils.ReplicaIdentityResult | undefined = undefined; - let id_columns_error = null; - - if (relationId != null) { - try { - id_columns_result = await replication_utils.getReplicationIdentityColumns(this.pool, relationId); - } catch (e) { - id_columns_error = { level: 'fatal', message: e.message }; - } - } - - const id_columns = id_columns_result?.replicationColumns ?? []; - - const sourceTable = new storage.SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); - - const syncData = syncRules.tableSyncsData(sourceTable); - const syncParameters = syncRules.tableSyncsParameters(sourceTable); - - if (relationId == null) { - return { - schema: schema, - name: name, - pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - replication_id: [], - data_queries: syncData, - parameter_queries: syncParameters, - // Also - errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] - }; - } - if (id_columns.length == 0 && id_columns_error == null) { - let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; - if (id_columns_result?.replicationIdentity == 'default') { - message += ' Configure a primary key on the table.'; - } - id_columns_error = { level: 'fatal', message }; - } - - let selectError = null; - try { - await pg_utils.retriedQuery(this.pool, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); - } catch (e) { - selectError = { level: 'fatal', message: e.message }; - } - - let replicateError = null; - - const publications = await pg_utils.retriedQuery(this.pool, { - statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, - params: [ - { type: 'varchar', value: this.publication_name }, - { type: 'varchar', value: tablePattern.schema }, - { type: 'varchar', value: name } - ] - }); - if (publications.rows.length == 0) { - replicateError = { - level: 'fatal', - message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` - }; - } - - return { - schema: schema, + return getDebugTableInfo({ + db: this.pool, name: name, - pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - replication_id: id_columns.map((c) => c.name), - data_queries: syncData, - parameter_queries: syncParameters, - errors: [id_columns_error, selectError, replicateError].filter( - (error) => error != null - ) as service_types.ReplicationError[] - }; + publicationName: this.publicationName, + connectionTag: this.connectionTag, + tablePattern: tablePattern, + relationId: relationId, + syncRules: syncRules + }); } async getReplicationLag(syncRulesId: string): Promise { diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 633fd6093..0323b06b1 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -46,7 +46,7 @@ export class PostgresModule extends replication.ReplicationModule { - const schema = tablePattern.schema; - let id_columns_result: ReplicaIdentityResult | undefined = undefined; - let id_columns_error = null; - - if (relationId != null) { - try { - id_columns_result = await getReplicationIdentityColumns(this.db, relationId); - } catch (e) { - id_columns_error = { level: 'fatal', message: e.message }; - } - } - - const id_columns = id_columns_result?.replicationColumns ?? []; - - const sourceTable = new SourceTable(0, this.connectionTag, relationId ?? 0, schema, name, id_columns, true); - - const syncData = this.sync_rules.tableSyncsData(sourceTable); - const syncParameters = this.sync_rules.tableSyncsParameters(sourceTable); - - if (relationId == null) { - return { - schema: schema, - name: name, - pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - replication_id: [], - data_queries: syncData, - parameter_queries: syncParameters, - // Also - errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] - }; - } - if (id_columns.length == 0 && id_columns_error == null) { - let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; - if (id_columns_result?.replicationIdentity == 'default') { - message += ' Configure a primary key on the table.'; - } - id_columns_error = { level: 'fatal', message }; - } - - let selectError = null; - try { - await util.retriedQuery(this.db, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); - } catch (e) { - selectError = { level: 'fatal', message: e.message }; - } - - let replicateError = null; - - const publications = await util.retriedQuery(this.db, { - statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, - params: [ - { type: 'varchar', value: this.publication_name }, - { type: 'varchar', value: tablePattern.schema }, - { type: 'varchar', value: name } - ] - }); - if (publications.rows.length == 0) { - replicateError = { - level: 'fatal', - message: `Table ${sourceTable.qualifiedName} is not part of publication '${this.publication_name}'. Run: \`ALTER PUBLICATION ${this.publication_name} ADD TABLE ${sourceTable.qualifiedName}\`.` - }; - } - - return { - schema: schema, - name: name, - pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, - replication_id: id_columns.map((c) => c.name), - data_queries: syncData, - parameter_queries: syncParameters, - errors: [id_columns_error, selectError, replicateError].filter((error) => error != null) as ReplicationError[] - }; - } - - async getDebugTablesInfo(tablePatterns: sync_rules.TablePattern[]): Promise { - let result: PatternResult[] = []; - - for (let tablePattern of tablePatterns) { - const schema = tablePattern.schema; - - let patternResult: PatternResult = { - schema: schema, - pattern: tablePattern.tablePattern, - wildcard: tablePattern.isWildcard - }; - result.push(patternResult); - - if (tablePattern.isWildcard) { - patternResult.tables = []; - const prefix = tablePattern.tablePrefix; - const results = await util.retriedQuery(this.db, { - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname LIKE $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - - for (let row of pgwire.pgwireRows(results)) { - const name = row.table_name as string; - const relationId = row.relid as number; - if (!name.startsWith(prefix)) { - continue; - } - const details = await this.getDebugTableInfo(tablePattern, name, relationId); - patternResult.tables.push(details); - } - } else { - const results = await util.retriedQuery(this.db, { - statement: `SELECT c.oid AS relid, c.relname AS table_name - FROM pg_class c - JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE n.nspname = $1 - AND c.relkind = 'r' - AND c.relname = $2`, - params: [ - { type: 'varchar', value: schema }, - { type: 'varchar', value: tablePattern.tablePattern } - ] - }); - if (results.rows.length == 0) { - // Table not found - patternResult.table = await this.getDebugTableInfo(tablePattern, tablePattern.name, null); - } else { - const row = pgwire.pgwireRows(results)[0]; - const name = row.table_name as string; - const relationId = row.relid as number; - patternResult.table = await this.getDebugTableInfo(tablePattern, name, relationId); - } - } - } - return result; - } -} diff --git a/modules/module-postgres/src/replication/replication-index.ts b/modules/module-postgres/src/replication/replication-index.ts index 3782a2da2..545553c1e 100644 --- a/modules/module-postgres/src/replication/replication-index.ts +++ b/modules/module-postgres/src/replication/replication-index.ts @@ -1,6 +1,5 @@ export * from './PgRelation.js'; export * from './replication-utils.js'; -export * from './WalConnection.js'; export * from './WalStream.js'; export * from './WalStreamReplicator.js'; export * from './WalStreamReplicationJob.js'; diff --git a/modules/module-postgres/src/replication/replication-utils.ts b/modules/module-postgres/src/replication/replication-utils.ts index a924574e9..089f0abab 100644 --- a/modules/module-postgres/src/replication/replication-utils.ts +++ b/modules/module-postgres/src/replication/replication-utils.ts @@ -1,8 +1,12 @@ import * as pgwire from '@powersync/service-jpgwire'; -import { storage } from '@powersync/service-core'; +import { PatternResult, storage } from '@powersync/service-core'; import * as pgwire_utils from '../utils/pgwire_utils.js'; import { ReplicationIdentity } from './PgRelation.js'; +import * as sync_rules from '@powersync/service-sync-rules'; +import * as service_types from '@powersync/service-types'; +import * as pg_utils from '../utils/pgwire_utils.js'; +import * as util from '../utils/pgwire_utils.js'; export interface ReplicaIdentityResult { replicationColumns: storage.ColumnDescriptor[]; @@ -127,3 +131,189 @@ $$ LANGUAGE plpgsql;` throw new Error(`'${publicationName}' uses publish_via_partition_root, which is not supported.`); } } + +export interface GetDebugTablesInfoOptions { + db: pgwire.PgClient; + publicationName: string; + connectionTag: string; + tablePatterns: sync_rules.TablePattern[]; + syncRules: sync_rules.SqlSyncRules; +} + +export async function getDebugTablesInfo(options: GetDebugTablesInfoOptions): Promise { + const { db, publicationName, connectionTag, tablePatterns, syncRules } = options; + let result: PatternResult[] = []; + + for (let tablePattern of tablePatterns) { + const schema = tablePattern.schema; + + let patternResult: PatternResult = { + schema: schema, + pattern: tablePattern.tablePattern, + wildcard: tablePattern.isWildcard + }; + result.push(patternResult); + + if (tablePattern.isWildcard) { + patternResult.tables = []; + const prefix = tablePattern.tablePrefix; + const results = await util.retriedQuery(db, { + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname LIKE $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + + for (let row of pgwire.pgwireRows(results)) { + const name = row.table_name as string; + const relationId = row.relid as number; + if (!name.startsWith(prefix)) { + continue; + } + const details = await getDebugTableInfo({ + db, + name, + publicationName, + connectionTag, + tablePattern, + relationId, + syncRules: syncRules + }); + patternResult.tables.push(details); + } + } else { + const results = await util.retriedQuery(db, { + statement: `SELECT c.oid AS relid, c.relname AS table_name + FROM pg_class c + JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = $1 + AND c.relkind = 'r' + AND c.relname = $2`, + params: [ + { type: 'varchar', value: schema }, + { type: 'varchar', value: tablePattern.tablePattern } + ] + }); + if (results.rows.length == 0) { + // Table not found + patternResult.table = await getDebugTableInfo({ + db, + name: tablePattern.name, + publicationName, + connectionTag, + tablePattern, + relationId: null, + syncRules: syncRules + }); + } else { + const row = pgwire.pgwireRows(results)[0]; + const name = row.table_name as string; + const relationId = row.relid as number; + patternResult.table = await getDebugTableInfo({ + db, + name, + publicationName, + connectionTag, + tablePattern, + relationId, + syncRules: syncRules + }); + } + } + } + return result; +} + +export interface GetDebugTableInfoOptions { + db: pgwire.PgClient; + name: string; + publicationName: string; + connectionTag: string; + tablePattern: sync_rules.TablePattern; + relationId: number | null; + syncRules: sync_rules.SqlSyncRules; +} + +export async function getDebugTableInfo(options: GetDebugTableInfoOptions): Promise { + const { db, name, publicationName, connectionTag, tablePattern, relationId, syncRules } = options; + const schema = tablePattern.schema; + let id_columns_result: ReplicaIdentityResult | undefined = undefined; + let id_columns_error = null; + + if (relationId != null) { + try { + id_columns_result = await getReplicationIdentityColumns(db, relationId); + } catch (e) { + id_columns_error = { level: 'fatal', message: e.message }; + } + } + + const id_columns = id_columns_result?.replicationColumns ?? []; + + const sourceTable = new storage.SourceTable(0, connectionTag, relationId ?? 0, schema, name, id_columns, true); + + const syncData = syncRules.tableSyncsData(sourceTable); + const syncParameters = syncRules.tableSyncsParameters(sourceTable); + + if (relationId == null) { + return { + schema: schema, + name: name, + pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + replication_id: [], + data_queries: syncData, + parameter_queries: syncParameters, + // Also + errors: [{ level: 'warning', message: `Table ${sourceTable.qualifiedName} not found.` }] + }; + } + if (id_columns.length == 0 && id_columns_error == null) { + let message = `No replication id found for ${sourceTable.qualifiedName}. Replica identity: ${id_columns_result?.replicationIdentity}.`; + if (id_columns_result?.replicationIdentity == 'default') { + message += ' Configure a primary key on the table.'; + } + id_columns_error = { level: 'fatal', message }; + } + + let selectError = null; + try { + await pg_utils.retriedQuery(db, `SELECT * FROM ${sourceTable.escapedIdentifier} LIMIT 1`); + } catch (e) { + selectError = { level: 'fatal', message: e.message }; + } + + let replicateError = null; + + const publications = await pg_utils.retriedQuery(db, { + statement: `SELECT tablename FROM pg_publication_tables WHERE pubname = $1 AND schemaname = $2 AND tablename = $3`, + params: [ + { type: 'varchar', value: publicationName }, + { type: 'varchar', value: tablePattern.schema }, + { type: 'varchar', value: name } + ] + }); + if (publications.rows.length == 0) { + replicateError = { + level: 'fatal', + message: `Table ${sourceTable.qualifiedName} is not part of publication '${publicationName}'. Run: \`ALTER PUBLICATION ${publicationName} ADD TABLE ${sourceTable.qualifiedName}\`.` + }; + } + + return { + schema: schema, + name: name, + pattern: tablePattern.isWildcard ? tablePattern.tablePattern : undefined, + replication_id: id_columns.map((c) => c.name), + data_queries: syncData, + parameter_queries: syncParameters, + errors: [id_columns_error, selectError, replicateError].filter( + (error) => error != null + ) as service_types.ReplicationError[] + }; +} diff --git a/modules/module-postgres/test/src/validation.test.ts b/modules/module-postgres/test/src/validation.test.ts index e55c207a8..54dced77e 100644 --- a/modules/module-postgres/test/src/validation.test.ts +++ b/modules/module-postgres/test/src/validation.test.ts @@ -1,7 +1,7 @@ import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; -import { WalConnection } from '@module/replication/WalConnection.js'; import { expect, test } from 'vitest'; import { walStreamTest } from './wal_stream_utils.js'; +import { getDebugTablesInfo } from '@module/replication/replication-utils.js'; // Not quite a walStreamTest, but it helps to manage the connection test( @@ -22,13 +22,14 @@ bucket_definitions: const syncRules = await context.factory.updateSyncRules({ content: syncRuleContent }); - const walConnection = new WalConnection({ + const tablePatterns = syncRules.parsed().sync_rules.getSourceTables(); + const tableInfo = await getDebugTablesInfo({ db: pool, - sync_rules: syncRules.parsed().sync_rules + publicationName: context.publicationName, + connectionTag: context.connectionTag, + tablePatterns: tablePatterns, + syncRules: syncRules.parsed().sync_rules }); - - const tablePatterns = syncRules.parsed().sync_rules.getSourceTables(); - const tableInfo = await walConnection.getDebugTablesInfo(tablePatterns); expect(tableInfo).toEqual([ { schema: 'public', diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index 6895b10dc..0de968e8e 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -1,7 +1,7 @@ import { BucketStorageFactory, SyncRulesBucketStorage } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import { TEST_CONNECTION_OPTIONS, clearTestDb, getClientCheckpoint } from './util.js'; -import { WalStream, WalStreamOptions } from '@module/replication/WalStream.js'; +import { WalStream, WalStreamOptions, PUBLICATION_NAME } from '@module/replication/WalStream.js'; import { fromAsync } from '@core-tests/stream_utils.js'; import { PgManager } from '@module/replication/PgManager.js'; @@ -48,6 +48,14 @@ export class WalStreamTestContext { return this.connectionManager.pool; } + get connectionTag() { + return this.connectionManager.connectionTag; + } + + get publicationName() { + return PUBLICATION_NAME; + } + async updateSyncRules(content: string) { const syncRules = await this.factory.updateSyncRules({ content: content }); this.storage = this.factory.getInstance(syncRules.parsed()); diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index 55af1dd07..d21c02259 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -55,10 +55,10 @@ export const diagnostics = routeDefinition({ } const { - storage: { bucketStorage } + storage: { activeBucketStorage } } = service_context; - const active = await bucketStorage.getActiveSyncRulesContent(); - const next = await bucketStorage.getNextSyncRulesContent(); + const active = await activeBucketStorage.getActiveSyncRulesContent(); + const next = await activeBucketStorage.getNextSyncRulesContent(); const active_status = await api.getSyncRulesStatus(service_context, active, { include_content, @@ -108,14 +108,14 @@ export const reprocess = routeDefinition({ context: { service_context } } = payload; const { - storage: { bucketStorage } + storage: { activeBucketStorage } } = service_context; - const next = await bucketStorage.getNextSyncRules(); + const next = await activeBucketStorage.getNextSyncRules(); if (next != null) { throw new Error(`Busy processing sync rules - cannot reprocess`); } - const active = await bucketStorage.getActiveSyncRules(); + const active = await activeBucketStorage.getActiveSyncRules(); if (active == null) { throw new errors.JourneyError({ status: 422, @@ -124,7 +124,7 @@ export const reprocess = routeDefinition({ }); } - const new_rules = await bucketStorage.updateSyncRules({ + const new_rules = await activeBucketStorage.updateSyncRules({ content: active.sync_rules.content }); diff --git a/packages/service-core/src/routes/endpoints/socket-route.ts b/packages/service-core/src/routes/endpoints/socket-route.ts index 0871980f6..fecda332a 100644 --- a/packages/service-core/src/routes/endpoints/socket-route.ts +++ b/packages/service-core/src/routes/endpoints/socket-route.ts @@ -31,10 +31,10 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => const syncParams = new RequestParameters(context.token_payload!, params.parameters ?? {}); const { - storage: { bucketStorage } + storage: { activeBucketStorage } } = service_context; // Sanity check before we start the stream - const cp = await bucketStorage.getActiveCheckpoint(); + const cp = await activeBucketStorage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { responder.onError( new errors.JourneyError({ @@ -65,7 +65,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => const tracker = new sync.RequestTracker(); try { for await (const data of sync.streamResponse({ - storage: bucketStorage, + storage: activeBucketStorage, params: { ...params, binary_data: true // always true for web sockets diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index ddff705cb..a98cc1c98 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -34,7 +34,7 @@ export const syncStreamed = routeDefinition({ const syncParams = new RequestParameters(payload.context.token_payload!, payload.params.parameters ?? {}); // Sanity check before we start the stream - const cp = await storage.bucketStorage.getActiveCheckpoint(); + const cp = await storage.activeBucketStorage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { throw new errors.JourneyError({ status: 500, @@ -50,7 +50,7 @@ export const syncStreamed = routeDefinition({ sync.transformToBytesTracked( sync.ndjson( sync.streamResponse({ - storage: storage.bucketStorage, + storage: storage.activeBucketStorage, params, syncParams, token: payload.context.token_payload!, From 34d415b93419a8368450c8c62ea47e582bb4d93c Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 28 Aug 2024 15:50:50 +0200 Subject: [PATCH 157/298] Removed Postgres ZeroLSN from BucketStorage --- .../src/replication/WalStream.ts | 4 +- modules/module-postgres/test/src/util.ts | 2 +- .../src/routes/endpoints/checkpointing.ts | 3 +- .../service-core/src/storage/BucketStorage.ts | 13 ++++- .../src/storage/MongoBucketStorage.ts | 7 +-- .../src/storage/mongo/MongoBucketBatch.ts | 7 +-- .../storage/mongo/MongoSyncBucketStorage.ts | 15 +++-- .../service-core/test/src/compacting.test.ts | 6 +- .../test/src/data_storage.test.ts | 58 +++++++++---------- packages/service-core/test/src/sync.test.ts | 34 +++++------ packages/service-core/test/src/util.ts | 2 + 11 files changed, 74 insertions(+), 77 deletions(-) diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index d2c2dad28..29ac6bfa5 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -333,7 +333,7 @@ WHERE oid = $1::regclass`, async initialReplication(db: pgwire.PgConnection, lsn: string) { const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({}, async (batch) => { + await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, db, tablePattern); for (let table of tables) { @@ -541,7 +541,7 @@ WHERE oid = $1::regclass`, // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); - await this.storage.startBatch({}, async (batch) => { + await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { // Replication never starts in the middle of a transaction let inTx = false; let count = 0; diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index 44e7cec62..d4e2b7170 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -93,7 +93,7 @@ export async function getClientCheckpoint( if (!cp.hasSyncRules()) { throw new Error('No sync rules available'); } - if (cp.lsn >= lsn) { + if (cp.lsn && cp.lsn >= lsn) { logger.info(`Got write checkpoint: ${lsn} : ${cp.checkpoint}`); return cp.checkpoint; } diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index a2404b407..0d8867f00 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -31,7 +31,7 @@ export const writeCheckpoint = routeDefinition({ if (!cp.hasSyncRules()) { throw new Error('No sync rules available'); } - if (cp.lsn >= head) { + if (cp.lsn && cp.lsn >= head) { logger.info(`Got write checkpoint: ${head} : ${cp.checkpoint}`); return { checkpoint: cp.checkpoint }; } @@ -52,7 +52,6 @@ export const writeCheckpoint2 = routeDefinition({ const api = service_context.routerEngine.getAPI(); - // Might want to call this something link replicationHead or something else const currentCheckpoint = await api.getReplicationHead(); const { storage: { activeBucketStorage } diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 887bc1bee..d0f67a384 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -104,7 +104,7 @@ export interface WriteCheckpoint { export interface ActiveCheckpoint { readonly checkpoint: util.OpId; - readonly lsn: string; + readonly lsn: string | null; hasSyncRules(): boolean; @@ -185,6 +185,10 @@ export interface BucketDataBatchOptions { chunkLimitBytes?: number; } +export interface StartBatchOptions { + zeroLSN: string; +} + export interface SyncRulesBucketStorage { readonly sync_rules: SqlSyncRules; readonly group_id: number; @@ -194,9 +198,12 @@ export interface SyncRulesBucketStorage { resolveTable(options: ResolveTableOptions): Promise; - startBatch(options: {}, callback: (batch: BucketStorageBatch) => Promise): Promise; + startBatch( + options: StartBatchOptions, + callback: (batch: BucketStorageBatch) => Promise + ): Promise; - getCheckpoint(): Promise<{ checkpoint: util.OpId; lsn: string }>; + getCheckpoint(): Promise<{ checkpoint: util.OpId }>; getParameterSets(checkpoint: util.OpId, lookups: SqliteJsonValue[][]): Promise; diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index d1fd1fab0..2cf45c436 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -8,8 +8,6 @@ import * as locks from '../locks/locks-index.js'; import * as sync from '../sync/sync-index.js'; import * as util from '../util/util-index.js'; -import { ZERO_LSN } from './mongo/MongoBucketBatch.js'; - import { logger } from '@powersync/lib-services-framework'; import { v4 as uuid } from 'uuid'; import { @@ -49,8 +47,7 @@ export class MongoBucketStorage implements BucketStorageFactory { return undefined; } const rules = new MongoPersistedSyncRulesContent(this.db, doc2); - const storage = this.getInstance(rules.parsed()); - return storage; + return this.getInstance(rules.parsed()); } }); @@ -372,7 +369,7 @@ export class MongoBucketStorage implements BucketStorageFactory { private makeActiveCheckpoint(doc: SyncRuleDocument | null) { return { checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n), - lsn: doc?.last_checkpoint_lsn ?? ZERO_LSN, + lsn: doc?.last_checkpoint_lsn ?? null, hasSyncRules() { return doc != null; }, diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index ce1937380..973f6a28a 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -25,9 +25,6 @@ const MAX_ROW_SIZE = 15 * 1024 * 1024; // In the future, we can investigate allowing multiple replication streams operating independently. const replicationMutex = new util.Mutex(); -// TODO: This needs to be provided by the replication Module, since LSNs are data source specific. -export const ZERO_LSN = '00000000/00000000'; - export class MongoBucketBatch implements BucketStorageBatch { private readonly client: mongo.MongoClient; public readonly db: PowerSyncMongo; @@ -64,7 +61,7 @@ export class MongoBucketBatch implements BucketStorageBatch { group_id: number, slot_name: string, last_checkpoint_lsn: string | null, - no_checkpoint_before_lsn: string | null + no_checkpoint_before_lsn: string ) { this.db = db; this.client = db.client; @@ -73,7 +70,7 @@ export class MongoBucketBatch implements BucketStorageBatch { this.slot_name = slot_name; this.session = this.client.startSession(); this.last_checkpoint_lsn = last_checkpoint_lsn; - this.no_checkpoint_before_lsn = no_checkpoint_before_lsn ?? ZERO_LSN; + this.no_checkpoint_before_lsn = no_checkpoint_before_lsn; } async flush(): Promise { diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index fdd292206..4aa17bf84 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -13,6 +13,7 @@ import { FlushedResult, ResolveTableOptions, ResolveTableResult, + StartBatchOptions, SyncBucketDataBatch, SyncRulesBucketStorage, SyncRuleStatus @@ -22,7 +23,7 @@ import { MongoBucketStorage } from '../MongoBucketStorage.js'; import { SourceTable } from '../SourceTable.js'; import { PowerSyncMongo } from './db.js'; import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './models.js'; -import { MongoBucketBatch, ZERO_LSN } from './MongoBucketBatch.js'; +import { MongoBucketBatch } from './MongoBucketBatch.js'; import { MongoCompactor } from './MongoCompactor.js'; import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js'; @@ -47,16 +48,18 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { const doc = await this.db.sync_rules.findOne( { _id: this.group_id }, { - projection: { last_checkpoint: 1, last_checkpoint_lsn: 1 } + projection: { last_checkpoint: 1 } } ); return { - checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n), - lsn: doc?.last_checkpoint_lsn ?? ZERO_LSN + checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n) }; } - async startBatch(options: {}, callback: (batch: BucketStorageBatch) => Promise): Promise { + async startBatch( + options: StartBatchOptions, + callback: (batch: BucketStorageBatch) => Promise + ): Promise { const doc = await this.db.sync_rules.findOne( { _id: this.group_id @@ -71,7 +74,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { this.group_id, this.slot_name, checkpoint_lsn, - doc?.no_checkpoint_before ?? null + doc?.no_checkpoint_before ?? options.zeroLSN ); try { await callback(batch); diff --git a/packages/service-core/test/src/compacting.test.ts b/packages/service-core/test/src/compacting.test.ts index 8ae3ab588..e7cb6d313 100644 --- a/packages/service-core/test/src/compacting.test.ts +++ b/packages/service-core/test/src/compacting.test.ts @@ -3,7 +3,7 @@ import { SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { validateCompactedBucket } from './bucket_validation.js'; import { oneFromAsync } from './stream_utils.js'; -import { makeTestTable, MONGO_STORAGE_FACTORY } from './util.js'; +import { makeTestTable, MONGO_STORAGE_FACTORY, ZERO_LSN } from './util.js'; const TEST_TABLE = makeTestTable('test', ['id']); @@ -26,7 +26,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -116,7 +116,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index 45fce8c0b..c2035148f 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -2,7 +2,7 @@ import { BucketDataBatchOptions } from '@/storage/BucketStorage.js'; import { RequestParameters, SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { fromAsync, oneFromAsync } from './stream_utils.js'; -import { getBatchData, getBatchMeta, makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory } from './util.js'; +import { getBatchData, getBatchMeta, makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory, ZERO_LSN } from './util.js'; const TEST_TABLE = makeTestTable('test', ['id']); @@ -22,7 +22,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -65,7 +65,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result1 = await storage.startBatch({}, async (batch) => { + const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -75,7 +75,7 @@ bucket_definitions: } }); }); - const result2 = await storage.startBatch({}, async (batch) => { + const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -113,7 +113,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -154,7 +154,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -195,7 +195,7 @@ bucket_definitions: `); const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -258,7 +258,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, tag: 'insert', @@ -304,7 +304,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, tag: 'insert', @@ -372,7 +372,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, tag: 'insert', @@ -454,7 +454,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); const sourceTable = TEST_TABLE; - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable, tag: 'insert', @@ -510,7 +510,7 @@ bucket_definitions: `); const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -523,7 +523,7 @@ bucket_definitions: }); }); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -535,7 +535,7 @@ bucket_definitions: }); }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -585,7 +585,7 @@ bucket_definitions: `); const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -598,7 +598,7 @@ bucket_definitions: }); }); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -628,7 +628,7 @@ bucket_definitions: }); }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -701,7 +701,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -740,7 +740,7 @@ bucket_definitions: const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); // Pre-setup - const result1 = await storage.startBatch({}, async (batch) => { + const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -765,7 +765,7 @@ bucket_definitions: const checkpoint1 = result1?.flushed_op ?? '0'; // Test batch - const result2 = await storage.startBatch({}, async (batch) => { + const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; // b await batch.save({ @@ -876,7 +876,7 @@ bucket_definitions: const sourceTable = makeTestTable('test', ['id', 'description']); // Pre-setup - const result1 = await storage.startBatch({}, async (batch) => { + const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable, tag: 'insert', @@ -889,7 +889,7 @@ bucket_definitions: const checkpoint1 = result1?.flushed_op ?? '0'; - const result2 = await storage.startBatch({}, async (batch) => { + const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { // Unchanged, but has a before id await batch.save({ sourceTable, @@ -905,7 +905,7 @@ bucket_definitions: }); }); - const result3 = await storage.startBatch({}, async (batch) => { + const result3 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { // Delete await batch.save({ sourceTable, @@ -968,7 +968,7 @@ bucket_definitions: const sourceTable = makeTestTable('test', ['id', 'description']); // Pre-setup - const result1 = await storage.startBatch({}, async (batch) => { + const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable, tag: 'insert', @@ -981,7 +981,7 @@ bucket_definitions: const checkpoint1 = result1?.flushed_op ?? '0'; - const result2 = await storage.startBatch({}, async (batch) => { + const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { // Unchanged, but has a before id await batch.save({ sourceTable, @@ -997,7 +997,7 @@ bucket_definitions: }); }); - const result3 = await storage.startBatch({}, async (batch) => { + const result3 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { // Delete await batch.save({ sourceTable, @@ -1054,7 +1054,7 @@ bucket_definitions: `); const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; const largeDescription = '0123456789'.repeat(12_000_00); @@ -1146,7 +1146,7 @@ bucket_definitions: `); const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; const largeDescription = '0123456789'.repeat(2_000_00); @@ -1235,7 +1235,7 @@ bucket_definitions: `); const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { const sourceTable = TEST_TABLE; for (let i = 1; i <= 6; i++) { diff --git a/packages/service-core/test/src/sync.test.ts b/packages/service-core/test/src/sync.test.ts index 496837e1e..c5e702633 100644 --- a/packages/service-core/test/src/sync.test.ts +++ b/packages/service-core/test/src/sync.test.ts @@ -1,5 +1,3 @@ -// FIXME: This needs to come from a better place -import { ZERO_LSN } from '@/storage/storage-index.js'; import { RequestTracker } from '@/sync/RequestTracker.js'; import { streamResponse } from '@/sync/sync.js'; import { StreamingSyncLine } from '@/util/protocol-types.js'; @@ -7,13 +5,7 @@ import { JSONBig } from '@powersync/service-jsonbig'; import { RequestParameters } from '@powersync/service-sync-rules'; import * as timers from 'timers/promises'; import { describe, expect, test } from 'vitest'; -import { makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory } from './util.js'; - -// FIXME: This should come from somewhere else -export function lsnMakeComparable(text: string) { - const [h, l] = text.split('/'); - return h.padStart(8, '0') + '/' + l.padStart(8, '0'); -} +import { makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory, ZERO_LSN } from './util.js'; describe('sync - mongodb', function () { defineTests(MONGO_STORAGE_FACTORY); @@ -42,7 +34,7 @@ function defineTests(factory: StorageFactory) { await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -61,7 +53,7 @@ function defineTests(factory: StorageFactory) { } }); - await batch.commit(lsnMakeComparable('0/1')); + await batch.commit('0/1'); }); const stream = streamResponse({ @@ -91,7 +83,7 @@ function defineTests(factory: StorageFactory) { await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -102,7 +94,7 @@ function defineTests(factory: StorageFactory) { } }); - await batch.commit(lsnMakeComparable('0/1')); + await batch.commit('0/1'); }); const stream = streamResponse({ @@ -176,7 +168,7 @@ function defineTests(factory: StorageFactory) { expect(await getCheckpointLines(iter)).toMatchSnapshot(); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -186,12 +178,12 @@ function defineTests(factory: StorageFactory) { } }); - await batch.commit(lsnMakeComparable('0/1')); + await batch.commit('0/1'); }); expect(await getCheckpointLines(iter)).toMatchSnapshot(); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -201,7 +193,7 @@ function defineTests(factory: StorageFactory) { } }); - await batch.commit(lsnMakeComparable('0/2')); + await batch.commit('0/2'); }); expect(await getCheckpointLines(iter)).toMatchSnapshot(); @@ -258,7 +250,7 @@ function defineTests(factory: StorageFactory) { await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -277,7 +269,7 @@ function defineTests(factory: StorageFactory) { } }); - await batch.commit(lsnMakeComparable('0/1')); + await batch.commit('0/1'); }); const stream = streamResponse({ @@ -306,7 +298,7 @@ function defineTests(factory: StorageFactory) { // Now we save additional data AND compact before continuing. // This invalidates the checkpoint we've received above. - await storage.startBatch({}, async (batch) => { + await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'update', @@ -325,7 +317,7 @@ function defineTests(factory: StorageFactory) { } }); - await batch.commit(lsnMakeComparable('0/2')); + await batch.commit('0/2'); }); await storage.compact(); diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 761217c05..4d11c3d5b 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -25,6 +25,8 @@ export const MONGO_STORAGE_FACTORY: StorageFactory = async () => { return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); }; +export const ZERO_LSN = '0/0'; + export async function connectMongo() { // Short timeout for tests, to fail fast when the server is not available. // Slightly longer timeouts for CI, to avoid arbitrary test failures From 82dd1b27c13a3bc2d8635da08b87173a0096a4ea Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 28 Aug 2024 16:25:10 +0200 Subject: [PATCH 158/298] Writecheckpoint fix after conflict --- .../src/routes/endpoints/checkpointing.ts | 5 ++- pnpm-lock.yaml | 33 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index db98bc6e8..69250d867 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -63,9 +63,8 @@ export const writeCheckpoint2 = routeDefinition({ storage: { activeBucketStorage } } = service_context; - const writeCheckpoint = await activeBucketStorage.createWriteCheckpoint(user_id!, { '1': currentCheckpoint }); - const writeCheckpoint = await util.createWriteCheckpoint(system.requirePgPool(), storage, full_user_id); - logger.info(`Write checkpoint 2: ${JSON.stringify({ currentCheckpoint, id: String(id) })}`); + const writeCheckpoint = await activeBucketStorage.createWriteCheckpoint(full_user_id, { '1': currentCheckpoint }); + logger.info(`Write checkpoint 2: ${JSON.stringify({ currentCheckpoint, id: String(full_user_id) })}`); return { write_checkpoint: String(writeCheckpoint) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 00d2a1f1a..cd8bcf24b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -465,6 +465,28 @@ importers: specifier: ^0.34.6 version: 0.34.6 + test-client: + dependencies: + '@powersync/service-core': + specifier: workspace:* + version: link:../packages/service-core + commander: + specifier: ^12.0.0 + version: 12.1.0 + jose: + specifier: ^4.15.1 + version: 4.15.9 + yaml: + specifier: ^2.5.0 + version: 2.5.0 + devDependencies: + '@types/node': + specifier: 18.11.11 + version: 18.11.11 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + packages: '@babel/code-frame@7.24.7': @@ -3633,6 +3655,11 @@ packages: engines: {node: '>= 14'} hasBin: true + yaml@2.5.0: + resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==} + engines: {node: '>= 14'} + hasBin: true + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -4525,7 +4552,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4533,7 +4560,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -7140,6 +7167,8 @@ snapshots: yaml@2.4.5: {} + yaml@2.5.0: {} + yargs-parser@20.2.9: {} yargs-parser@21.1.1: {} From 755c708aac43d75c2d7100e7bb7148db7cee2e1a Mon Sep 17 00:00:00 2001 From: stevensJourney <51082125+stevensJourney@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:36:16 +0200 Subject: [PATCH 159/298] fix: align debug_api field for compatibility (#6) --- modules/module-postgres/src/types/types.ts | 2 -- .../src/routes/endpoints/admin.ts | 2 +- packages/types/src/config/PowerSyncConfig.ts | 35 +++++++++++-------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/modules/module-postgres/src/types/types.ts b/modules/module-postgres/src/types/types.ts index 4f4acbb83..205b147fe 100644 --- a/modules/module-postgres/src/types/types.ts +++ b/modules/module-postgres/src/types/types.ts @@ -46,8 +46,6 @@ export const PostgresConnectionConfig = service_types.configFile.dataSourceConfi /** Expose database credentials */ demo_database: t.boolean.optional(), - /** Expose "execute-sql" */ - debug_api: t.boolean.optional(), /** * Prefix for the slot name. Defaults to "powersync_" diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index d21c02259..77366abba 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -21,7 +21,7 @@ export const executeSql = routeDefinition({ const api = payload.context.service_context.routerEngine.getAPI(); const sourceConfig = await api?.getSourceConfig(); - if (!sourceConfig?.debug_enabled) { + if (!sourceConfig?.debug_api) { return internal_routes.ExecuteSqlResponse.encode({ results: { columns: [], diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index 87607842c..e9dff54f4 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -19,23 +19,27 @@ export const portParser = { }) }; -export const dataSourceConfig = t - .object({ - // Unique string identifier for the data source - type: t.string, - /** Unique identifier for the connection - optional when a single connection is present. */ - id: t.string.optional(), - /** Additional meta tag for connection */ - tag: t.string.optional(), - /** - * Allows for debug query execution - */ - debug_enabled: t.boolean.optional() - }) - .and(t.record(t.any)); // This essentially allows any extra fields on this type +export const dataSourceConfig = t.object({ + // Unique string identifier for the data source + type: t.string, + /** Unique identifier for the connection - optional when a single connection is present. */ + id: t.string.optional(), + /** Additional meta tag for connection */ + tag: t.string.optional(), + /** + * Allows for debug query execution + */ + debug_api: t.boolean.optional() +}); export type DataSourceConfig = t.Decoded; +/** + * This essentially allows any extra fields on this type + */ +export const genericDataSourceConfig = dataSourceConfig.and(t.record(t.any)); +export type GenericDataSourceConfig = t.Decoded; + export const jwkRSA = t.object({ kty: t.literal('RSA'), kid: t.string, @@ -78,7 +82,8 @@ export type StorageConfig = t.Decoded; export const powerSyncConfig = t.object({ replication: t .object({ - connections: t.array(dataSourceConfig).optional() + // This uses the generic config which may have additional fields + connections: t.array(genericDataSourceConfig).optional() }) .optional(), From 593cad2734c58b07164047aae87b200876779aa4 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 6 Sep 2024 11:00:23 +0200 Subject: [PATCH 160/298] Implement powersync instance teardown functionality Reworked replication config resource cleanup Streamlined storage provider and engine a bit. Exposed option to dispose of storage. Made some engines in the service context optional --- .../src/module/PostgresModule.ts | 62 ++++++++++++----- .../replication/WalStreamReplicationJob.ts | 28 ++++---- .../src/replication/WalStreamReplicator.ts | 18 ++++- .../src/replication/replication-utils.ts | 10 +++ packages/service-core/src/api/diagnostics.ts | 28 +++----- .../src/entry/commands/teardown-action.ts | 2 +- .../src/modules/AbstractModule.ts | 12 +++- .../service-core/src/modules/ModuleManager.ts | 8 +-- .../src/replication/AbstractReplicationJob.ts | 19 +----- .../src/replication/AbstractReplicator.ts | 38 ++++++----- .../src/replication/ReplicationModule.ts | 13 ++-- .../src/routes/endpoints/admin.ts | 67 +++++++++++++------ .../src/routes/endpoints/checkpointing.ts | 19 ++++-- .../src/routes/endpoints/socket-route.ts | 7 +- .../src/routes/endpoints/sync-rules.ts | 35 ++++++---- .../src/routes/endpoints/sync-stream.ts | 11 ++- packages/service-core/src/runner/teardown.ts | 60 ++++++++++++++--- .../service-core/src/storage/BucketStorage.ts | 9 ++- .../service-core/src/storage/StorageEngine.ts | 42 +++++++----- .../src/storage/StorageProvider.ts | 7 +- .../src/storage/mongo/MongoStorageProvider.ts | 7 +- .../storage/mongo/MongoSyncBucketStorage.ts | 11 +-- .../src/storage/mongo/MongoSyncRulesLock.ts | 2 +- .../service-core/src/system/ServiceContext.ts | 36 +++++----- service/src/metrics.ts | 6 +- service/src/runners/server.ts | 26 +++---- 26 files changed, 375 insertions(+), 208 deletions(-) diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 0323b06b1..5dc0caf79 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,4 +1,11 @@ -import { api, auth, ConfigurationFileSyncRulesProvider, replication, system } from '@powersync/service-core'; +import { + api, + auth, + ConfigurationFileSyncRulesProvider, + replication, + system, + TearDownOptions +} from '@powersync/service-core'; import * as jpgwire from '@powersync/service-jpgwire'; import * as types from '../types/types.js'; import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js'; @@ -6,6 +13,8 @@ import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; import { WalStreamReplicator } from '../replication/WalStreamReplicator.js'; import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js'; import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js'; +import { cleanUpReplicationSlot } from '../replication/replication-utils.js'; +import { PgManager } from '../replication/PgManager.js'; export class PostgresModule extends replication.ReplicationModule { constructor() { @@ -24,29 +33,28 @@ export class PostgresModule extends replication.ReplicationModule {} + async teardown(options: TearDownOptions): Promise { + const normalisedConfig = this.resolveConfig(this.decodedConfig!); + const connectionManager = new PgManager(normalisedConfig, { + idleTimeout: 30_000, + maxSize: 1 + }); + + try { + if (options.syncRules) { + // TODO: In the future, once we have more replication types, we will need to check if these syncRules are for Postgres + for (let syncRules of options.syncRules) { + try { + await cleanUpReplicationSlot(syncRules.slot_name, connectionManager.pool); + } catch (e) { + // Not really much we can do here for failures, most likely the database is no longer accessible + this.logger.warn(`Failed to fully clean up Postgres replication slot: ${syncRules.slot_name}`, e); + } + } + } + } finally { + await connectionManager.end(); + } + } // TODO: This should rather be done by registering the key collector in some kind of auth engine private registerSupabaseAuth(context: system.ServiceContextContainer) { diff --git a/modules/module-postgres/src/replication/WalStreamReplicationJob.ts b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts index 3c5731729..00603d48d 100644 --- a/modules/module-postgres/src/replication/WalStreamReplicationJob.ts +++ b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts @@ -4,6 +4,7 @@ import { PgManager } from './PgManager.js'; import { replication } from '@powersync/service-core'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; +import { cleanUpReplicationSlot } from './replication-utils.js'; export interface WalStreamReplicationJobOptions extends replication.AbstractReplicationJobOptions { connectionFactory: ConnectionManagerFactory; @@ -11,7 +12,7 @@ export interface WalStreamReplicationJobOptions extends replication.AbstractRepl export class WalStreamReplicationJob extends replication.AbstractReplicationJob { private connectionFactory: ConnectionManagerFactory; - private connectionManager: PgManager; + private readonly connectionManager: PgManager; constructor(options: WalStreamReplicationJobOptions) { super(options); @@ -24,15 +25,14 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob } async cleanUp(): Promise { - this.logger.info(`Cleaning up replication slot: ${this.slot_name}`); - + const connectionManager = this.connectionFactory.create({ + idleTimeout: 30_000, + maxSize: 1 + }); try { - await this.connectionManager.pool.query({ - statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', - params: [{ type: 'varchar', value: this.slot_name }] - }); + await cleanUpReplicationSlot(this.slotName, connectionManager.pool); } finally { - await this.connectionManager.end(); + await connectionManager.end(); } } @@ -56,7 +56,7 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob } } - get slot_name() { + get slotName() { return this.options.storage.slot_name; } @@ -67,14 +67,14 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob // Fatal exception container.reporter.captureException(e, { metadata: { - replication_slot: this.slot_name + replication_slot: this.slotName } }); - this.logger.error(`Replication failed on ${this.slot_name}`, e); + this.logger.error(`Replication failed on ${this.slotName}`, e); if (e instanceof MissingReplicationSlotError) { // This stops replication on this slot, and creates a new slot - await this.options.storage.factory.slotRemoved(this.slot_name); + await this.options.storage.factory.slotRemoved(this.slotName); } } finally { this.abortController.abort(); @@ -108,7 +108,7 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob const stream = new WalStream({ abort_signal: this.abortController.signal, storage: this.options.storage, - connections: this.connectionManager + connections: connectionManager }); await stream.replicate(); } catch (e) { @@ -142,7 +142,7 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob // Report the error if relevant, before retrying container.reporter.captureException(e, { metadata: { - replication_slot: this.slot_name + replication_slot: this.slotName } }); // This sets the retry delay diff --git a/modules/module-postgres/src/replication/WalStreamReplicator.ts b/modules/module-postgres/src/replication/WalStreamReplicator.ts index 8002b7806..61848b377 100644 --- a/modules/module-postgres/src/replication/WalStreamReplicator.ts +++ b/modules/module-postgres/src/replication/WalStreamReplicator.ts @@ -1,8 +1,9 @@ -import { AbstractReplicatorOptions, replication } from '@powersync/service-core'; +import { storage, replication } from '@powersync/service-core'; import { WalStreamReplicationJob } from './WalStreamReplicationJob.js'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; +import { cleanUpReplicationSlot } from './replication-utils.js'; -export interface WalStreamReplicatorOptions extends AbstractReplicatorOptions { +export interface WalStreamReplicatorOptions extends replication.AbstractReplicatorOptions { connectionFactory: ConnectionManagerFactory; } @@ -23,6 +24,19 @@ export class WalStreamReplicator extends replication.AbstractReplicator { + const connectionManager = this.connectionFactory.create({ + idleTimeout: 30_000, + maxSize: 1 + }); + try { + // TODO: Slot_name will likely have to come from a different source in the future + await cleanUpReplicationSlot(syncRulesStorage.slot_name, connectionManager.pool); + } finally { + await connectionManager.end(); + } + } + async stop(): Promise { await super.stop(); await this.connectionFactory.shutdown(); diff --git a/modules/module-postgres/src/replication/replication-utils.ts b/modules/module-postgres/src/replication/replication-utils.ts index 089f0abab..b2f0db008 100644 --- a/modules/module-postgres/src/replication/replication-utils.ts +++ b/modules/module-postgres/src/replication/replication-utils.ts @@ -7,6 +7,7 @@ import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import * as pg_utils from '../utils/pgwire_utils.js'; import * as util from '../utils/pgwire_utils.js'; +import { logger } from '@powersync/lib-services-framework'; export interface ReplicaIdentityResult { replicationColumns: storage.ColumnDescriptor[]; @@ -317,3 +318,12 @@ export async function getDebugTableInfo(options: GetDebugTableInfoOptions): Prom ) as service_types.ReplicationError[] }; } + +export async function cleanUpReplicationSlot(slotName: string, db: pgwire.PgClient): Promise { + logger.info(`Cleaning up Postgres replication slot: ${slotName}...`); + + await db.query({ + statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', + params: [{ type: 'varchar', value: slotName }] + }); +} diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index d0b8a2ca3..7fd4ef440 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -3,7 +3,7 @@ import { DEFAULT_TAG, SourceTableInterface, SqlSyncRules } from '@powersync/serv import { SyncRulesStatus, TableInfo } from '@powersync/service-types'; import * as storage from '../storage/storage-index.js'; -import * as system from '../system/system-index.js'; +import { RouteAPI } from './RouteAPI.js'; export interface DiagnosticsOptions { /** @@ -27,7 +27,8 @@ export interface DiagnosticsOptions { export const DEFAULT_DATASOURCE_ID = 'default'; export async function getSyncRulesStatus( - serviceContext: system.ServiceContext, + bucketStorage: storage.BucketStorageFactory, + apiHandler: RouteAPI, sync_rules: storage.PersistedSyncRulesContent | null, options: DiagnosticsOptions ): Promise { @@ -52,24 +53,15 @@ export async function getSyncRulesStatus( }; } - const { - storage: { activeBucketStorage } - } = serviceContext; - const api = serviceContext.routerEngine.getAPI(); - - const systemStorage = live_status ? activeBucketStorage.getInstance(persisted) : undefined; + const systemStorage = live_status ? bucketStorage.getInstance(persisted) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; let tables_flat: TableInfo[] = []; if (check_connection) { - if (!api) { - throw new Error('No connection configured'); - } - const source_table_patterns = rules.getSourceTables(); - const resolved_tables = await api.getDebugTablesInfo(source_table_patterns, rules); + const resolved_tables = await apiHandler.getDebugTablesInfo(source_table_patterns, rules); tables_flat = resolved_tables.flatMap((info) => { if (info.table) { return [info.table]; @@ -82,7 +74,7 @@ export async function getSyncRulesStatus( if (systemStorage) { try { - replication_lag_bytes = await api.getReplicationLag(systemStorage.slot_name); + replication_lag_bytes = await apiHandler.getReplicationLag(systemStorage.slot_name); } catch (e) { // Ignore logger.warn(`Unable to get replication lag`, e); @@ -136,15 +128,15 @@ export async function getSyncRulesStatus( }) ); - const sourceConfig = await api?.getSourceConfig(); - const tag = sourceConfig?.tag ?? DEFAULT_TAG; + const sourceConfig = await apiHandler.getSourceConfig(); + const tag = sourceConfig.tag ?? DEFAULT_TAG; return { content: include_content ? sync_rules.sync_rules_content : undefined, connections: [ { - id: sourceConfig?.id ?? DEFAULT_DATASOURCE_ID, - tag: sourceConfig?.tag ?? DEFAULT_TAG, + id: sourceConfig.id ?? DEFAULT_DATASOURCE_ID, + tag: sourceConfig.tag ?? DEFAULT_TAG, slot_name: sync_rules.slot_name, initial_replication_done: status?.snapshot_done ?? false, // TODO: Rename? diff --git a/packages/service-core/src/entry/commands/teardown-action.ts b/packages/service-core/src/entry/commands/teardown-action.ts index fb983fb79..2515f91af 100644 --- a/packages/service-core/src/entry/commands/teardown-action.ts +++ b/packages/service-core/src/entry/commands/teardown-action.ts @@ -12,7 +12,7 @@ export function registerTearDownAction(program: Command) { return teardownCommand .argument('[ack]', 'Type `TEARDOWN` to confirm teardown should occur') - .description('Terminate all replicating sync rules, deleting the replication slots') + .description('Terminate all replicating sync rules, clear remote configuration and remove all data') .action(async (ack, options) => { if (ack !== 'TEARDOWN') { throw new Error('TEARDOWN was not acknowledged.'); diff --git a/packages/service-core/src/modules/AbstractModule.ts b/packages/service-core/src/modules/AbstractModule.ts index 439622646..0cdb8c626 100644 --- a/packages/service-core/src/modules/AbstractModule.ts +++ b/packages/service-core/src/modules/AbstractModule.ts @@ -1,6 +1,14 @@ import { ServiceContextContainer } from '../system/ServiceContext.js'; import { logger } from '@powersync/lib-services-framework'; import winston from 'winston'; +import { PersistedSyncRulesContent } from '../storage/BucketStorage.js'; + +export interface TearDownOptions { + /** + * If required, tear down any configuration/state for the specific sync rules + */ + syncRules?: PersistedSyncRulesContent[]; +} export interface AbstractModuleOptions { name: string; @@ -19,9 +27,9 @@ export abstract class AbstractModule { public abstract initialize(context: ServiceContextContainer): Promise; /** - * Terminate and clean up any resources managed by the module right away + * Permanently clean up and dispose of any configuration or state for this module. */ - public abstract teardown(): Promise; + public abstract teardown(options: TearDownOptions): Promise; public get name() { return this.options.name; diff --git a/packages/service-core/src/modules/ModuleManager.ts b/packages/service-core/src/modules/ModuleManager.ts index 42811748e..427acd810 100644 --- a/packages/service-core/src/modules/ModuleManager.ts +++ b/packages/service-core/src/modules/ModuleManager.ts @@ -1,8 +1,8 @@ import { logger } from '@powersync/lib-services-framework'; import * as system from '../system/system-index.js'; -import { AbstractModule } from './AbstractModule.js'; +import { AbstractModule, TearDownOptions } from './AbstractModule.js'; /** - * The module manager is responsible for managing the lifecycle of all modules in the system. + * The module manager keeps track of activated modules */ export class ModuleManager { private readonly modules: Map = new Map(); @@ -26,9 +26,9 @@ export class ModuleManager { logger.info(`Successfully Initialized modules.`); } - async tearDown() { + async tearDown(options: TearDownOptions) { for (const module of this.modules.values()) { - await module.teardown(); + await module.teardown(options); } } } diff --git a/packages/service-core/src/replication/AbstractReplicationJob.ts b/packages/service-core/src/replication/AbstractReplicationJob.ts index b8bef79b1..da0dfcae2 100644 --- a/packages/service-core/src/replication/AbstractReplicationJob.ts +++ b/packages/service-core/src/replication/AbstractReplicationJob.ts @@ -29,12 +29,6 @@ export abstract class AbstractReplicationJob { */ abstract keepAlive(): Promise; - /** - * Clean up any configuration or state for this replication on the datasource. - * This assumes that the replication is not currently active. - */ - abstract cleanUp(): Promise; - /** * Start the replication process */ @@ -63,22 +57,11 @@ export abstract class AbstractReplicationJob { await this.isReplicatingPromise; } - /** - * Stop the replication if it is still running. - * Clean up any config on the datasource related to this replication job - */ - public async terminate(): Promise { - this.logger.info(`${this.id} Terminating replication`); - await this.stop(); - await this.cleanUp(); - await this.options.storage.terminate(); - } - public get id() { return this.options.id; } - protected get storage() { + public get storage() { return this.options.storage; } diff --git a/packages/service-core/src/replication/AbstractReplicator.ts b/packages/service-core/src/replication/AbstractReplicator.ts index cd6cd0e4d..bcf480fc9 100644 --- a/packages/service-core/src/replication/AbstractReplicator.ts +++ b/packages/service-core/src/replication/AbstractReplicator.ts @@ -1,6 +1,5 @@ import { hrtime } from 'node:process'; import * as storage from '../storage/storage-index.js'; - import { container, logger } from '@powersync/lib-services-framework'; import { SyncRulesProvider } from '../util/config/sync-rules/sync-rules-provider.js'; import winston from 'winston'; @@ -50,6 +49,12 @@ export abstract class AbstractReplicator; + public get id() { return this.options.id; } @@ -175,7 +180,7 @@ export abstract class AbstractReplicator extends modules.AbstractModule { protected type: string; protected configSchema: t.AnyCodec; + protected decodedConfig: TConfig | undefined; /** * @protected @@ -35,12 +36,12 @@ export abstract class ReplicationModule extend * Create the RouteAPI adapter for the DataSource required to service the sync API * endpoints. */ - protected abstract createRouteAPIAdapter(decodedConfig: TConfig): api.RouteAPI; + protected abstract createRouteAPIAdapter(): api.RouteAPI; /** * Create the Replicator to be used by the ReplicationEngine. */ - protected abstract createReplicator(decodedConfig: TConfig, context: system.ServiceContext): AbstractReplicator; + protected abstract createReplicator(context: system.ServiceContext): AbstractReplicator; /** * Register this module's Replicators and RouteAPI adapters if the required configuration is present. @@ -67,10 +68,10 @@ export abstract class ReplicationModule extend const baseMatchingConfig = matchingConfig[0] as TConfig; // If validation fails, log the error and continue, no replication will happen for this data source this.validateConfig(baseMatchingConfig); - const decodedConfig = this.configSchema.decode(baseMatchingConfig); - context.replicationEngine.register(this.createReplicator(decodedConfig, context)); - const apiAdapter = this.createRouteAPIAdapter(decodedConfig); - context.routerEngine.registerAPI(apiAdapter); + this.decodedConfig = this.configSchema.decode(baseMatchingConfig); + + context.replicationEngine?.register(this.createReplicator(context)); + context.routerEngine?.registerAPI(this.createRouteAPIAdapter()); } catch (e) { this.logger.error('Failed to initialize.', e); } diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index d21c02259..f41fe1e39 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -19,9 +19,13 @@ export const executeSql = routeDefinition({ } } = payload; - const api = payload.context.service_context.routerEngine.getAPI(); - const sourceConfig = await api?.getSourceConfig(); - if (!sourceConfig?.debug_enabled) { + const apiHandler = payload.context.service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } + + const sourceConfig = await apiHandler.getSourceConfig(); + if (!sourceConfig.debug_enabled) { return internal_routes.ExecuteSqlResponse.encode({ results: { columns: [], @@ -32,7 +36,7 @@ export const executeSql = routeDefinition({ }); } - return internal_routes.ExecuteSqlResponse.encode(await api!.executeQuery(query, args)); + return internal_routes.ExecuteSqlResponse.encode(await apiHandler.executeQuery(query, args)); } }); @@ -46,8 +50,12 @@ export const diagnostics = routeDefinition({ const { service_context } = context; const include_content = payload.params.sync_rules_content ?? false; - const apiHandler = service_context.routerEngine.getAPI(); - const status = await apiHandler?.getConnectionStatus(); + const apiHandler = service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } + + const status = await apiHandler.getConnectionStatus(); if (!status) { return internal_routes.DiagnosticsResponse.encode({ connections: [] @@ -55,18 +63,18 @@ export const diagnostics = routeDefinition({ } const { - storage: { activeBucketStorage } + storageEngine: { activeBucketStorage } } = service_context; const active = await activeBucketStorage.getActiveSyncRulesContent(); const next = await activeBucketStorage.getNextSyncRulesContent(); - const active_status = await api.getSyncRulesStatus(service_context, active, { + const active_status = await api.getSyncRulesStatus(activeBucketStorage, apiHandler, active, { include_content, check_connection: status.connected, live_status: true }); - const next_status = await api.getSyncRulesStatus(service_context, next, { + const next_status = await api.getSyncRulesStatus(activeBucketStorage, apiHandler, next, { include_content, check_connection: status.connected, live_status: true @@ -92,9 +100,12 @@ export const getSchema = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }), handler: async (payload) => { - return internal_routes.GetSchemaResponse.encode( - await api.getConnectionsSchema(payload.context.service_context.routerEngine.getAPI()) - ); + const apiHandler = payload.context.service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } + + return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(apiHandler)); } }); @@ -108,7 +119,7 @@ export const reprocess = routeDefinition({ context: { service_context } } = payload; const { - storage: { activeBucketStorage } + storageEngine: { activeBucketStorage } } = service_context; const next = await activeBucketStorage.getNextSyncRules(); if (next != null) { @@ -128,8 +139,12 @@ export const reprocess = routeDefinition({ content: active.sync_rules.content }); - const apiHandler = service_context.routerEngine.getAPI(); - const baseConfig = await apiHandler?.getSourceConfig(); + const apiHandler = service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } + + const baseConfig = await apiHandler.getSourceConfig(); return internal_routes.ReprocessResponse.encode({ connections: [ @@ -154,7 +169,10 @@ export const validate = routeDefinition({ context: { service_context } } = payload; const content = payload.params.sync_rules; - const apiHandler = service_context.routerEngine.getAPI(); + const apiHandler = service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } const schemaData = await api.getConnectionsSchema(apiHandler); const schema = new StaticSchema(schemaData.connections); @@ -176,7 +194,7 @@ export const validate = routeDefinition({ } }; - const connectionStatus = await apiHandler?.getConnectionStatus(); + const connectionStatus = await apiHandler.getConnectionStatus(); if (!connectionStatus) { return internal_routes.ValidateResponse.encode({ errors: [{ level: 'fatal', message: 'No connection configured' }], @@ -184,11 +202,16 @@ export const validate = routeDefinition({ }); } - const status = (await api.getSyncRulesStatus(service_context, sync_rules, { - include_content: false, - check_connection: connectionStatus.connected, - live_status: false - }))!; + const status = (await api.getSyncRulesStatus( + service_context.storageEngine.activeBucketStorage, + apiHandler, + sync_rules, + { + include_content: false, + check_connection: connectionStatus.connected, + live_status: false + } + ))!; if (connectionStatus == null) { status.errors.push({ level: 'fatal', message: 'No connection configured' }); diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index 69250d867..0778a1d9a 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -18,19 +18,22 @@ export const writeCheckpoint = routeDefinition({ const { context: { service_context } } = payload; - const api = service_context.routerEngine.getAPI(); + const apiHandler = service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } // This old API needs a persisted checkpoint id. // Since we don't use LSNs anymore, the only way to get that is to wait. const start = Date.now(); - const head = await api.getReplicationHead(); + const head = await apiHandler.getReplicationHead(); const timeout = 50_000; logger.info(`Waiting for LSN checkpoint: ${head}`); while (Date.now() - start < timeout) { - const cp = await service_context.storage.activeBucketStorage.getActiveCheckpoint(); + const cp = await service_context.storageEngine.activeBucketStorage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { throw new Error('No sync rules available'); } @@ -53,14 +56,18 @@ export const writeCheckpoint2 = routeDefinition({ handler: async (payload) => { const { user_id, service_context } = payload.context; - const api = service_context.routerEngine.getAPI(); + const apiHandler = service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } + const client_id = payload.params.client_id; const full_user_id = util.checkpointUserId(user_id, client_id); // Might want to call this something link replicationHead or something else - const currentCheckpoint = await api.getReplicationHead(); + const currentCheckpoint = await apiHandler.getReplicationHead(); const { - storage: { activeBucketStorage } + storageEngine: { activeBucketStorage } } = service_context; const writeCheckpoint = await activeBucketStorage.createWriteCheckpoint(full_user_id, { '1': currentCheckpoint }); diff --git a/packages/service-core/src/routes/endpoints/socket-route.ts b/packages/service-core/src/routes/endpoints/socket-route.ts index 9b4dc1914..44d0d7010 100644 --- a/packages/service-core/src/routes/endpoints/socket-route.ts +++ b/packages/service-core/src/routes/endpoints/socket-route.ts @@ -14,6 +14,11 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => handler: async ({ context, params, responder, observer, initialN }) => { const { service_context } = context; const { routerEngine } = service_context; + + if (!routerEngine) { + throw new Error(`No routerEngine has not been registered yet.`); + } + if (routerEngine.closed) { responder.onError( new errors.JourneyError({ @@ -31,7 +36,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => const syncParams = new RequestParameters(context.token_payload!, params.parameters ?? {}); const { - storage: { activeBucketStorage } + storageEngine: { activeBucketStorage } } = service_context; // Sanity check before we start the stream const cp = await activeBucketStorage.getActiveCheckpoint(); diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 0ceffa68e..99ab7c7b2 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -3,9 +3,9 @@ import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules'; import type { FastifyPluginAsync } from 'fastify'; import * as t from 'ts-codec'; -import * as system from '../../system/system-index.js'; import { authApi } from '../auth.js'; import { routeDefinition } from '../router.js'; +import { RouteAPI } from '../../api/RouteAPI.js'; const DeploySyncRulesRequest = t.object({ content: t.string @@ -39,7 +39,7 @@ export const deploySyncRules = routeDefinition({ validator: schema.createTsCodecValidator(DeploySyncRulesRequest, { allowAdditional: true }), handler: async (payload) => { const { service_context } = payload.context; - const { storage } = service_context; + const { storageEngine } = service_context; if (service_context.configuration.sync_rules.present) { // If sync rules are configured via the config, disable deploy via the API. @@ -63,7 +63,7 @@ export const deploySyncRules = routeDefinition({ }); } - const sync_rules = await storage.activeBucketStorage.updateSyncRules({ + const sync_rules = await storageEngine.activeBucketStorage.updateSyncRules({ content: content }); @@ -86,8 +86,13 @@ export const validateSyncRules = routeDefinition({ validator: schema.createTsCodecValidator(ValidateSyncRulesRequest, { allowAdditional: true }), handler: async (payload) => { const content = payload.params.content; + const { service_context } = payload.context; + const apiHandler = service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } - const info = await debugSyncRules(payload.context.service_context, content); + const info = await debugSyncRules(apiHandler, content); return replyPrettyJson(info); } @@ -100,8 +105,9 @@ export const currentSyncRules = routeDefinition({ handler: async (payload) => { const { service_context } = payload.context; const { - storage: { activeBucketStorage } + storageEngine: { activeBucketStorage } } = service_context; + const sync_rules = await activeBucketStorage.getActiveSyncRulesContent(); if (!sync_rules) { throw new errors.JourneyError({ @@ -110,10 +116,16 @@ export const currentSyncRules = routeDefinition({ description: 'No active sync rules' }); } - const info = await debugSyncRules(service_context, sync_rules.sync_rules_content); + + const apiHandler = service_context.routerEngine?.getAPI(); + if (!apiHandler) { + throw new Error(`No active route API handler has been found.`); + } + + const info = await debugSyncRules(apiHandler, sync_rules.sync_rules_content); const next = await activeBucketStorage.getNextSyncRulesContent(); - const next_info = next ? await debugSyncRules(service_context, next.sync_rules_content) : null; + const next_info = next ? await debugSyncRules(apiHandler, next.sync_rules_content) : null; const response = { current: { @@ -144,7 +156,7 @@ export const reprocessSyncRules = routeDefinition({ validator: schema.createTsCodecValidator(ReprocessSyncRulesRequest), handler: async (payload) => { const { - storage: { activeBucketStorage } + storageEngine: { activeBucketStorage } } = payload.context.service_context; const sync_rules = await activeBucketStorage.getActiveSyncRules(); if (sync_rules == null) { @@ -174,14 +186,11 @@ function replyPrettyJson(payload: any) { }); } -async function debugSyncRules(serviceContext: system.ServiceContext, sync_rules: string) { +async function debugSyncRules(apiHandler: RouteAPI, sync_rules: string) { try { const rules = SqlSyncRules.fromYaml(sync_rules); const source_table_patterns = rules.getSourceTables(); - - const api = serviceContext.routerEngine.getAPI(); - - const resolved_tables = await api.getDebugTablesInfo(source_table_patterns, rules); + const resolved_tables = await apiHandler.getDebugTablesInfo(source_table_patterns, rules); return { valid: true, diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index e0a003bff..10e2e6281 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -20,10 +20,15 @@ export const syncStreamed = routeDefinition({ validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }), handler: async (payload) => { const { service_context } = payload.context; - const { routerEngine, storage } = service_context; + const { routerEngine, storageEngine } = service_context; const headers = payload.request.headers; const userAgent = headers['x-user-agent'] ?? headers['user-agent']; const clientId = payload.params.client_id; + + if (!routerEngine) { + throw new Error(`No routerEngine has not been registered yet.`); + } + if (routerEngine.closed) { throw new errors.JourneyError({ status: 503, @@ -36,7 +41,7 @@ export const syncStreamed = routeDefinition({ const syncParams = new RequestParameters(payload.context.token_payload!, payload.params.parameters ?? {}); // Sanity check before we start the stream - const cp = await storage.activeBucketStorage.getActiveCheckpoint(); + const cp = await storageEngine.activeBucketStorage.getActiveCheckpoint(); if (!cp.hasSyncRules()) { throw new errors.JourneyError({ status: 500, @@ -52,7 +57,7 @@ export const syncStreamed = routeDefinition({ sync.transformToBytesTracked( sync.ndjson( sync.streamResponse({ - storage: storage.activeBucketStorage, + storage: storageEngine.activeBucketStorage, params, syncParams, token: payload.context.token_payload!, diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index 67b0662d3..61033cce6 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -1,26 +1,70 @@ // Script to tear down the data when deleting an instance. -// This deletes: -// 1. The replication slots on the source postgres instance (if available). -// 2. The mongo database. +// This should: +// 1. Attempt to clean up any remote configuration of data sources that was set up. +// 2. Delete the storage import { container, logger } from '@powersync/lib-services-framework'; import * as modules from '../modules/modules-index.js'; import * as system from '../system/system-index.js'; +import * as storage from '../storage/storage-index.js'; import * as utils from '../util/util-index.js'; +import timers from 'timers/promises'; export async function teardown(runnerConfig: utils.RunnerConfig) { try { + logger.info(`Tearing down PowerSync instance...`); const config = await utils.loadConfig(runnerConfig); const serviceContext = new system.ServiceContextContainer(config); + const moduleManager = container.getImplementation(modules.ModuleManager); + await moduleManager.initialize(serviceContext); + // This is mostly done to ensure that the storage is ready + await serviceContext.lifeCycleEngine.start(); - // TODO Teardown stopped replication, clean up all replication slots and then wiped the MongoDB - // This will now have to take into account multiple modules - // const moduleManager = container.getImplementation(modules.ModuleManager); - // await moduleManager.initialize(serviceContext); - // await moduleManager.tearDown(); + await terminateSyncRules(serviceContext.storageEngine.activeBucketStorage, moduleManager); + await serviceContext.storageEngine.activeStorage.dispose(); + logger.info(`Teardown complete.`); process.exit(0); } catch (e) { logger.error(`Teardown failure`, e); process.exit(1); } } + +async function terminateSyncRules(storageFactory: storage.BucketStorageFactory, moduleManager: modules.ModuleManager) { + logger.info(`Terminating sync rules...`); + const start = Date.now(); + const locks: storage.ReplicationLock[] = []; + while (Date.now() - start < 120_000) { + let retry = false; + const replicatingSyncRules = await storageFactory.getReplicatingSyncRules(); + // Lock all the replicating sync rules + for (const replicatingSyncRule of replicatingSyncRules) { + const lock = await replicatingSyncRule.lock(); + locks.push(lock); + } + + const stoppedSyncRules = await storageFactory.getStoppedSyncRules(); + const combinedSyncRules = [...replicatingSyncRules, ...stoppedSyncRules]; + try { + // Clean up any module specific configuration for the sync rules + await moduleManager.tearDown({ syncRules: combinedSyncRules }); + + // Mark the sync rules as terminated + for (let syncRules of combinedSyncRules) { + const syncRulesStorage = storageFactory.getInstance(syncRules.parsed()); + // The storage will be dropped at the end of the teardown, so we don't need to clear it here + await syncRulesStorage.terminate({ clearStorage: false }); + } + } catch (e) { + retry = true; + for (const lock of locks) { + await lock.release(); + } + } + + if (!retry) { + break; + } + await timers.setTimeout(5_000); + } +} diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 887bc1bee..b89d728ae 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -231,7 +231,7 @@ export interface SyncRulesBucketStorage { * * Must only be called on stopped sync rules. */ - terminate(): Promise; + terminate(options?: TerminateOptions): Promise; getStatus(): Promise; @@ -421,3 +421,10 @@ export interface CompactOptions { */ compactBuckets?: string[]; } + +export interface TerminateOptions { + /** + * If true, also clear the storage before terminating. + */ + clearStorage: boolean; +} diff --git a/packages/service-core/src/storage/StorageEngine.ts b/packages/service-core/src/storage/StorageEngine.ts index 6a6950b7c..966851a26 100644 --- a/packages/service-core/src/storage/StorageEngine.ts +++ b/packages/service-core/src/storage/StorageEngine.ts @@ -1,36 +1,28 @@ -import { LifeCycledSystem } from '@powersync/lib-services-framework'; import { ResolvedPowerSyncConfig } from '../util/util-index.js'; import { BucketStorageFactory } from './BucketStorage.js'; import { BucketStorageProvider, ActiveStorage } from './StorageProvider.js'; export type StorageEngineOptions = { configuration: ResolvedPowerSyncConfig; - lifecycleEngine: LifeCycledSystem; }; export class StorageEngine { // TODO: This will need to revisited when we actually support multiple storage providers. - protected storageProviders: Map = new Map(); - protected currentActiveStorage: ActiveStorage | null = null; - - constructor(options: StorageEngineOptions) { - // This will create the relevant storage provider when the system is started. - options.lifecycleEngine.withLifecycle(null, { - start: async () => { - const { configuration } = options; - this.currentActiveStorage = await this.storageProviders.get(configuration.storage.type)!.getStorage({ - resolvedConfig: configuration - }); - }, - stop: () => this.currentActiveStorage?.disposer() - }); - } + private storageProviders: Map = new Map(); + private currentActiveStorage: ActiveStorage | null = null; + + constructor(private options: StorageEngineOptions) {} get activeBucketStorage(): BucketStorageFactory { + return this.activeStorage.storage; + } + + get activeStorage(): ActiveStorage { if (!this.currentActiveStorage) { throw new Error(`No storage provider has been initialized yet.`); } - return this.currentActiveStorage.storage; + + return this.currentActiveStorage; } /** @@ -40,4 +32,18 @@ export class StorageEngine { registerProvider(provider: BucketStorageProvider) { this.storageProviders.set(provider.type, provider); } + + public async start(): Promise { + const { configuration } = this.options; + this.currentActiveStorage = await this.storageProviders.get(configuration.storage.type)!.getStorage({ + resolvedConfig: configuration + }); + } + + /** + * Shutdown the storage engine, safely shutting down any activated storage providers. + */ + public async shutDown(): Promise { + await this.currentActiveStorage?.shutDown(); + } } diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts index f41b8ac42..3709bb37a 100644 --- a/packages/service-core/src/storage/StorageProvider.ts +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -3,7 +3,12 @@ import * as util from '../util/util-index.js'; export interface ActiveStorage { storage: BucketStorageFactory; - disposer: () => Promise; + shutDown(): Promise; + + /** + * Drop/Dispose of the storage permanently + */ + dispose(): Promise; } export interface GetStorageOptions { diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts index c7fb238f0..afe952497 100644 --- a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -2,6 +2,7 @@ import * as db from '../../db/db-index.js'; import { MongoBucketStorage } from '../MongoBucketStorage.js'; import { BucketStorageProvider, ActiveStorage, GetStorageOptions } from '../StorageProvider.js'; import { PowerSyncMongo } from './db.js'; +import { logger } from '@powersync/lib-services-framework'; export class MongoStorageProvider implements BucketStorageProvider { get type() { @@ -20,7 +21,11 @@ export class MongoStorageProvider implements BucketStorageProvider { // TODO currently need the entire resolved config for this slot_name_prefix: resolvedConfig.slot_name_prefix }), - disposer: () => client.close() + shutDown: () => client.close(), + dispose: () => { + logger.info(`Dropping storage: ${database.db.namespace}...`); + return database.db.dropDatabase(); + } }; } } diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index fdd292206..d8baa4610 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -15,7 +15,8 @@ import { ResolveTableResult, SyncBucketDataBatch, SyncRulesBucketStorage, - SyncRuleStatus + SyncRuleStatus, + TerminateOptions } from '../BucketStorage.js'; import { ChecksumCache, FetchPartialBucketChecksum } from '../ChecksumCache.js'; import { MongoBucketStorage } from '../MongoBucketStorage.js'; @@ -389,9 +390,11 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { ); } - async terminate() { - await this.clear(); - + async terminate(options?: TerminateOptions) { + // Default is to clear the storage except when explicitly requested not to. + if (!options || options?.clearStorage) { + await this.clear(); + } await this.db.sync_rules.updateOne( { _id: this.group_id diff --git a/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts b/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts index 5a9711ab3..dd2c055e3 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts @@ -30,7 +30,7 @@ export class MongoSyncRulesLock implements ReplicationLock { ); if (doc == null) { - throw new Error(`Replication slot ${sync_rules.slot_name} is locked by another process`); + throw new Error(`Sync rules: ${sync_rules.id} have been locked by another process for replication.`); } return new MongoSyncRulesLock(db, sync_rules.id, lockId); } diff --git a/packages/service-core/src/system/ServiceContext.ts b/packages/service-core/src/system/ServiceContext.ts index 4ddd0ff1c..642c3a9ae 100644 --- a/packages/service-core/src/system/ServiceContext.ts +++ b/packages/service-core/src/system/ServiceContext.ts @@ -9,10 +9,10 @@ import * as utils from '../util/util-index.js'; export interface ServiceContext { configuration: utils.ResolvedPowerSyncConfig; lifeCycleEngine: LifeCycledSystem; - metrics: metrics.Metrics; - replicationEngine: replication.ReplicationEngine; - routerEngine: routes.RouterEngine; - storage: storage.StorageEngine; + metrics: metrics.Metrics | null; + replicationEngine: replication.ReplicationEngine | null; + routerEngine: routes.RouterEngine | null; + storageEngine: storage.StorageEngine; } /** @@ -22,29 +22,33 @@ export interface ServiceContext { */ export class ServiceContextContainer implements ServiceContext { lifeCycleEngine: LifeCycledSystem; - storage: storage.StorageEngine; + storageEngine: storage.StorageEngine; constructor(public configuration: utils.ResolvedPowerSyncConfig) { this.lifeCycleEngine = new LifeCycledSystem(); - this.storage = new storage.StorageEngine({ - configuration, - lifecycleEngine: this.lifeCycleEngine + + this.storageEngine = new storage.StorageEngine({ + configuration + }); + this.lifeCycleEngine.withLifecycle(this.storageEngine, { + start: (storageEngine) => storageEngine.start(), + stop: (storageEngine) => storageEngine.shutDown() }); - // Mongo storage is available as an option by default - this.storage.registerProvider(new storage.MongoStorageProvider()); + // Mongo storage is available as an option by default TODO: Consider moving this to a Mongo Storage Module + this.storageEngine.registerProvider(new storage.MongoStorageProvider()); } - get replicationEngine(): replication.ReplicationEngine { - return this.get(replication.ReplicationEngine); + get replicationEngine(): replication.ReplicationEngine | null { + return container.getOptional(replication.ReplicationEngine); } - get routerEngine(): routes.RouterEngine { - return this.get(routes.RouterEngine); + get routerEngine(): routes.RouterEngine | null { + return container.getOptional(routes.RouterEngine); } - get metrics(): metrics.Metrics { - return this.get(metrics.Metrics); + get metrics(): metrics.Metrics | null { + return container.getOptional(metrics.Metrics); } /** diff --git a/service/src/metrics.ts b/service/src/metrics.ts index e231448f5..57ef04db0 100644 --- a/service/src/metrics.ts +++ b/service/src/metrics.ts @@ -16,7 +16,7 @@ export const registerMetrics = async (options: MetricsRegistrationOptions) => { // This requires an instantiated bucket storage, which is only created when the lifecycle starts service_context.lifeCycleEngine.withLifecycle(null, { start: async () => { - const instanceId = await service_context.storage.activeBucketStorage.getPowerSyncInstanceId(); + const instanceId = await service_context.storageEngine.activeBucketStorage.getPowerSyncInstanceId(); await core.metrics.Metrics.initialise({ powersync_instance_id: instanceId, disable_telemetry_sharing: service_context.configuration.telemetry.disable_telemetry_sharing, @@ -32,9 +32,9 @@ export const registerMetrics = async (options: MetricsRegistrationOptions) => { } if (modes.includes(MetricModes.REPLICATION)) { - instance.configureReplicationMetrics(service_context.storage.activeBucketStorage); + instance.configureReplicationMetrics(service_context.storageEngine.activeBucketStorage); } }, - stop: () => service_context.metrics.shutdown() + stop: () => service_context.metrics!.shutdown() }); }; diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 83d980bcf..3a4d7b50e 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -10,20 +10,20 @@ import { SocketRouter } from '../routes/router.js'; /** * Configures the server portion on a {@link ServiceContext} */ -export const registerServerServices = (serviceContext: core.system.ServiceContextContainer) => { +export function registerServerServices(serviceContext: core.system.ServiceContextContainer) { serviceContext.register(core.routes.RouterEngine, new core.routes.RouterEngine()); serviceContext.lifeCycleEngine.withLifecycle(serviceContext.routerEngine, { start: async (routerEngine) => { - await routerEngine.start(async (routes) => { + await routerEngine!.start(async (routes) => { const server = fastify.fastify(); - server.register(cors, { - origin: '*', - allowedHeaders: ['Content-Type', 'Authorization', 'User-Agent', 'X-User-Agent'], - exposedHeaders: ['Content-Type'], - // Cache time for preflight response - maxAge: 3600 - }); + server.register(cors, { + origin: '*', + allowedHeaders: ['Content-Type', 'Authorization', 'User-Agent', 'X-User-Agent'], + exposedHeaders: ['Content-Type'], + // Cache time for preflight response + maxAge: 3600 + }); core.routes.configureFastifyServer(server, { service_context: serviceContext, @@ -54,14 +54,14 @@ export const registerServerServices = (serviceContext: core.system.ServiceContex }; }); }, - stop: (routerEngine) => routerEngine.shutdown() + stop: (routerEngine) => routerEngine!.shutdown() }); -}; +} /** * Starts an API server */ -export const startServer = async (runnerConfig: core.utils.RunnerConfig) => { +export async function startServer(runnerConfig: core.utils.RunnerConfig) { logger.info('Booting'); const config = await core.utils.loadConfig(runnerConfig); @@ -85,4 +85,4 @@ export const startServer = async (runnerConfig: core.utils.RunnerConfig) => { // Enable in development to track memory usage: // trackMemoryUsage(); -}; +} From 9fd930853ce1da9d31345f2e6f273a6e317701d3 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Fri, 6 Sep 2024 15:34:21 +0200 Subject: [PATCH 161/298] add some helpers --- modules/module-mysql/dev/.env.template | 2 + modules/module-mysql/dev/README.md | 9 +++++ .../module-mysql/dev/config/sync_rules.yaml | 12 ++++++ .../dev/docker/mysql/docker-compose.yaml | 0 .../dev/docker/mysql/init-scripts/my.cnf | 9 +++++ .../dev/docker/mysql/init-scripts/mysql.sql | 37 +++++++++++++++++++ 6 files changed, 69 insertions(+) create mode 100644 modules/module-mysql/dev/.env.template create mode 100644 modules/module-mysql/dev/README.md create mode 100644 modules/module-mysql/dev/config/sync_rules.yaml create mode 100644 modules/module-mysql/dev/docker/mysql/docker-compose.yaml create mode 100644 modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf create mode 100644 modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql diff --git a/modules/module-mysql/dev/.env.template b/modules/module-mysql/dev/.env.template new file mode 100644 index 000000000..d82ac9668 --- /dev/null +++ b/modules/module-mysql/dev/.env.template @@ -0,0 +1,2 @@ +PS_MONGO_URI=mongodb://mongo:27017/powersync_demo +PS_PORT=8080 \ No newline at end of file diff --git a/modules/module-mysql/dev/README.md b/modules/module-mysql/dev/README.md new file mode 100644 index 000000000..fa00d7d17 --- /dev/null +++ b/modules/module-mysql/dev/README.md @@ -0,0 +1,9 @@ +# MySQL Development Helpers + +This folder contains some helpers for developing with MySQL. + +- `./.env.template` contains basic settings to be applied to a root `.env` file +- `./config` contains YAML configuration files for a MySQL odo list application +- `./docker/mysql` contains a docker compose file for starting Mysql + +TODO this does not contain any auth or backend functionality. diff --git a/modules/module-mysql/dev/config/sync_rules.yaml b/modules/module-mysql/dev/config/sync_rules.yaml new file mode 100644 index 000000000..eb74a7198 --- /dev/null +++ b/modules/module-mysql/dev/config/sync_rules.yaml @@ -0,0 +1,12 @@ +# See Documentation for more information: +# https://docs.powersync.com/usage/sync-rules +# Note that changes to this file are not watched. +# The service needs to be restarted for changes to take effect. + +# Note that specifying the schema is currently required due to the default +# schema being specified as `public`, but in mysql the schema is the database name +bucket_definitions: + global: + data: + - SELECT * FROM mydatabase.lists + - SELECT * FROM mydatabase.todos diff --git a/modules/module-mysql/dev/docker/mysql/docker-compose.yaml b/modules/module-mysql/dev/docker/mysql/docker-compose.yaml new file mode 100644 index 000000000..e69de29bb diff --git a/modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf b/modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf new file mode 100644 index 000000000..815c4b690 --- /dev/null +++ b/modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf @@ -0,0 +1,9 @@ +[mysqld] +gtid_mode = ON +enforce-gtid-consistency = ON +# Row format required for ZongJi +binlog_format = row +log_bin=mysql-bin +server-id=1 +binlog-do-db=mydatabase +replicate-do-table=mydatabase.lists \ No newline at end of file diff --git a/modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql b/modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql new file mode 100644 index 000000000..480ae786c --- /dev/null +++ b/modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql @@ -0,0 +1,37 @@ +-- Create a user with necessary privileges +CREATE USER 'repl_user'@'%' IDENTIFIED BY 'good_password'; + +-- Grant replication client privilege +GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl_user'@'%'; + +-- Grant access to the specific database +GRANT ALL PRIVILEGES ON mydatabase.* TO 'repl_user'@'%'; + +-- Apply changes +FLUSH PRIVILEGES; + +CREATE TABLE lists ( + id CHAR(36) NOT NULL DEFAULT (UUID()), -- String UUID (36 characters) + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + name TEXT NOT NULL, + owner_id CHAR(36) NOT NULL, + PRIMARY KEY (id) +); + +CREATE TABLE todos ( + id CHAR(36) NOT NULL DEFAULT (UUID()), -- String UUID (36 characters) + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + completed_at TIMESTAMP NULL, + description TEXT NOT NULL, + completed BOOLEAN NOT NULL DEFAULT FALSE, + created_by CHAR(36) NULL, + completed_by CHAR(36) NULL, + list_id CHAR(36) NOT NULL, + PRIMARY KEY (id), + FOREIGN KEY (list_id) REFERENCES lists (id) ON DELETE CASCADE +); + +-- TODO fix case where no data is present +INSERT INTO lists (id, name, owner_id) +VALUES + (UUID(), 'Do a demo', UUID()); \ No newline at end of file From 6a075e165ddbf3c7b1f22978e5cb42b8ce0934ea Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 9 Sep 2024 09:16:47 +0200 Subject: [PATCH 162/298] Adjusted checks in routes for the the RouterEngine --- packages/service-core/src/api/schema.ts | 2 +- .../src/routes/endpoints/admin.ts | 25 ++++--------------- .../src/routes/endpoints/checkpointing.ts | 10 ++------ .../src/routes/endpoints/socket-route.ts | 8 ++---- .../src/routes/endpoints/sync-rules.ts | 11 ++------ .../src/routes/endpoints/sync-stream.ts | 8 ++---- 6 files changed, 14 insertions(+), 50 deletions(-) diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index 30e737363..aff6d770f 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -2,7 +2,7 @@ import { internal_routes } from '@powersync/service-types'; import * as api from '../api/api-index.js'; -export async function getConnectionsSchema(api: api.RouteAPI | null): Promise { +export async function getConnectionsSchema(api: api.RouteAPI): Promise { if (!api) { return { connections: [] diff --git a/packages/service-core/src/routes/endpoints/admin.ts b/packages/service-core/src/routes/endpoints/admin.ts index 01a7efc1e..9adff16c4 100644 --- a/packages/service-core/src/routes/endpoints/admin.ts +++ b/packages/service-core/src/routes/endpoints/admin.ts @@ -19,10 +19,7 @@ export const executeSql = routeDefinition({ } } = payload; - const apiHandler = payload.context.service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = payload.context.service_context.routerEngine!.getAPI(); const sourceConfig = await apiHandler.getSourceConfig(); if (!sourceConfig.debug_api) { @@ -50,10 +47,7 @@ export const diagnostics = routeDefinition({ const { service_context } = context; const include_content = payload.params.sync_rules_content ?? false; - const apiHandler = service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = service_context.routerEngine!.getAPI(); const status = await apiHandler.getConnectionStatus(); if (!status) { @@ -100,10 +94,7 @@ export const getSchema = routeDefinition({ authorize: authApi, validator: schema.createTsCodecValidator(internal_routes.GetSchemaRequest, { allowAdditional: true }), handler: async (payload) => { - const apiHandler = payload.context.service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = payload.context.service_context.routerEngine!.getAPI(); return internal_routes.GetSchemaResponse.encode(await api.getConnectionsSchema(apiHandler)); } @@ -139,10 +130,7 @@ export const reprocess = routeDefinition({ content: active.sync_rules.content }); - const apiHandler = service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = service_context.routerEngine!.getAPI(); const baseConfig = await apiHandler.getSourceConfig(); @@ -169,10 +157,7 @@ export const validate = routeDefinition({ context: { service_context } } = payload; const content = payload.params.sync_rules; - const apiHandler = service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = service_context.routerEngine!.getAPI(); const schemaData = await api.getConnectionsSchema(apiHandler); const schema = new StaticSchema(schemaData.connections); diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index 6f09ca86f..c60c3b233 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -18,10 +18,7 @@ export const writeCheckpoint = routeDefinition({ const { context: { service_context } } = payload; - const apiHandler = service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = service_context.routerEngine!.getAPI(); // This old API needs a persisted checkpoint id. // Since we don't use LSNs anymore, the only way to get that is to wait. @@ -56,10 +53,7 @@ export const writeCheckpoint2 = routeDefinition({ handler: async (payload) => { const { user_id, service_context } = payload.context; - const apiHandler = service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = service_context.routerEngine!.getAPI(); const client_id = payload.params.client_id; const full_user_id = util.checkpointUserId(user_id, client_id); diff --git a/packages/service-core/src/routes/endpoints/socket-route.ts b/packages/service-core/src/routes/endpoints/socket-route.ts index 44d0d7010..d13f62c41 100644 --- a/packages/service-core/src/routes/endpoints/socket-route.ts +++ b/packages/service-core/src/routes/endpoints/socket-route.ts @@ -15,11 +15,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => const { service_context } = context; const { routerEngine } = service_context; - if (!routerEngine) { - throw new Error(`No routerEngine has not been registered yet.`); - } - - if (routerEngine.closed) { + if (routerEngine!.closed) { responder.onError( new errors.JourneyError({ status: 503, @@ -62,7 +58,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => } }); - const removeStopHandler = routerEngine.addStopHandler(() => { + const removeStopHandler = routerEngine!.addStopHandler(() => { observer.triggerCancel(); }); diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 99ab7c7b2..605d3c82d 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -87,10 +87,7 @@ export const validateSyncRules = routeDefinition({ handler: async (payload) => { const content = payload.params.content; const { service_context } = payload.context; - const apiHandler = service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } + const apiHandler = service_context.routerEngine!.getAPI(); const info = await debugSyncRules(apiHandler, content); @@ -117,11 +114,7 @@ export const currentSyncRules = routeDefinition({ }); } - const apiHandler = service_context.routerEngine?.getAPI(); - if (!apiHandler) { - throw new Error(`No active route API handler has been found.`); - } - + const apiHandler = service_context.routerEngine!.getAPI(); const info = await debugSyncRules(apiHandler, sync_rules.sync_rules_content); const next = await activeBucketStorage.getNextSyncRulesContent(); diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index 10e2e6281..e637e99ea 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -25,11 +25,7 @@ export const syncStreamed = routeDefinition({ const userAgent = headers['x-user-agent'] ?? headers['user-agent']; const clientId = payload.params.client_id; - if (!routerEngine) { - throw new Error(`No routerEngine has not been registered yet.`); - } - - if (routerEngine.closed) { + if (routerEngine!.closed) { throw new errors.JourneyError({ status: 503, code: 'SERVICE_UNAVAILABLE', @@ -70,7 +66,7 @@ export const syncStreamed = routeDefinition({ { objectMode: false, highWaterMark: 16 * 1024 } ); - const deregister = routerEngine.addStopHandler(() => { + const deregister = routerEngine!.addStopHandler(() => { // This error is not currently propagated to the client controller.abort(); stream.destroy(new Error('Shutting down system')); From 5b5792d618d139ab623e538d4b058a04b9b90d94 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 10 Sep 2024 07:47:40 +0200 Subject: [PATCH 163/298] Updated lockfile --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 04dd0c2c9..d9b74b6bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -540,7 +540,7 @@ importers: version: 18.11.11 typescript: specifier: ^5.2.2 - version: 5.2.2 + version: 5.5.4 packages: From 285afb05258b6250adb9d7f0da1c3f696aed7387 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 10 Sep 2024 07:53:17 +0200 Subject: [PATCH 164/298] Renamed storage provider storage dispose method. --- packages/service-core/src/runner/teardown.ts | 2 +- packages/service-core/src/storage/StorageProvider.ts | 4 ++-- .../src/storage/mongo/MongoStorageProvider.ts | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index 61033cce6..49e2bc66d 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -21,7 +21,7 @@ export async function teardown(runnerConfig: utils.RunnerConfig) { await serviceContext.lifeCycleEngine.start(); await terminateSyncRules(serviceContext.storageEngine.activeBucketStorage, moduleManager); - await serviceContext.storageEngine.activeStorage.dispose(); + await serviceContext.storageEngine.activeStorage.tearDown(); logger.info(`Teardown complete.`); process.exit(0); } catch (e) { diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts index 3709bb37a..9e6078874 100644 --- a/packages/service-core/src/storage/StorageProvider.ts +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -6,9 +6,9 @@ export interface ActiveStorage { shutDown(): Promise; /** - * Drop/Dispose of the storage permanently + * Tear down / drop the storage permanently */ - dispose(): Promise; + tearDown(): Promise; } export interface GetStorageOptions { diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts index afe952497..e5af38922 100644 --- a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -18,14 +18,14 @@ export class MongoStorageProvider implements BucketStorageProvider { return { storage: new MongoBucketStorage(database, { - // TODO currently need the entire resolved config for this + // TODO currently need the entire resolved config due to this slot_name_prefix: resolvedConfig.slot_name_prefix }), shutDown: () => client.close(), - dispose: () => { - logger.info(`Dropping storage: ${database.db.namespace}...`); + tearDown: () => { + logger.info(`Tearing down storage: ${database.db.namespace}...`); return database.db.dropDatabase(); } - }; + } satisfies ActiveStorage; } } From d2bfd9fa0ffce0df8a6cde4f766d0c28c460f612 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 10 Sep 2024 11:45:52 +0200 Subject: [PATCH 165/298] Update lockfile --- pnpm-lock.yaml | 101 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 14 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80def96eb..7174dd5be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,6 +74,9 @@ importers: ts-codec: specifier: ^1.2.2 version: 1.2.2 + uuid: + specifier: ^9.0.1 + version: 9.0.1 winston: specifier: ^3.13.0 version: 3.13.0 @@ -84,6 +87,61 @@ importers: '@types/lodash': specifier: ^4.17.5 version: 4.17.5 + '@types/uuid': + specifier: ^9.0.4 + version: 9.0.8 + vitest: + specifier: ^0.34.6 + version: 0.34.6 + + modules/module-postgres: + dependencies: + '@powersync/lib-services-framework': + specifier: workspace:* + version: link:../../libs/lib-services + '@powersync/service-core': + specifier: workspace:* + version: link:../../packages/service-core + '@powersync/service-jpgwire': + specifier: workspace:* + version: link:../../packages/jpgwire + '@powersync/service-jsonbig': + specifier: workspace:* + version: link:../../packages/jsonbig + '@powersync/service-sync-rules': + specifier: workspace:* + version: link:../../packages/sync-rules + '@powersync/service-types': + specifier: workspace:* + version: link:../../packages/types + jose: + specifier: ^4.15.1 + version: 4.15.5 + mongodb: + specifier: ^6.7.0 + version: 6.7.0(socks@2.8.3) + pgwire: + specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 + ts-codec: + specifier: ^1.2.2 + version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + devDependencies: + '@types/uuid': + specifier: ^9.0.4 + version: 9.0.8 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.2.2)(vite@5.2.11(@types/node@18.11.11)) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -93,6 +151,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:^ version: link:../jsonbig + '@powersync/service-sync-rules': + specifier: workspace:^ + version: link:../sync-rules '@powersync/service-types': specifier: workspace:^ version: link:../types @@ -169,9 +230,6 @@ importers: '@powersync/lib-services-framework': specifier: workspace:* version: link:../../libs/lib-services - '@powersync/service-jpgwire': - specifier: workspace:* - version: link:../jpgwire '@powersync/service-jsonbig': specifier: workspace:* version: link:../jsonbig @@ -220,9 +278,6 @@ importers: node-fetch: specifier: ^3.3.2 version: 3.3.2 - pgwire: - specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 - version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -321,6 +376,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:* version: link:../packages/jsonbig + '@powersync/service-module-postgres': + specifier: workspace:* + version: link:../modules/module-postgres '@powersync/service-rsocket-router': specifier: workspace:* version: link:../packages/rsocket-router @@ -5472,7 +5530,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -6518,7 +6576,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -6530,7 +6588,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -7687,7 +7745,7 @@ snapshots: require-in-the-middle@7.3.0: dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -7896,7 +7954,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -8183,6 +8241,10 @@ snapshots: string-argv: 0.3.2 typescript: 5.2.2 + tsconfck@3.0.3(typescript@5.2.2): + optionalDependencies: + typescript: 5.2.2 + tsconfck@3.0.3(typescript@5.4.5): optionalDependencies: typescript: 5.4.5 @@ -8209,7 +8271,7 @@ snapshots: tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color @@ -8344,7 +8406,7 @@ snapshots: vite-node@0.34.6(@types/node@18.11.11): dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 mlly: 1.7.0 pathe: 1.1.2 picocolors: 1.0.1 @@ -8376,9 +8438,20 @@ snapshots: - supports-color - terser + vite-tsconfig-paths@4.3.2(typescript@5.2.2)(vite@5.2.11(@types/node@18.11.11)): + dependencies: + debug: 4.3.7 + globrex: 0.1.2 + tsconfck: 3.0.3(typescript@5.2.2) + optionalDependencies: + vite: 5.2.11(@types/node@18.11.11) + transitivePeerDependencies: + - supports-color + - typescript + vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@18.11.11)): dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.0.3(typescript@5.4.5) optionalDependencies: From 04cc1bd399a5d22c568586e03e7176d6c35d4f05 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 10 Sep 2024 18:05:25 +0200 Subject: [PATCH 166/298] Basic MongoDB replication structure. --- modules/module-mongodb/CHANGELOG.md | 1 + modules/module-mongodb/LICENSE | 67 ++++ modules/module-mongodb/README.md | 3 + modules/module-mongodb/package.json | 47 +++ .../src/api/MongoRouteAPIAdapter.ts | 75 +++++ modules/module-mongodb/src/index.ts | 5 + .../module-mongodb/src/module/MongoModule.ts | 52 +++ .../src/replication/ChangeStream.ts | 304 ++++++++++++++++++ .../replication/ChangeStreamReplicationJob.ts | 109 +++++++ .../src/replication/ChangeStreamReplicator.ts | 34 ++ .../replication/ConnectionManagerFactory.ts | 27 ++ .../src/replication/MongoErrorRateLimiter.ts | 45 +++ .../src/replication/MongoManager.ts | 47 +++ .../src/replication/MongoRelation.ts | 104 ++++++ .../src/replication/replication-index.ts | 4 + modules/module-mongodb/src/types/types.ts | 143 ++++++++ modules/module-mongodb/test/tsconfig.json | 28 ++ modules/module-mongodb/tsconfig.json | 28 ++ modules/module-mongodb/vitest.config.ts | 9 + pnpm-lock.yaml | 46 +++ service/package.json | 1 + service/src/entry.ts | 3 +- service/tsconfig.json | 3 + 23 files changed, 1184 insertions(+), 1 deletion(-) create mode 100644 modules/module-mongodb/CHANGELOG.md create mode 100644 modules/module-mongodb/LICENSE create mode 100644 modules/module-mongodb/README.md create mode 100644 modules/module-mongodb/package.json create mode 100644 modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts create mode 100644 modules/module-mongodb/src/index.ts create mode 100644 modules/module-mongodb/src/module/MongoModule.ts create mode 100644 modules/module-mongodb/src/replication/ChangeStream.ts create mode 100644 modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts create mode 100644 modules/module-mongodb/src/replication/ChangeStreamReplicator.ts create mode 100644 modules/module-mongodb/src/replication/ConnectionManagerFactory.ts create mode 100644 modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts create mode 100644 modules/module-mongodb/src/replication/MongoManager.ts create mode 100644 modules/module-mongodb/src/replication/MongoRelation.ts create mode 100644 modules/module-mongodb/src/replication/replication-index.ts create mode 100644 modules/module-mongodb/src/types/types.ts create mode 100644 modules/module-mongodb/test/tsconfig.json create mode 100644 modules/module-mongodb/tsconfig.json create mode 100644 modules/module-mongodb/vitest.config.ts diff --git a/modules/module-mongodb/CHANGELOG.md b/modules/module-mongodb/CHANGELOG.md new file mode 100644 index 000000000..05f7d8b81 --- /dev/null +++ b/modules/module-mongodb/CHANGELOG.md @@ -0,0 +1 @@ +# @powersync/service-module-mongodb diff --git a/modules/module-mongodb/LICENSE b/modules/module-mongodb/LICENSE new file mode 100644 index 000000000..c8efd46cc --- /dev/null +++ b/modules/module-mongodb/LICENSE @@ -0,0 +1,67 @@ +# Functional Source License, Version 1.1, Apache 2.0 Future License + +## Abbreviation + +FSL-1.1-Apache-2.0 + +## Notice + +Copyright 2023-2024 Journey Mobile, Inc. + +## Terms and Conditions + +### Licensor ("We") + +The party offering the Software under these Terms and Conditions. + +### The Software + +The "Software" is each version of the software that we make available under these Terms and Conditions, as indicated by our inclusion of these Terms and Conditions with the Software. + +### License Grant + +Subject to your compliance with this License Grant and the Patents, Redistribution and Trademark clauses below, we hereby grant you the right to use, copy, modify, create derivative works, publicly perform, publicly display and redistribute the Software for any Permitted Purpose identified below. + +### Permitted Purpose + +A Permitted Purpose is any purpose other than a Competing Use. A Competing Use means making the Software available to others in a commercial product or service that: + +1. substitutes for the Software; +2. substitutes for any other product or service we offer using the Software that exists as of the date we make the Software available; or +3. offers the same or substantially similar functionality as the Software. + +Permitted Purposes specifically include using the Software: + +1. for your internal use and access; +2. for non-commercial education; +3. for non-commercial research; and +4. in connection with professional services that you provide to a licensee using the Software in accordance with these Terms and Conditions. + +### Patents + +To the extent your use for a Permitted Purpose would necessarily infringe our patents, the license grant above includes a license under our patents. If you make a claim against any party that the Software infringes or contributes to the infringement of any patent, then your patent license to the Software ends immediately. + +### Redistribution + +The Terms and Conditions apply to all copies, modifications and derivatives of the Software. +If you redistribute any copies, modifications or derivatives of the Software, you must include a copy of or a link to these Terms and Conditions and not remove any copyright notices provided in or with the Software. + +### Disclaimer + +THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT. +IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE. + +### Trademarks + +Except for displaying the License Details and identifying us as the origin of the Software, you have no right under these Terms and Conditions to use our trademarks, trade names, service marks or product names. + +## Grant of Future License + +We hereby irrevocably grant you an additional license to use the Software under the Apache License, Version 2.0 that is effective on the second anniversary of the date we make the Software available. On or after that date, you may use the Software under the Apache License, Version 2.0, in which case the following will apply: + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/modules/module-mongodb/README.md b/modules/module-mongodb/README.md new file mode 100644 index 000000000..f9e9e4c64 --- /dev/null +++ b/modules/module-mongodb/README.md @@ -0,0 +1,3 @@ +# PowerSync Service Module MongoDB + +MongoDB replication module for PowerSync diff --git a/modules/module-mongodb/package.json b/modules/module-mongodb/package.json new file mode 100644 index 000000000..dfcd52e0e --- /dev/null +++ b/modules/module-mongodb/package.json @@ -0,0 +1,47 @@ +{ + "name": "@powersync/service-module-mongodb", + "repository": "https://github.com/powersync-ja/powersync-service", + "types": "dist/index.d.ts", + "publishConfig": { + "access": "restricted" + }, + "version": "0.0.1", + "main": "dist/index.js", + "license": "FSL-1.1-Apache-2.0", + "type": "module", + "scripts": { + "build": "tsc -b", + "build:tests": "tsc -b test/tsconfig.json", + "clean": "rm -rf ./lib && tsc -b --clean", + "test": "vitest --no-threads" + }, + "exports": { + ".": { + "import": "./dist/index.js", + "require": "./dist/index.js", + "default": "./dist/index.js" + }, + "./types": { + "import": "./dist/types/types.js", + "require": "./dist/types/types.js", + "default": "./dist/types/types.js" + } + }, + "dependencies": { + "@powersync/lib-services-framework": "workspace:*", + "@powersync/service-core": "workspace:*", + "@powersync/service-jsonbig": "workspace:*", + "@powersync/service-sync-rules": "workspace:*", + "@powersync/service-types": "workspace:*", + "mongodb": "^6.7.0", + "ts-codec": "^1.2.2", + "uuid": "^9.0.1", + "uri-js": "^4.4.1" + }, + "devDependencies": { + "@types/uuid": "^9.0.4", + "typescript": "^5.2.2", + "vitest": "^0.34.6", + "vite-tsconfig-paths": "^4.3.2" + } +} diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts new file mode 100644 index 000000000..61aed14f1 --- /dev/null +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -0,0 +1,75 @@ +import { api } from '@powersync/service-core'; +import * as mongo from 'mongodb'; + +import * as sync_rules from '@powersync/service-sync-rules'; +import * as service_types from '@powersync/service-types'; +import * as types from '../types/types.js'; +import { MongoManager } from '../replication/MongoManager.js'; + +export class MongoRouteAPIAdapter implements api.RouteAPI { + protected client: mongo.MongoClient; + + connectionTag: string; + + constructor(protected config: types.ResolvedConnectionConfig) { + this.client = new MongoManager(config).client; + this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG; + } + + async shutdown(): Promise { + await this.client.close(); + } + + async getSourceConfig(): Promise { + return this.config; + } + + async getConnectionStatus(): Promise { + // TODO: Implement + const base = { + id: this.config.id, + uri: types.baseUri(this.config) + }; + return { + ...base, + connected: true, + errors: [] + }; + } + + async executeQuery(query: string, params: any[]): Promise { + return service_types.internal_routes.ExecuteSqlResponse.encode({ + results: { + columns: [], + rows: [] + }, + success: false, + error: 'SQL querying is not supported for MongoDB' + }); + } + + async getDebugTablesInfo( + tablePatterns: sync_rules.TablePattern[], + sqlSyncRules: sync_rules.SqlSyncRules + ): Promise { + // TODO: Implement + return []; + } + + async getReplicationLag(syncRulesId: string): Promise { + // TODO: Implement + + return 0; + } + + async getReplicationHead(): Promise { + // TODO: implement + return ''; + } + + async getConnectionSchema(): Promise { + // TODO: Implement + + return []; + } +} diff --git a/modules/module-mongodb/src/index.ts b/modules/module-mongodb/src/index.ts new file mode 100644 index 000000000..4cfc25695 --- /dev/null +++ b/modules/module-mongodb/src/index.ts @@ -0,0 +1,5 @@ +import { MongoModule } from './module/MongoModule.js'; + +export const module = new MongoModule(); + +export default module; diff --git a/modules/module-mongodb/src/module/MongoModule.ts b/modules/module-mongodb/src/module/MongoModule.ts new file mode 100644 index 000000000..3f6e27636 --- /dev/null +++ b/modules/module-mongodb/src/module/MongoModule.ts @@ -0,0 +1,52 @@ +import { api, ConfigurationFileSyncRulesProvider, replication, system, TearDownOptions } from '@powersync/service-core'; +import { MongoRouteAPIAdapter } from '../api/MongoRouteAPIAdapter.js'; +import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js'; +import { MongoErrorRateLimiter } from '../replication/MongoErrorRateLimiter.js'; +import { ChangeStreamReplicator } from '../replication/ChangeStreamReplicator.js'; +import * as types from '../types/types.js'; + +export class MongoModule extends replication.ReplicationModule { + constructor() { + super({ + name: 'MongoDB', + type: types.MONGO_CONNECTION_TYPE, + configSchema: types.MongoConnectionConfig + }); + } + + async initialize(context: system.ServiceContextContainer): Promise { + await super.initialize(context); + } + + protected createRouteAPIAdapter(): api.RouteAPI { + return new MongoRouteAPIAdapter(this.resolveConfig(this.decodedConfig!)); + } + + protected createReplicator(context: system.ServiceContext): replication.AbstractReplicator { + const normalisedConfig = this.resolveConfig(this.decodedConfig!); + const syncRuleProvider = new ConfigurationFileSyncRulesProvider(context.configuration.sync_rules); + const connectionFactory = new ConnectionManagerFactory(normalisedConfig); + + return new ChangeStreamReplicator({ + id: this.getDefaultId(normalisedConfig.database ?? ''), + syncRuleProvider: syncRuleProvider, + storageEngine: context.storageEngine, + connectionFactory: connectionFactory, + rateLimiter: new MongoErrorRateLimiter() + }); + } + + /** + * Combines base config with normalized connection settings + */ + private resolveConfig(config: types.MongoConnectionConfig): types.ResolvedConnectionConfig { + return { + ...config, + ...types.normalizeConnectionConfig(config) + }; + } + + async teardown(options: TearDownOptions): Promise { + // TODO: Implement? + } +} diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts new file mode 100644 index 000000000..8ad5759df --- /dev/null +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -0,0 +1,304 @@ +import { container, logger } from '@powersync/lib-services-framework'; +import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; +import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; +import * as mongo from 'mongodb'; +import { MongoManager } from './MongoManager.js'; +import { constructAfterRecord, getMongoLsn, getMongoRelation } from './MongoRelation.js'; + +export const ZERO_LSN = '00000000'; + +export interface WalStreamOptions { + connections: MongoManager; + storage: storage.SyncRulesBucketStorage; + abort_signal: AbortSignal; +} + +interface InitResult { + needsInitialSync: boolean; +} + +export class MissingReplicationSlotError extends Error { + constructor(message: string) { + super(message); + } +} + +export class ChangeStream { + sync_rules: SqlSyncRules; + group_id: number; + + connection_id = 1; + + private readonly storage: storage.SyncRulesBucketStorage; + + private connections: MongoManager; + private readonly client: mongo.MongoClient; + + private abort_signal: AbortSignal; + + private relation_cache = new Map(); + + constructor(options: WalStreamOptions) { + this.storage = options.storage; + this.sync_rules = options.storage.sync_rules; + this.group_id = options.storage.group_id; + this.connections = options.connections; + this.client = this.connections.client; + + this.abort_signal = options.abort_signal; + this.abort_signal.addEventListener( + 'abort', + () => { + // TODO: Fast abort? + }, + { once: true } + ); + } + + get stopped() { + return this.abort_signal.aborted; + } + + async getQualifiedTableNames( + batch: storage.BucketStorageBatch, + tablePattern: TablePattern + ): Promise { + const schema = tablePattern.schema; + if (tablePattern.connectionTag != this.connections.connectionTag) { + return []; + } + + const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; + if (tablePattern.isWildcard) { + // TODO: Implement + throw new Error('not supported yet'); + } + let result: storage.SourceTable[] = []; + + const name = tablePattern.name; + + const table = await this.handleRelation( + batch, + { + name, + schema, + objectId: name, + replicationColumns: [{ name: '_id' }] + } as SourceEntityDescriptor, + false + ); + + result.push(table); + return result; + } + + async initSlot(): Promise { + const status = await this.storage.getStatus(); + if (status.snapshot_done && status.checkpoint_lsn) { + logger.info(`Initial replication already done`); + return { needsInitialSync: false }; + } + + return { needsInitialSync: true }; + } + + async estimatedCount(table: storage.SourceTable): Promise { + const db = this.client.db(table.schema); + const count = db.collection(table.table).estimatedDocumentCount(); + return `~${count}`; + } + + /** + * Start initial replication. + * + * If (partial) replication was done before on this slot, this clears the state + * and starts again from scratch. + */ + async startInitialReplication() { + await this.storage.clear(); + await this.initialReplication(); + } + + async initialReplication() { + const sourceTables = this.sync_rules.getSourceTables(); + await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, tablePattern); + for (let table of tables) { + await this.snapshotTable(batch, table); + await batch.markSnapshotDone([table], ZERO_LSN); + + await touch(); + } + } + await batch.commit(ZERO_LSN); + }); + } + + static *getQueryData(results: Iterable): Generator { + for (let row of results) { + yield toSyncRulesRow(row); + } + } + + private async snapshotTable(batch: storage.BucketStorageBatch, table: storage.SourceTable) { + logger.info(`Replicating ${table.qualifiedName}`); + const estimatedCount = await this.estimatedCount(table); + let at = 0; + + const db = this.client.db(table.schema); + const collection = db.collection(table.table); + const query = collection.find({}, {}); + + const cursor = query.stream(); + + for await (let document of cursor) { + if (this.abort_signal.aborted) { + throw new Error(`Aborted initial replication`); + } + + const record = constructAfterRecord(document); + + // This auto-flushes when the batch reaches its size limit + await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); + + at += 1; + Metrics.getInstance().rows_replicated_total.add(1); + + await touch(); + } + + await batch.flush(); + } + + async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { + if (!descriptor.objectId && typeof descriptor.objectId != 'string') { + throw new Error('objectId expected'); + } + const result = await this.storage.resolveTable({ + group_id: this.group_id, + connection_id: this.connection_id, + connection_tag: this.connections.connectionTag, + entity_descriptor: descriptor, + sync_rules: this.sync_rules + }); + this.relation_cache.set(descriptor.objectId, result.table); + + // Drop conflicting tables. This includes for example renamed tables. + await batch.drop(result.dropTables); + + // Snapshot if: + // 1. Snapshot is requested (false for initial snapshot, since that process handles it elsewhere) + // 2. Snapshot is not already done, AND: + // 3. The table is used in sync rules. + const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; + + if (shouldSnapshot) { + // Truncate this table, in case a previous snapshot was interrupted. + await batch.truncate([result.table]); + + let lsn: string = ZERO_LSN; + // TODO: Transaction / consistency + await this.snapshotTable(batch, result.table); + const [table] = await batch.markSnapshotDone([result.table], lsn); + return table; + } + + return result.table; + } + + async writeChange( + batch: storage.BucketStorageBatch, + table: storage.SourceTable, + change: mongo.ChangeStreamDocument + ): Promise { + if (!table.syncAny) { + logger.debug(`Collection ${table.qualifiedName} not used in sync rules - skipping`); + return null; + } + + Metrics.getInstance().rows_replicated_total.add(1); + if (change.operationType == 'insert') { + const baseRecord = constructAfterRecord(change.fullDocument); + return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); + } else if (change.operationType == 'update') { + const before = undefined; // TODO: handle changing _id? + const after = constructAfterRecord(change.fullDocument!); + return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); + } else if (change.operationType == 'delete') { + const key = constructAfterRecord(change.documentKey); + console.log('delete', key); + return await batch.save({ tag: 'delete', sourceTable: table, before: key }); + } else { + throw new Error(`Unsupported operation: ${change.operationType}`); + } + } + + async replicate() { + try { + // If anything errors here, the entire replication process is halted, and + // all connections automatically closed, including this one. + + await this.initReplication(); + await this.streamChanges(); + } catch (e) { + await this.storage.reportError(e); + throw e; + } + } + + async initReplication() { + const result = await this.initSlot(); + if (result.needsInitialSync) { + await this.startInitialReplication(); + } + } + + async streamChanges() { + // Auto-activate as soon as initial replication is done + await this.storage.autoActivate(); + + await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + // TODO: Resume replication + const stream = this.client.watch(undefined, { + fullDocument: 'updateLookup' // FIXME: figure this one out + }); + this.abort_signal.addEventListener('abort', () => { + stream.close(); + }); + + for await (const changeDocument of stream) { + await touch(); + + if (this.abort_signal.aborted) { + break; + } + + if ( + changeDocument.operationType == 'insert' || + changeDocument.operationType == 'update' || + changeDocument.operationType == 'delete' + ) { + const rel = getMongoRelation(changeDocument.ns); + const table = await this.handleRelation(batch, rel, true); + // TODO: Support transactions + if (table.syncAny) { + await this.writeChange(batch, table, changeDocument); + + if (changeDocument.clusterTime != null) { + const lsn = getMongoLsn(changeDocument.clusterTime); + await batch.commit(lsn); + } + } + } + } + }); + } +} + +async function touch() { + // FIXME: The hosted Kubernetes probe does not actually check the timestamp on this. + // FIXME: We need a timeout of around 5+ minutes in Kubernetes if we do start checking the timestamp, + // or reduce PING_INTERVAL here. + return container.probes.touch(); +} diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts new file mode 100644 index 000000000..b3b993d7a --- /dev/null +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts @@ -0,0 +1,109 @@ +import { container } from '@powersync/lib-services-framework'; +import { MongoManager } from './MongoManager.js'; +import { MissingReplicationSlotError, ChangeStream } from './ChangeStream.js'; + +import { replication } from '@powersync/service-core'; +import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; + +export interface ChangeStreamReplicationJobOptions extends replication.AbstractReplicationJobOptions { + connectionFactory: ConnectionManagerFactory; +} + +export class ChangeStreamReplicationJob extends replication.AbstractReplicationJob { + private connectionFactory: ConnectionManagerFactory; + private readonly connectionManager: MongoManager; + + constructor(options: ChangeStreamReplicationJobOptions) { + super(options); + this.connectionFactory = options.connectionFactory; + this.connectionManager = this.connectionFactory.create(); + } + + async cleanUp(): Promise { + // TODO: Implement? + } + + async keepAlive() { + // TODO: Implement? + } + + async replicate() { + try { + await this.replicateLoop(); + } catch (e) { + // Fatal exception + container.reporter.captureException(e, { + metadata: {} + }); + this.logger.error(`Replication failed`, e); + } finally { + this.abortController.abort(); + } + } + + async replicateLoop() { + while (!this.isStopped) { + await this.replicateOnce(); + + if (!this.isStopped) { + await new Promise((resolve) => setTimeout(resolve, 5000)); + } + } + } + + async replicateOnce() { + // New connections on every iteration (every error with retry), + // otherwise we risk repeating errors related to the connection, + // such as caused by cached PG schemas. + const connectionManager = this.connectionFactory.create(); + try { + await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); + if (this.isStopped) { + return; + } + const stream = new ChangeStream({ + abort_signal: this.abortController.signal, + storage: this.options.storage, + connections: connectionManager + }); + await stream.replicate(); + } catch (e) { + this.logger.error(`Replication error`, e); + if (e.cause != null) { + // Example: + // PgError.conn_ended: Unable to do postgres query on ended connection + // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) + // at stream.next () + // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) + // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) + // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) + // ... + // cause: TypeError: match is not iterable + // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) + // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) + // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) + // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) + // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) + // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) + // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), + // [Symbol(pg.ErrorCode)]: 'conn_ended', + // [Symbol(pg.ErrorResponse)]: undefined + // } + // Without this additional log, the cause would not be visible in the logs. + this.logger.error(`cause`, e.cause); + } + if (e instanceof MissingReplicationSlotError) { + throw e; + } else { + // Report the error if relevant, before retrying + container.reporter.captureException(e, { + metadata: {} + }); + // This sets the retry delay + this.rateLimiter?.reportError(e); + } + } finally { + await connectionManager.end(); + } + } +} diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts new file mode 100644 index 000000000..d4c1314dd --- /dev/null +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts @@ -0,0 +1,34 @@ +import { storage, replication } from '@powersync/service-core'; +import { ChangeStreamReplicationJob } from './ChangeStreamReplicationJob.js'; +import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; + +export interface WalStreamReplicatorOptions extends replication.AbstractReplicatorOptions { + connectionFactory: ConnectionManagerFactory; +} + +export class ChangeStreamReplicator extends replication.AbstractReplicator { + private readonly connectionFactory: ConnectionManagerFactory; + + constructor(options: WalStreamReplicatorOptions) { + super(options); + this.connectionFactory = options.connectionFactory; + } + + createJob(options: replication.CreateJobOptions): ChangeStreamReplicationJob { + return new ChangeStreamReplicationJob({ + id: this.createJobId(options.storage.group_id), + storage: options.storage, + connectionFactory: this.connectionFactory, + lock: options.lock + }); + } + + async cleanUp(syncRulesStorage: storage.SyncRulesBucketStorage): Promise { + // TODO: Implement anything? + } + + async stop(): Promise { + await super.stop(); + await this.connectionFactory.shutdown(); + } +} diff --git a/modules/module-mongodb/src/replication/ConnectionManagerFactory.ts b/modules/module-mongodb/src/replication/ConnectionManagerFactory.ts new file mode 100644 index 000000000..c84c28e05 --- /dev/null +++ b/modules/module-mongodb/src/replication/ConnectionManagerFactory.ts @@ -0,0 +1,27 @@ +import { logger } from '@powersync/lib-services-framework'; +import { NormalizedMongoConnectionConfig } from '../types/types.js'; +import { MongoManager } from './MongoManager.js'; + +export class ConnectionManagerFactory { + private readonly connectionManagers: MongoManager[]; + private readonly dbConnectionConfig: NormalizedMongoConnectionConfig; + + constructor(dbConnectionConfig: NormalizedMongoConnectionConfig) { + this.dbConnectionConfig = dbConnectionConfig; + this.connectionManagers = []; + } + + create() { + const manager = new MongoManager(this.dbConnectionConfig); + this.connectionManagers.push(manager); + return manager; + } + + async shutdown() { + logger.info('Shutting down MongoDB connection Managers...'); + for (const manager of this.connectionManagers) { + await manager.end(); + } + logger.info('MongoDB connection Managers shutdown completed.'); + } +} diff --git a/modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts b/modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts new file mode 100644 index 000000000..bcf58db63 --- /dev/null +++ b/modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts @@ -0,0 +1,45 @@ +import { setTimeout } from 'timers/promises'; +import { ErrorRateLimiter } from '@powersync/service-core'; + +export class MongoErrorRateLimiter implements ErrorRateLimiter { + nextAllowed: number = Date.now(); + + async waitUntilAllowed(options?: { signal?: AbortSignal | undefined } | undefined): Promise { + const delay = Math.max(0, this.nextAllowed - Date.now()); + // Minimum delay between connections, even without errors + this.setDelay(500); + await setTimeout(delay, undefined, { signal: options?.signal }); + } + + mayPing(): boolean { + return Date.now() >= this.nextAllowed; + } + + reportError(e: any): void { + // FIXME: Check mongodb-specific requirements + const message = (e.message as string) ?? ''; + if (message.includes('password authentication failed')) { + // Wait 15 minutes, to avoid triggering Supabase's fail2ban + this.setDelay(900_000); + } else if (message.includes('ENOTFOUND')) { + // DNS lookup issue - incorrect URI or deleted instance + this.setDelay(120_000); + } else if (message.includes('ECONNREFUSED')) { + // Could be fail2ban or similar + this.setDelay(120_000); + } else if ( + message.includes('Unable to do postgres query on ended pool') || + message.includes('Postgres unexpectedly closed connection') + ) { + // Connection timed out - ignore / immediately retry + // We don't explicitly set the delay to 0, since there could have been another error that + // we need to respect. + } else { + this.setDelay(30_000); + } + } + + private setDelay(delay: number) { + this.nextAllowed = Math.max(this.nextAllowed, Date.now() + delay); + } +} diff --git a/modules/module-mongodb/src/replication/MongoManager.ts b/modules/module-mongodb/src/replication/MongoManager.ts new file mode 100644 index 000000000..cb2f9d54f --- /dev/null +++ b/modules/module-mongodb/src/replication/MongoManager.ts @@ -0,0 +1,47 @@ +import * as mongo from 'mongodb'; +import { NormalizedMongoConnectionConfig } from '../types/types.js'; + +export class MongoManager { + /** + * Do not use this for any transactions. + */ + public readonly client: mongo.MongoClient; + public readonly db: mongo.Db; + + constructor(public options: NormalizedMongoConnectionConfig) { + // The pool is lazy - no connections are opened until a query is performed. + this.client = new mongo.MongoClient(options.uri, { + auth: { + username: options.username, + password: options.password + }, + // Time for connection to timeout + connectTimeoutMS: 5_000, + // Time for individual requests to timeout + socketTimeoutMS: 60_000, + // How long to wait for new primary selection + serverSelectionTimeoutMS: 30_000, + + // Avoid too many connections: + // 1. It can overwhelm the source database. + // 2. Processing too many queries in parallel can cause the process to run out of memory. + maxPoolSize: 8, + + maxConnecting: 3, + maxIdleTimeMS: 60_000 + }); + this.db = this.client.db(options.database, {}); + } + + public get connectionTag() { + return this.options.tag; + } + + async end(): Promise { + await this.client.close(); + } + + async destroy() { + // TODO: Implement? + } +} diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts new file mode 100644 index 000000000..0573eda4a --- /dev/null +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -0,0 +1,104 @@ +import { storage } from '@powersync/service-core'; +import { SqliteRow, SqliteValue, toSyncRulesRow } from '@powersync/service-sync-rules'; +import * as mongo from 'mongodb'; +import { JSONBig, JsonContainer } from '@powersync/service-jsonbig'; + +export function getMongoRelation(source: mongo.ChangeStreamNameSpace): storage.SourceEntityDescriptor { + return { + name: source.coll, + schema: source.db, + objectId: source.coll, + replicationColumns: [{ name: '_id' }] + } satisfies storage.SourceEntityDescriptor; +} + +export function getMongoLsn(timestamp: mongo.Timestamp) { + const a = timestamp.high.toString(16).padStart(4, '0'); + const b = timestamp.low.toString(16).padStart(4, '0'); + return a + b; +} + +export function constructAfterRecord(document: mongo.Document): SqliteRow { + let record: SqliteRow = {}; + for (let key of Object.keys(document)) { + record[key] = toMongoSyncRulesValue(document[key]); + } + console.log('convert', document, record); + return record; +} + +export function toMongoSyncRulesValue(data: any): SqliteValue { + const autoBigNum = true; + if (data == null) { + // null or undefined + return data; + } else if (typeof data == 'string') { + return data; + } else if (typeof data == 'number') { + if (Number.isInteger(data) && autoBigNum) { + return BigInt(data); + } else { + return data; + } + } else if (typeof data == 'bigint') { + return data; + } else if (typeof data == 'boolean') { + return data ? 1n : 0n; + } else if (data instanceof mongo.ObjectId) { + return data.toHexString(); + } else if (data instanceof mongo.UUID) { + return data.toHexString(); + } else if (Array.isArray(data)) { + // We may be able to avoid some parse + stringify cycles here for JsonSqliteContainer. + return JSONBig.stringify(data.map((element) => filterJsonData(element))); + } else if (data instanceof Uint8Array) { + return data; + } else if (data instanceof JsonContainer) { + return data.toString(); + } else if (typeof data == 'object') { + let record: Record = {}; + for (let key of Object.keys(data)) { + record[key] = filterJsonData(data[key]); + } + return JSONBig.stringify(record); + } else { + return null; + } +} + +const DEPTH_LIMIT = 20; + +function filterJsonData(data: any, depth = 0): any { + if (depth > DEPTH_LIMIT) { + // This is primarily to prevent infinite recursion + throw new Error(`json nested object depth exceeds the limit of ${DEPTH_LIMIT}`); + } + if (data == null) { + return data; // null or undefined + } else if (typeof data == 'string' || typeof data == 'number') { + return data; + } else if (typeof data == 'boolean') { + return data ? 1n : 0n; + } else if (typeof data == 'bigint') { + return data; + } else if (data instanceof mongo.ObjectId) { + return data.toHexString(); + } else if (data instanceof mongo.UUID) { + return data.toHexString(); + } else if (Array.isArray(data)) { + return data.map((element) => filterJsonData(element, depth + 1)); + } else if (ArrayBuffer.isView(data)) { + return undefined; + } else if (data instanceof JsonContainer) { + // Can be stringified directly when using our JSONBig implementation + return data; + } else if (typeof data == 'object') { + let record: Record = {}; + for (let key of Object.keys(data)) { + record[key] = filterJsonData(data[key], depth + 1); + } + return record; + } else { + return undefined; + } +} diff --git a/modules/module-mongodb/src/replication/replication-index.ts b/modules/module-mongodb/src/replication/replication-index.ts new file mode 100644 index 000000000..4ff43b56a --- /dev/null +++ b/modules/module-mongodb/src/replication/replication-index.ts @@ -0,0 +1,4 @@ +export * from './MongoRelation.js'; +export * from './ChangeStream.js'; +export * from './ChangeStreamReplicator.js'; +export * from './ChangeStreamReplicationJob.js'; diff --git a/modules/module-mongodb/src/types/types.ts b/modules/module-mongodb/src/types/types.ts new file mode 100644 index 000000000..93e9fc846 --- /dev/null +++ b/modules/module-mongodb/src/types/types.ts @@ -0,0 +1,143 @@ +import * as service_types from '@powersync/service-types'; +import * as t from 'ts-codec'; +import * as urijs from 'uri-js'; + +export const MONGO_CONNECTION_TYPE = 'mongodb' as const; + +export interface NormalizedMongoConnectionConfig { + id: string; + tag: string; + + uri: string; + hostname?: string; + port?: number; + database?: string; + + username?: string; + password?: string; + + sslmode: 'verify-full' | 'verify-ca' | 'disable'; + cacert: string | undefined; + + client_certificate: string | undefined; + client_private_key: string | undefined; +} + +export const MongoConnectionConfig = service_types.configFile.dataSourceConfig.and( + t.object({ + type: t.literal(MONGO_CONNECTION_TYPE), + /** Unique identifier for the connection - optional when a single connection is present. */ + id: t.string.optional(), + /** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */ + tag: t.string.optional(), + uri: t.string.optional(), + hostname: t.string.optional(), + port: service_types.configFile.portCodec.optional(), + username: t.string.optional(), + password: t.string.optional(), + database: t.string.optional(), + + /** Defaults to verify-full */ + sslmode: t.literal('verify-full').or(t.literal('verify-ca')).or(t.literal('disable')).optional(), + /** Required for verify-ca, optional for verify-full */ + cacert: t.string.optional(), + + client_certificate: t.string.optional(), + client_private_key: t.string.optional() + }) +); + +/** + * Config input specified when starting services + */ +export type MongoConnectionConfig = t.Decoded; + +/** + * Resolved version of {@link MongoConnectionConfig} + */ +export type ResolvedConnectionConfig = MongoConnectionConfig & NormalizedMongoConnectionConfig; + +/** + * Validate and normalize connection options. + * + * Returns destructured options. + */ +export function normalizeConnectionConfig(options: MongoConnectionConfig): NormalizedMongoConnectionConfig { + let uri: urijs.URIComponents; + if (options.uri) { + uri = urijs.parse(options.uri); + if (uri.scheme != 'mongodb') { + `Invalid URI - protocol must be postgresql, got ${uri.scheme}`; + } + } else { + uri = urijs.parse('mongodb:///'); + } + + const hostname = options.hostname ?? uri.host ?? ''; + const port = validatePort(options.port ?? uri.port ?? 5432); + + const database = options.database ?? uri.path?.substring(1) ?? ''; + + const [uri_username, uri_password] = (uri.userinfo ?? '').split(':'); + + const username = options.username ?? uri_username; + const password = options.password ?? uri_password; + + const sslmode = options.sslmode ?? 'verify-full'; // Configuration not supported via URI + const cacert = options.cacert; + + if (sslmode == 'verify-ca' && cacert == null) { + throw new Error('Explicit cacert is required for sslmode=verify-ca'); + } + + if (hostname == '') { + throw new Error(`hostname required`); + } + + if (database == '') { + throw new Error(`database required`); + } + + return { + id: options.id ?? 'default', + tag: options.tag ?? 'default', + + uri: options.uri ?? '', + hostname, + port, + database, + + username, + password, + sslmode, + cacert, + + client_certificate: options.client_certificate ?? undefined, + client_private_key: options.client_private_key ?? undefined + }; +} + +/** + * Check whether the port is in a "safe" range. + * + * We do not support connecting to "privileged" ports. + */ +export function validatePort(port: string | number): number { + if (typeof port == 'string') { + port = parseInt(port); + } + if (port >= 1024 && port <= 49151) { + return port; + } else { + throw new Error(`Port ${port} not supported`); + } +} + +/** + * Construct a mongodb URI, without username, password or ssl options. + * + * Only contains hostname, port, database. + */ +export function baseUri(options: NormalizedMongoConnectionConfig) { + return `mongodb://${options.hostname}:${options.port}/${options.database}`; +} diff --git a/modules/module-mongodb/test/tsconfig.json b/modules/module-mongodb/test/tsconfig.json new file mode 100644 index 000000000..18898c4ee --- /dev/null +++ b/modules/module-mongodb/test/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "baseUrl": "./", + "noEmit": true, + "esModuleInterop": true, + "skipLibCheck": true, + "sourceMap": true, + "paths": { + "@/*": ["../../../packages/service-core/src/*"], + "@module/*": ["../src/*"], + "@core-tests/*": ["../../../packages/service-core/test/src/*"] + } + }, + "include": ["src"], + "references": [ + { + "path": "../" + }, + { + "path": "../../../packages/service-core/test" + }, + { + "path": "../../../packages/service-core/" + } + ] +} diff --git a/modules/module-mongodb/tsconfig.json b/modules/module-mongodb/tsconfig.json new file mode 100644 index 000000000..6afdde02f --- /dev/null +++ b/modules/module-mongodb/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "esModuleInterop": true, + "skipLibCheck": true, + "sourceMap": true + }, + "include": ["src"], + "references": [ + { + "path": "../../packages/types" + }, + { + "path": "../../packages/jsonbig" + }, + { + "path": "../../packages/sync-rules" + }, + { + "path": "../../packages/service-core" + }, + { + "path": "../../libs/lib-services" + } + ] +} diff --git a/modules/module-mongodb/vitest.config.ts b/modules/module-mongodb/vitest.config.ts new file mode 100644 index 000000000..b392696b7 --- /dev/null +++ b/modules/module-mongodb/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [tsconfigPaths()], + test: { + setupFiles: './test/src/setup.ts' + } +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd8bcf24b..320c292db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,6 +94,49 @@ importers: specifier: ^0.34.6 version: 0.34.6 + modules/module-mongodb: + dependencies: + '@powersync/lib-services-framework': + specifier: workspace:* + version: link:../../libs/lib-services + '@powersync/service-core': + specifier: workspace:* + version: link:../../packages/service-core + '@powersync/service-jsonbig': + specifier: workspace:* + version: link:../../packages/jsonbig + '@powersync/service-sync-rules': + specifier: workspace:* + version: link:../../packages/sync-rules + '@powersync/service-types': + specifier: workspace:* + version: link:../../packages/types + mongodb: + specifier: ^6.7.0 + version: 6.8.0(socks@2.8.3) + ts-codec: + specifier: ^1.2.2 + version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + devDependencies: + '@types/uuid': + specifier: ^9.0.4 + version: 9.0.8 + typescript: + specifier: ^5.2.2 + version: 5.2.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) + vitest: + specifier: ^0.34.6 + version: 0.34.6 + modules/module-postgres: dependencies: '@powersync/lib-services-framework': @@ -376,6 +419,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:* version: link:../packages/jsonbig + '@powersync/service-module-mongodb': + specifier: workspace:* + version: link:../modules/module-mongodb '@powersync/service-module-postgres': specifier: workspace:* version: link:../modules/module-postgres diff --git a/service/package.json b/service/package.json index e5eb849cf..2f8aee5d0 100644 --- a/service/package.json +++ b/service/package.json @@ -17,6 +17,7 @@ "@powersync/service-core": "workspace:*", "@powersync/lib-services-framework": "workspace:*", "@powersync/service-module-postgres": "workspace:*", + "@powersync/service-module-mongodb": "workspace:*", "@powersync/service-jpgwire": "workspace:*", "@powersync/service-jsonbig": "workspace:*", "@powersync/service-rsocket-router": "workspace:*", diff --git a/service/src/entry.ts b/service/src/entry.ts index d817f46fe..c92d19397 100644 --- a/service/src/entry.ts +++ b/service/src/entry.ts @@ -1,6 +1,7 @@ import { container, ContainerImplementation } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; import PostgresModule from '@powersync/service-module-postgres'; +import MongoModule from '@powersync/service-module-mongodb'; import { startServer } from './runners/server.js'; import { startStreamRunner } from './runners/stream-worker.js'; @@ -12,7 +13,7 @@ container.registerDefaults(); container.register(ContainerImplementation.REPORTER, createSentryReporter()); const moduleManager = new core.modules.ModuleManager(); -moduleManager.register([PostgresModule]); +moduleManager.register([PostgresModule, MongoModule]); // This is a bit of a hack. Commands such as the teardown command or even migrations might // want access to the ModuleManager in order to use modules container.register(core.ModuleManager, moduleManager); diff --git a/service/tsconfig.json b/service/tsconfig.json index 363c72bdb..40c9a5329 100644 --- a/service/tsconfig.json +++ b/service/tsconfig.json @@ -32,6 +32,9 @@ }, { "path": "../modules/module-postgres" + }, + { + "path": "../modules/module-mongodb" } ] } From 4ae6b23fef757c2c50b25174f07a2ed4b5b3ccf8 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 12 Sep 2024 18:33:33 +0200 Subject: [PATCH 167/298] Support resuming; multi-document operations. --- .../src/replication/ChangeStream.ts | 46 ++++++++++++++++--- .../src/replication/MongoRelation.ts | 13 +++++- modules/module-mongodb/src/types/types.ts | 36 ++------------- .../service-core/src/storage/BucketStorage.ts | 5 ++ .../src/storage/mongo/MongoBucketBatch.ts | 4 ++ 5 files changed, 64 insertions(+), 40 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 8ad5759df..4020190e8 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -3,7 +3,7 @@ import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-cor import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; import { MongoManager } from './MongoManager.js'; -import { constructAfterRecord, getMongoLsn, getMongoRelation } from './MongoRelation.js'; +import { constructAfterRecord, getMongoLsn, getMongoRelation, mongoLsnToTimestamp } from './MongoRelation.js'; export const ZERO_LSN = '00000000'; @@ -33,6 +33,7 @@ export class ChangeStream { private connections: MongoManager; private readonly client: mongo.MongoClient; + private readonly defaultDb: mongo.Db; private abort_signal: AbortSignal; @@ -44,6 +45,7 @@ export class ChangeStream { this.group_id = options.storage.group_id; this.connections = options.connections; this.client = this.connections.client; + this.defaultDb = this.connections.db; this.abort_signal = options.abort_signal; this.abort_signal.addEventListener( @@ -260,13 +262,24 @@ export class ChangeStream { await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { // TODO: Resume replication + const lastLsn = batch.lastCheckpointLsn; + const startAfter = mongoLsnToTimestamp(lastLsn) ?? undefined; + logger.info(`Resume streaming at ${startAfter?.inspect()} / ${lastLsn}`); + + // TODO: Use changeStreamSplitLargeEvent + const stream = this.client.watch(undefined, { + startAtOperationTime: startAfter, + showExpandedEvents: true, + useBigInt64: true, fullDocument: 'updateLookup' // FIXME: figure this one out }); this.abort_signal.addEventListener('abort', () => { stream.close(); }); + let lastEventTimestamp: mongo.Timestamp | null = null; + for await (const changeDocument of stream) { await touch(); @@ -274,6 +287,30 @@ export class ChangeStream { break; } + if (startAfter != null && changeDocument.clusterTime?.lte(startAfter)) { + continue; + } + + if ( + lastEventTimestamp != null && + changeDocument.clusterTime != null && + changeDocument.clusterTime.neq(lastEventTimestamp) + ) { + const lsn = getMongoLsn(lastEventTimestamp); + await batch.commit(lsn); + lastEventTimestamp = null; + } + + if ((changeDocument as any).ns?.db != this.defaultDb.databaseName) { + // HACK: Ignore events to other databases, but only _after_ + // the above commit. + + // TODO: filter out storage db on in the pipeline + // TODO: support non-default dbs + continue; + } + console.log('event', changeDocument); + if ( changeDocument.operationType == 'insert' || changeDocument.operationType == 'update' || @@ -281,13 +318,10 @@ export class ChangeStream { ) { const rel = getMongoRelation(changeDocument.ns); const table = await this.handleRelation(batch, rel, true); - // TODO: Support transactions if (table.syncAny) { await this.writeChange(batch, table, changeDocument); - - if (changeDocument.clusterTime != null) { - const lsn = getMongoLsn(changeDocument.clusterTime); - await batch.commit(lsn); + if (changeDocument.clusterTime) { + lastEventTimestamp = changeDocument.clusterTime; } } } diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index 0573eda4a..924cda2af 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -13,11 +13,20 @@ export function getMongoRelation(source: mongo.ChangeStreamNameSpace): storage.S } export function getMongoLsn(timestamp: mongo.Timestamp) { - const a = timestamp.high.toString(16).padStart(4, '0'); - const b = timestamp.low.toString(16).padStart(4, '0'); + const a = timestamp.high.toString(16).padStart(8, '0'); + const b = timestamp.low.toString(16).padStart(8, '0'); return a + b; } +export function mongoLsnToTimestamp(lsn: string | null) { + if (lsn == null) { + return null; + } + const a = parseInt(lsn.substring(0, 8), 16); + const b = parseInt(lsn.substring(8, 16), 16); + return mongo.Timestamp.fromBits(b, a); +} + export function constructAfterRecord(document: mongo.Document): SqliteRow { let record: SqliteRow = {}; for (let key of Object.keys(document)) { diff --git a/modules/module-mongodb/src/types/types.ts b/modules/module-mongodb/src/types/types.ts index 93e9fc846..4aeef79d3 100644 --- a/modules/module-mongodb/src/types/types.ts +++ b/modules/module-mongodb/src/types/types.ts @@ -9,18 +9,10 @@ export interface NormalizedMongoConnectionConfig { tag: string; uri: string; - hostname?: string; - port?: number; - database?: string; + database: string; username?: string; password?: string; - - sslmode: 'verify-full' | 'verify-ca' | 'disable'; - cacert: string | undefined; - - client_certificate: string | undefined; - client_private_key: string | undefined; } export const MongoConnectionConfig = service_types.configFile.dataSourceConfig.and( @@ -73,9 +65,6 @@ export function normalizeConnectionConfig(options: MongoConnectionConfig): Norma uri = urijs.parse('mongodb:///'); } - const hostname = options.hostname ?? uri.host ?? ''; - const port = validatePort(options.port ?? uri.port ?? 5432); - const database = options.database ?? uri.path?.substring(1) ?? ''; const [uri_username, uri_password] = (uri.userinfo ?? '').split(':'); @@ -83,17 +72,6 @@ export function normalizeConnectionConfig(options: MongoConnectionConfig): Norma const username = options.username ?? uri_username; const password = options.password ?? uri_password; - const sslmode = options.sslmode ?? 'verify-full'; // Configuration not supported via URI - const cacert = options.cacert; - - if (sslmode == 'verify-ca' && cacert == null) { - throw new Error('Explicit cacert is required for sslmode=verify-ca'); - } - - if (hostname == '') { - throw new Error(`hostname required`); - } - if (database == '') { throw new Error(`database required`); } @@ -102,18 +80,12 @@ export function normalizeConnectionConfig(options: MongoConnectionConfig): Norma id: options.id ?? 'default', tag: options.tag ?? 'default', + // TODO: remove username & password from uri uri: options.uri ?? '', - hostname, - port, database, username, - password, - sslmode, - cacert, - - client_certificate: options.client_certificate ?? undefined, - client_private_key: options.client_private_key ?? undefined + password }; } @@ -139,5 +111,5 @@ export function validatePort(port: string | number): number { * Only contains hostname, port, database. */ export function baseUri(options: NormalizedMongoConnectionConfig) { - return `mongodb://${options.hostname}:${options.port}/${options.database}`; + return options.uri; } diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 6f8ea0220..8615f1206 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -333,6 +333,11 @@ export interface BucketStorageBatch { */ keepalive(lsn: string): Promise; + /** + * Get the last checkpoint LSN, from either commit or keepalive. + */ + lastCheckpointLsn: string | null; + markSnapshotDone(tables: SourceTable[], no_checkpoint_before_lsn: string): Promise; } diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index 973f6a28a..c49cdf689 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -73,6 +73,10 @@ export class MongoBucketBatch implements BucketStorageBatch { this.no_checkpoint_before_lsn = no_checkpoint_before_lsn; } + get lastCheckpointLsn() { + return this.last_checkpoint_lsn; + } + async flush(): Promise { let result: FlushedResult | null = null; // One flush may be split over multiple transactions. From bed97ebe23b97cd59696a9aec0e44cc878f9c9a9 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Fri, 13 Sep 2024 13:20:42 +0200 Subject: [PATCH 168/298] Workaround for transaction boundaries; filter ns in pipeline. --- .../src/replication/ChangeStream.ts | 64 +++++++++++++++---- .../src/storage/mongo/MongoBucketBatch.ts | 2 +- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 4020190e8..e5ab7d771 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -70,7 +70,6 @@ export class ChangeStream { return []; } - const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; if (tablePattern.isWildcard) { // TODO: Implement throw new Error('not supported yet'); @@ -137,6 +136,28 @@ export class ChangeStream { }); } + private getSourceNamespaceFilters() { + const sourceTables = this.sync_rules.getSourceTables(); + + let filters: any[] = []; + for (let tablePattern of sourceTables) { + if (tablePattern.connectionTag != this.connections.connectionTag) { + continue; + } + + if (tablePattern.isWildcard) { + // TODO: Implement + throw new Error('wildcard collections not supported yet'); + } + + filters.push({ + db: tablePattern.schema, + coll: tablePattern.name + }); + } + return { $in: filters }; + } + static *getQueryData(results: Iterable): Generator { for (let row of results) { yield toSyncRulesRow(row); @@ -268,10 +289,22 @@ export class ChangeStream { // TODO: Use changeStreamSplitLargeEvent - const stream = this.client.watch(undefined, { + const nsFilter = this.getSourceNamespaceFilters(); + nsFilter.$in.push({ ns: nsFilter }); + + const pipeline: mongo.Document[] = [ + { + $match: { + ns: this.getSourceNamespaceFilters() + } + } + ]; + + const stream = this.client.watch(pipeline, { startAtOperationTime: startAfter, showExpandedEvents: true, useBigInt64: true, + maxAwaitTimeMS: 200, fullDocument: 'updateLookup' // FIXME: figure this one out }); this.abort_signal.addEventListener('abort', () => { @@ -280,7 +313,22 @@ export class ChangeStream { let lastEventTimestamp: mongo.Timestamp | null = null; - for await (const changeDocument of stream) { + while (true) { + const changeDocument = await stream.tryNext(); + if (changeDocument == null) { + // We don't get events for transaction commit. + // So if no events are available in the stream within maxAwaitTimeMS, + // we assume the transaction is complete. + // This is not foolproof - we may end up with a commit in the middle + // of a transaction. + if (lastEventTimestamp != null) { + const lsn = getMongoLsn(lastEventTimestamp); + await batch.commit(lsn); + lastEventTimestamp = null; + } + + continue; + } await touch(); if (this.abort_signal.aborted) { @@ -301,15 +349,7 @@ export class ChangeStream { lastEventTimestamp = null; } - if ((changeDocument as any).ns?.db != this.defaultDb.databaseName) { - // HACK: Ignore events to other databases, but only _after_ - // the above commit. - - // TODO: filter out storage db on in the pipeline - // TODO: support non-default dbs - continue; - } - console.log('event', changeDocument); + // console.log('event', changeDocument); if ( changeDocument.operationType == 'insert' || diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index c49cdf689..a184ac408 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -539,7 +539,7 @@ export class MongoBucketBatch implements BucketStorageBatch { async commit(lsn: string): Promise { await this.flush(); - if (this.last_checkpoint_lsn != null && lsn <= this.last_checkpoint_lsn) { + if (this.last_checkpoint_lsn != null && lsn < this.last_checkpoint_lsn) { // When re-applying transactions, don't create a new checkpoint until // we are past the last transaction. logger.info(`Re-applied transaction ${lsn} - skipping checkpoint`); From f68a6ec155e463a05833db4acdf4666170c625cb Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Fri, 13 Sep 2024 13:57:41 +0200 Subject: [PATCH 169/298] Proper initial snapshot + streaming afterwards. --- .../src/replication/ChangeStream.ts | 50 +++++++++++++------ .../replication/ChangeStreamReplicationJob.ts | 36 ++++++------- .../src/replication/MongoRelation.ts | 1 - 3 files changed, 49 insertions(+), 38 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index e5ab7d771..647909f14 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -5,7 +5,7 @@ import * as mongo from 'mongodb'; import { MongoManager } from './MongoManager.js'; import { constructAfterRecord, getMongoLsn, getMongoRelation, mongoLsnToTimestamp } from './MongoRelation.js'; -export const ZERO_LSN = '00000000'; +export const ZERO_LSN = '0000000000000000'; export interface WalStreamOptions { connections: MongoManager; @@ -72,7 +72,7 @@ export class ChangeStream { if (tablePattern.isWildcard) { // TODO: Implement - throw new Error('not supported yet'); + throw new Error('Wildcard collections not supported yet'); } let result: storage.SourceTable[] = []; @@ -122,18 +122,35 @@ export class ChangeStream { async initialReplication() { const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { - for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, table); - await batch.markSnapshotDone([table], ZERO_LSN); + await this.client.connect(); - await touch(); - } - } - await batch.commit(ZERO_LSN); + const session = await this.client.startSession({ + snapshot: true }); + try { + await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, tablePattern); + for (let table of tables) { + await this.snapshotTable(batch, table, session); + await batch.markSnapshotDone([table], ZERO_LSN); + + await touch(); + } + } + const time = session.clusterTime; + + if (time != null) { + const lsn = getMongoLsn(time.clusterTime); + logger.info(`Snapshot commit at ${time.clusterTime.inspect()} / ${lsn}`); + await batch.commit(lsn); + } else { + logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); + } + }); + } finally { + session.endSession(); + } } private getSourceNamespaceFilters() { @@ -164,14 +181,18 @@ export class ChangeStream { } } - private async snapshotTable(batch: storage.BucketStorageBatch, table: storage.SourceTable) { + private async snapshotTable( + batch: storage.BucketStorageBatch, + table: storage.SourceTable, + session?: mongo.ClientSession + ) { logger.info(`Replicating ${table.qualifiedName}`); const estimatedCount = await this.estimatedCount(table); let at = 0; const db = this.client.db(table.schema); const collection = db.collection(table.table); - const query = collection.find({}, {}); + const query = collection.find({}, { session }); const cursor = query.stream(); @@ -282,7 +303,6 @@ export class ChangeStream { await this.storage.autoActivate(); await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { - // TODO: Resume replication const lastLsn = batch.lastCheckpointLsn; const startAfter = mongoLsnToTimestamp(lastLsn) ?? undefined; logger.info(`Resume streaming at ${startAfter?.inspect()} / ${lastLsn}`); diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts index b3b993d7a..05b6c1e26 100644 --- a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts @@ -5,6 +5,8 @@ import { MissingReplicationSlotError, ChangeStream } from './ChangeStream.js'; import { replication } from '@powersync/service-core'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; +import * as mongo from 'mongodb'; + export interface ChangeStreamReplicationJobOptions extends replication.AbstractReplicationJobOptions { connectionFactory: ConnectionManagerFactory; } @@ -27,6 +29,10 @@ export class ChangeStreamReplicationJob extends replication.AbstractReplicationJ // TODO: Implement? } + private get slotName() { + return this.options.storage.slot_name; + } + async replicate() { try { await this.replicateLoop(); @@ -36,6 +42,11 @@ export class ChangeStreamReplicationJob extends replication.AbstractReplicationJ metadata: {} }); this.logger.error(`Replication failed`, e); + + if (e instanceof MissingReplicationSlotError) { + // This stops replication on this slot, and creates a new slot + await this.options.storage.factory.slotRemoved(this.slotName); + } } finally { this.abortController.abort(); } @@ -70,30 +81,11 @@ export class ChangeStreamReplicationJob extends replication.AbstractReplicationJ } catch (e) { this.logger.error(`Replication error`, e); if (e.cause != null) { - // Example: - // PgError.conn_ended: Unable to do postgres query on ended connection - // at PgConnection.stream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:315:13) - // at stream.next () - // at PgResult.fromStream (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:1174:22) - // at PgConnection.query (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:311:21) - // at WalStream.startInitialReplication (file:///.../powersync/powersync-service/lib/replication/WalStream.js:266:22) - // ... - // cause: TypeError: match is not iterable - // at timestamptzToSqlite (file:///.../powersync/packages/jpgwire/dist/util.js:140:50) - // at PgType.decode (file:///.../powersync/packages/jpgwire/dist/pgwire_types.js:25:24) - // at PgConnection._recvDataRow (file:///.../powersync/packages/jpgwire/dist/util.js:88:22) - // at PgConnection._recvMessages (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:656:30) - // at PgConnection._ioloopAttempt (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:563:20) - // at process.processTicksAndRejections (node:internal/process/task_queues:95:5) - // at async PgConnection._ioloop (file:///.../powersync/node_modules/.pnpm/github.com+kagis+pgwire@f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87/node_modules/pgwire/mod.js:517:14), - // [Symbol(pg.ErrorCode)]: 'conn_ended', - // [Symbol(pg.ErrorResponse)]: undefined - // } - // Without this additional log, the cause would not be visible in the logs. + // Without this additional log, the cause may not be visible in the logs. this.logger.error(`cause`, e.cause); } - if (e instanceof MissingReplicationSlotError) { - throw e; + if (e instanceof mongo.MongoError && e.hasErrorLabel('NonResumableChangeStreamError')) { + throw new MissingReplicationSlotError(e.message); } else { // Report the error if relevant, before retrying container.reporter.captureException(e, { diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index 924cda2af..d64e531f2 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -32,7 +32,6 @@ export function constructAfterRecord(document: mongo.Document): SqliteRow { for (let key of Object.keys(document)) { record[key] = toMongoSyncRulesValue(document[key]); } - console.log('convert', document, record); return record; } From 9d68d5c9d38e3733d8e44a09fec133498dbcecdc Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 12:20:14 +0200 Subject: [PATCH 170/298] Use _id directly as replica identity. --- .../src/replication/ChangeStream.ts | 39 +++++++++++++++---- .../src/replication/WalStream.ts | 38 +++++++++++++++--- .../service-core/src/storage/BucketStorage.ts | 11 +++++- .../src/storage/mongo/MongoBucketBatch.ts | 6 +-- .../src/storage/mongo/MongoCompactor.ts | 3 +- .../src/storage/mongo/OperationBatch.ts | 26 +++++++------ .../src/storage/mongo/PersistedBatch.ts | 5 ++- .../service-core/src/storage/mongo/models.ts | 5 ++- .../service-core/src/storage/mongo/util.ts | 32 ++++++++++++++- packages/service-core/src/util/utils.ts | 16 -------- 10 files changed, 129 insertions(+), 52 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 647909f14..16685186a 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -204,7 +204,14 @@ export class ChangeStream { const record = constructAfterRecord(document); // This auto-flushes when the batch reaches its size limit - await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); + await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: record, + afterReplicaId: document._id + }); at += 1; Metrics.getInstance().rows_replicated_total.add(1); @@ -264,15 +271,31 @@ export class ChangeStream { Metrics.getInstance().rows_replicated_total.add(1); if (change.operationType == 'insert') { const baseRecord = constructAfterRecord(change.fullDocument); - return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); + return await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: baseRecord, + afterReplicaId: change.documentKey._id + }); } else if (change.operationType == 'update') { - const before = undefined; // TODO: handle changing _id? - const after = constructAfterRecord(change.fullDocument!); - return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); + const after = constructAfterRecord(change.fullDocument ?? {}); + return await batch.save({ + tag: 'update', + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: after, + afterReplicaId: change.documentKey._id + }); } else if (change.operationType == 'delete') { - const key = constructAfterRecord(change.documentKey); - console.log('delete', key); - return await batch.save({ tag: 'delete', sourceTable: table, before: key }); + return await batch.save({ + tag: 'delete', + sourceTable: table, + before: undefined, + beforeReplicaId: change.documentKey._id + }); } else { throw new Error(`Unsupported operation: ${change.operationType}`); } diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 29ac6bfa5..c519a63d4 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -3,7 +3,7 @@ import * as util from '../utils/pgwire_utils.js'; import { container, errors, logger } from '@powersync/lib-services-framework'; import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; import { getPgOutputRelation, getRelId } from './PgRelation.js'; -import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; +import { getUuidReplicaIdentityBson, Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; import { checkSourceConfiguration, getReplicationIdentityColumns } from './replication-utils.js'; import { PgManager } from './PgManager.js'; @@ -389,7 +389,14 @@ WHERE oid = $1::regclass`, for (let record of WalStream.getQueryData(rows)) { // This auto-flushes when the batch reaches its size limit - await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); + await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: record, + afterReplicaId: getUuidReplicaIdentityBson(record, table.replicaIdColumns) + }); } at += rows.length; Metrics.getInstance().rows_replicated_total.add(rows.length); @@ -481,19 +488,40 @@ WHERE oid = $1::regclass`, if (msg.tag == 'insert') { Metrics.getInstance().rows_replicated_total.add(1); const baseRecord = util.constructAfterRecord(msg); - return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); + return await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: baseRecord, + afterReplicaId: getUuidReplicaIdentityBson(baseRecord, table.replicaIdColumns) + }); } else if (msg.tag == 'update') { Metrics.getInstance().rows_replicated_total.add(1); // "before" may be null if the replica id columns are unchanged // It's fine to treat that the same as an insert. const before = util.constructBeforeRecord(msg); const after = util.constructAfterRecord(msg); - return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); + return await batch.save({ + tag: 'update', + sourceTable: table, + before: before, + beforeReplicaId: before ? getUuidReplicaIdentityBson(before, table.replicaIdColumns) : undefined, + after: after, + afterReplicaId: getUuidReplicaIdentityBson(after, table.replicaIdColumns) + }); } else if (msg.tag == 'delete') { Metrics.getInstance().rows_replicated_total.add(1); const before = util.constructBeforeRecord(msg)!; - return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); + return await batch.save({ + tag: 'delete', + sourceTable: table, + before: before, + beforeReplicaId: getUuidReplicaIdentityBson(before, table.replicaIdColumns), + after: undefined, + afterReplicaId: undefined + }); } } else if (msg.tag == 'truncate') { let tables: storage.SourceTable[] = []; diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 8615f1206..2cf706528 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -10,6 +10,7 @@ import { import * as util from '../util/util-index.js'; import { SourceTable } from './SourceTable.js'; import { SourceEntityDescriptor } from './SourceEntity.js'; +import * as bson from 'bson'; export interface BucketStorageFactory { /** @@ -358,11 +359,15 @@ export interface SaveBucketData { export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete; +export type ReplicaId = string | bson.UUID | bson.Document; + export interface SaveInsert { tag: 'insert'; sourceTable: SourceTable; before?: undefined; + beforeReplicaId?: undefined; after: SqliteRow; + afterReplicaId: ReplicaId; } export interface SaveUpdate { @@ -373,6 +378,7 @@ export interface SaveUpdate { * This is only present when the id has changed, and will only contain replica identity columns. */ before?: SqliteRow; + beforeReplicaId?: ReplicaId; /** * A null value means null column. @@ -380,13 +386,16 @@ export interface SaveUpdate { * An undefined value means it's a TOAST value - must be copied from another record. */ after: ToastableSqliteRow; + afterReplicaId: ReplicaId; } export interface SaveDelete { tag: 'delete'; sourceTable: SourceTable; - before: SqliteRow; + before?: SqliteRow; + beforeReplicaId: ReplicaId; after?: undefined; + afterReplicaId?: undefined; } export interface SyncBucketDataBatch { diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index a184ac408..d82759029 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -11,7 +11,7 @@ import { CurrentBucket, CurrentDataDocument, SourceKey } from './models.js'; import { MongoIdSequence } from './MongoIdSequence.js'; import { cacheKey, OperationBatch, RecordOperation } from './OperationBatch.js'; import { PersistedBatch } from './PersistedBatch.js'; -import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, serializeLookup } from './util.js'; +import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, replicaIdEquals, serializeLookup } from './util.js'; /** * 15MB @@ -305,7 +305,7 @@ export class MongoBucketBatch implements BucketStorageBatch { } // 2. Save bucket data - if (beforeId != null && (afterId == null || !beforeId.equals(afterId))) { + if (beforeId != null && (afterId == null || !replicaIdEquals(beforeId, afterId))) { // Source ID updated if (sourceTable.syncData) { // Delete old record @@ -435,7 +435,7 @@ export class MongoBucketBatch implements BucketStorageBatch { }; } - if (afterId == null || !beforeId.equals(afterId)) { + if (afterId == null || !replicaIdEquals(beforeId, afterId)) { // Either a delete (afterId == null), or replaced the old replication id batch.deleteCurrentData(before_key); } diff --git a/packages/service-core/src/storage/mongo/MongoCompactor.ts b/packages/service-core/src/storage/mongo/MongoCompactor.ts index 3c52936ba..ff754973d 100644 --- a/packages/service-core/src/storage/mongo/MongoCompactor.ts +++ b/packages/service-core/src/storage/mongo/MongoCompactor.ts @@ -4,6 +4,7 @@ import { addChecksums } from '../../util/utils.js'; import { PowerSyncMongo } from './db.js'; import { BucketDataDocument, BucketDataKey } from './models.js'; import { CompactOptions } from '../BucketStorage.js'; +import { cacheKey } from './OperationBatch.js'; interface CurrentBucketState { /** Bucket name */ @@ -168,7 +169,7 @@ export class MongoCompactor { let isPersistentPut = doc.op == 'PUT'; if (doc.op == 'REMOVE' || doc.op == 'PUT') { - const key = `${doc.table}/${doc.row_id}/${doc.source_table}/${doc.source_key?.toHexString()}`; + const key = `${doc.table}/${doc.row_id}/${cacheKey(doc.source_table!, doc.source_key!)}`; const targetOp = currentState.seen.get(key); if (targetOp) { // Will convert to MOVE, so don't count as PUT diff --git a/packages/service-core/src/storage/mongo/OperationBatch.ts b/packages/service-core/src/storage/mongo/OperationBatch.ts index 9e1edbb6d..75babb5a6 100644 --- a/packages/service-core/src/storage/mongo/OperationBatch.ts +++ b/packages/service-core/src/storage/mongo/OperationBatch.ts @@ -1,8 +1,7 @@ -import * as bson from 'bson'; import { ToastableSqliteRow } from '@powersync/service-sync-rules'; +import * as bson from 'bson'; -import * as util from '../../util/util-index.js'; -import { SaveOptions } from '../BucketStorage.js'; +import { ReplicaId, SaveOptions } from '../BucketStorage.js'; /** * Maximum number of operations in a batch. @@ -63,18 +62,15 @@ export class OperationBatch { } export class RecordOperation { - public readonly afterId: bson.UUID | null; - public readonly beforeId: bson.UUID; + public readonly afterId: ReplicaId | null; + public readonly beforeId: ReplicaId; public readonly internalBeforeKey: string; public readonly internalAfterKey: string | null; public readonly estimatedSize: number; constructor(public readonly record: SaveOptions) { - const after = record.after; - const afterId = after ? util.getUuidReplicaIdentityBson(after, record.sourceTable.replicaIdColumns!) : null; - const beforeId = record.before - ? util.getUuidReplicaIdentityBson(record.before, record.sourceTable.replicaIdColumns!) - : afterId!; + const afterId = record.afterReplicaId ?? null; + const beforeId = record.beforeReplicaId ?? record.afterReplicaId; this.afterId = afterId; this.beforeId = beforeId; this.internalBeforeKey = cacheKey(record.sourceTable.id, beforeId); @@ -84,8 +80,14 @@ export class RecordOperation { } } -export function cacheKey(table: bson.ObjectId, id: bson.UUID) { - return `${table.toHexString()}.${id.toHexString()}`; +export function cacheKey(table: bson.ObjectId, id: ReplicaId) { + if (id instanceof bson.UUID) { + return `${table.toHexString()}.${id.toHexString()}`; + } else if (typeof id == 'string') { + return `${table.toHexString()}.${id}`; + } else { + return `${table.toHexString()}.${(bson.serialize(id) as Buffer).toString('base64')}`; + } } /** diff --git a/packages/service-core/src/storage/mongo/PersistedBatch.ts b/packages/service-core/src/storage/mongo/PersistedBatch.ts index 486c9d800..0e5d2fabc 100644 --- a/packages/service-core/src/storage/mongo/PersistedBatch.ts +++ b/packages/service-core/src/storage/mongo/PersistedBatch.ts @@ -17,6 +17,7 @@ import { } from './models.js'; import { serializeLookup } from './util.js'; import { logger } from '@powersync/lib-services-framework'; +import { ReplicaId } from '../BucketStorage.js'; /** * Maximum size of operations we write in a single transaction. @@ -59,7 +60,7 @@ export class PersistedBatch { saveBucketData(options: { op_seq: MongoIdSequence; - sourceKey: bson.UUID; + sourceKey: ReplicaId; table: SourceTable; evaluated: EvaluatedRow[]; before_buckets: CurrentBucket[]; @@ -134,7 +135,7 @@ export class PersistedBatch { saveParameterData(data: { op_seq: MongoIdSequence; - sourceKey: bson.UUID; + sourceKey: ReplicaId; sourceTable: SourceTable; evaluated: EvaluatedParameters[]; existing_lookups: bson.Binary[]; diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index ef26564bc..19dbf0fbc 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -1,5 +1,6 @@ import * as bson from 'bson'; import { SqliteJsonValue } from '@powersync/service-sync-rules'; +import { ReplicaId } from '../BucketStorage.js'; export interface SourceKey { /** group_id */ @@ -7,7 +8,7 @@ export interface SourceKey { /** source table id */ t: bson.ObjectId; /** source key */ - k: bson.UUID; + k: ReplicaId; } export interface BucketDataKey { @@ -43,7 +44,7 @@ export interface BucketDataDocument { _id: BucketDataKey; op: OpType; source_table?: bson.ObjectId; - source_key?: bson.UUID; + source_key?: ReplicaId; table?: string; row_id?: string; checksum: number; diff --git a/packages/service-core/src/storage/mongo/util.ts b/packages/service-core/src/storage/mongo/util.ts index fef4bc396..8dc5ac40b 100644 --- a/packages/service-core/src/storage/mongo/util.ts +++ b/packages/service-core/src/storage/mongo/util.ts @@ -3,8 +3,10 @@ import * as bson from 'bson'; import * as mongo from 'mongodb'; import * as crypto from 'crypto'; import { BucketDataDocument } from './models.js'; -import { timestampToOpId } from '../../util/utils.js'; +import { ID_NAMESPACE, timestampToOpId } from '../../util/utils.js'; import { OplogEntry } from '../../util/protocol-types.js'; +import { ReplicaId } from '../BucketStorage.js'; +import * as uuid from 'uuid'; /** * Lookup serialization must be number-agnostic. I.e. normalize numbers, instead of preserving numbers. @@ -98,7 +100,7 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { object_type: row.table, object_id: row.row_id, checksum: Number(row.checksum), - subkey: `${row.source_table}/${row.source_key!.toHexString()}`, + subkey: `${row.source_table}/${replicaIdToSubkey(row.source_key!)}`, data: row.data }; } else { @@ -111,3 +113,29 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { }; } } + +export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { + if (typeof a == 'string' && typeof b == 'string') { + return a == b; + } else if (a instanceof bson.UUID && b instanceof bson.UUID) { + return a.equals(b); + } else if (a == null && b == null) { + return true; + } else if (a != null || b != null) { + return false; + } else { + return (bson.serialize(a) as Buffer).equals(bson.serialize(b)); + } +} + +export function replicaIdToSubkey(id: ReplicaId) { + if (id instanceof bson.UUID) { + return id.toHexString(); + } else if (typeof id == 'string') { + return id; + } else { + const repr = bson.serialize(id); + return uuid.v5(repr, ID_NAMESPACE); + return; + } +} diff --git a/packages/service-core/src/util/utils.ts b/packages/service-core/src/util/utils.ts index 84cfa634e..9a40785cd 100644 --- a/packages/service-core/src/util/utils.ts +++ b/packages/service-core/src/util/utils.ts @@ -93,22 +93,6 @@ function getRawReplicaIdentity( return result; } -export function getUuidReplicaIdentityString( - tuple: sync_rules.ToastableSqliteRow, - columns: storage.ColumnDescriptor[] -): string { - const rawIdentity = getRawReplicaIdentity(tuple, columns); - - return uuidForRow(rawIdentity); -} - -export function uuidForRow(row: sync_rules.SqliteRow): string { - // Important: This must not change, since it will affect how ids are generated. - // Use BSON so that it's a well-defined format without encoding ambiguities. - const repr = bson.serialize(row); - return uuid.v5(repr, ID_NAMESPACE); -} - export function getUuidReplicaIdentityBson( tuple: sync_rules.ToastableSqliteRow, columns: storage.ColumnDescriptor[] From 5cf147453592f174f3aa40cc6e2886b53853fead Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 13:49:16 +0200 Subject: [PATCH 171/298] Configurable defaultSchema. --- .../src/api/MongoRouteAPIAdapter.ts | 13 +++++- .../src/replication/ChangeStream.ts | 43 +++++++++++-------- .../src/api/PostgresRouteAPIAdapter.ts | 8 +++- .../src/replication/WalStream.ts | 7 +-- .../test/src/wal_stream_utils.ts | 2 +- packages/service-core/src/api/RouteAPI.ts | 6 +++ packages/service-core/src/api/diagnostics.ts | 4 +- .../src/entry/commands/compact-action.ts | 2 +- .../src/replication/AbstractReplicator.ts | 5 +-- .../src/routes/endpoints/admin.ts | 12 +++--- .../src/routes/endpoints/socket-route.ts | 1 + .../src/routes/endpoints/sync-rules.ts | 16 +++++-- .../src/routes/endpoints/sync-stream.ts | 1 + packages/service-core/src/runner/teardown.ts | 2 +- .../service-core/src/storage/BucketStorage.ts | 19 +++++--- .../src/storage/MongoBucketStorage.ts | 32 +++++++------- .../service-core/src/storage/SourceTable.ts | 3 +- .../mongo/MongoPersistedSyncRulesContent.ts | 6 +-- .../storage/mongo/MongoSyncBucketStorage.ts | 14 +++++- .../src/storage/mongo/OperationBatch.ts | 8 +++- .../service-core/src/storage/mongo/util.ts | 19 ++++++-- packages/service-core/src/sync/sync.ts | 12 +++--- .../sync-rules/src/SqlBucketDescriptor.ts | 9 ++-- packages/sync-rules/src/SqlDataQuery.ts | 6 ++- packages/sync-rules/src/SqlParameterQuery.ts | 9 ++-- packages/sync-rules/src/SqlSyncRules.ts | 29 +++++++++---- packages/sync-rules/src/TablePattern.ts | 4 +- packages/sync-rules/src/types.ts | 3 +- 28 files changed, 189 insertions(+), 106 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 61aed14f1..3d862db5e 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -1,4 +1,4 @@ -import { api } from '@powersync/service-core'; +import { api, ParseSyncRulesOptions } from '@powersync/service-core'; import * as mongo from 'mongodb'; import * as sync_rules from '@powersync/service-sync-rules'; @@ -10,12 +10,21 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { protected client: mongo.MongoClient; connectionTag: string; + defaultSchema: string; constructor(protected config: types.ResolvedConnectionConfig) { - this.client = new MongoManager(config).client; + const manager = new MongoManager(config); + this.client = manager.client; + this.defaultSchema = manager.db.databaseName; this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG; } + getParseSyncRulesOptions(): ParseSyncRulesOptions { + return { + defaultSchema: this.defaultSchema + }; + } + async shutdown(): Promise { await this.client.close(); } diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 16685186a..150cb9970 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -41,11 +41,13 @@ export class ChangeStream { constructor(options: WalStreamOptions) { this.storage = options.storage; - this.sync_rules = options.storage.sync_rules; this.group_id = options.storage.group_id; this.connections = options.connections; this.client = this.connections.client; this.defaultDb = this.connections.db; + this.sync_rules = options.storage.getParsedSyncRules({ + defaultSchema: this.defaultDb.databaseName + }); this.abort_signal = options.abort_signal; this.abort_signal.addEventListener( @@ -128,26 +130,29 @@ export class ChangeStream { snapshot: true }); try { - await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { - for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, table, session); - await batch.markSnapshotDone([table], ZERO_LSN); - - await touch(); + await this.storage.startBatch( + { zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName }, + async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, tablePattern); + for (let table of tables) { + await this.snapshotTable(batch, table, session); + await batch.markSnapshotDone([table], ZERO_LSN); + + await touch(); + } } - } - const time = session.clusterTime; + const time = session.clusterTime; - if (time != null) { - const lsn = getMongoLsn(time.clusterTime); - logger.info(`Snapshot commit at ${time.clusterTime.inspect()} / ${lsn}`); - await batch.commit(lsn); - } else { - logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); + if (time != null) { + const lsn = getMongoLsn(time.clusterTime); + logger.info(`Snapshot commit at ${time.clusterTime.inspect()} / ${lsn}`); + await batch.commit(lsn); + } else { + logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); + } } - }); + ); } finally { session.endSession(); } @@ -325,7 +330,7 @@ export class ChangeStream { // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName }, async (batch) => { const lastLsn = batch.lastCheckpointLsn; const startAfter = mongoLsnToTimestamp(lastLsn) ?? undefined; logger.info(`Resume streaming at ${startAfter?.inspect()} / ${lastLsn}`); diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index cfd11b132..205a2c744 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -1,4 +1,4 @@ -import { api } from '@powersync/service-core'; +import { api, ParseSyncRulesOptions } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import * as sync_rules from '@powersync/service-sync-rules'; @@ -23,6 +23,12 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG; } + getParseSyncRulesOptions(): ParseSyncRulesOptions { + return { + defaultSchema: 'public' + }; + } + async shutdown(): Promise { await this.pool.end(); } diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index c519a63d4..69cffc80e 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -9,6 +9,7 @@ import { PgManager } from './PgManager.js'; export const ZERO_LSN = '00000000/00000000'; export const PUBLICATION_NAME = 'powersync'; +export const POSTGRES_DEFAULT_SCHEMA = 'public'; export interface WalStreamOptions { connections: PgManager; @@ -46,7 +47,7 @@ export class WalStream { constructor(options: WalStreamOptions) { this.storage = options.storage; - this.sync_rules = options.storage.sync_rules; + this.sync_rules = options.storage.getParsedSyncRules({ defaultSchema: POSTGRES_DEFAULT_SCHEMA }); this.group_id = options.storage.group_id; this.slot_name = options.storage.slot_name; this.connections = options.connections; @@ -333,7 +334,7 @@ WHERE oid = $1::regclass`, async initialReplication(db: pgwire.PgConnection, lsn: string) { const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA }, async (batch) => { for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, db, tablePattern); for (let table of tables) { @@ -569,7 +570,7 @@ WHERE oid = $1::regclass`, // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA }, async (batch) => { // Replication never starts in the middle of a transaction let inTx = false; let count = 0; diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index 0de968e8e..d9e4f2609 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -58,7 +58,7 @@ export class WalStreamTestContext { async updateSyncRules(content: string) { const syncRules = await this.factory.updateSyncRules({ content: content }); - this.storage = this.factory.getInstance(syncRules.parsed()); + this.storage = this.factory.getInstance(syncRules); return this.storage!; } diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index b5ad5ccab..393d56342 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -1,5 +1,6 @@ import { SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; +import { ParseSyncRulesOptions } from '../storage/BucketStorage.js'; export interface PatternResult { schema: string; @@ -69,4 +70,9 @@ export interface RouteAPI { * Close any resources that need graceful termination. */ shutdown(): Promise; + + /** + * Get the default schema (or database) when only a table name is specified in sync rules. + */ + getParseSyncRulesOptions(): ParseSyncRulesOptions; } diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 7fd4ef440..7b6da99ce 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -43,7 +43,7 @@ export async function getSyncRulesStatus( let rules: SqlSyncRules; let persisted: storage.PersistedSyncRules; try { - persisted = sync_rules.parsed(); + persisted = sync_rules.parsed(apiHandler.getParseSyncRulesOptions()); rules = persisted.sync_rules; } catch (e) { return { @@ -53,7 +53,7 @@ export async function getSyncRulesStatus( }; } - const systemStorage = live_status ? bucketStorage.getInstance(persisted) : undefined; + const systemStorage = live_status ? bucketStorage.getInstance(sync_rules) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index 6016644e5..5a6636bf1 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -34,7 +34,7 @@ export function registerCompactAction(program: Command) { await client.connect(); try { const bucketStorage = new storage.MongoBucketStorage(psdb, { slot_name_prefix: configuration.slot_name_prefix }); - const active = await bucketStorage.getActiveSyncRules(); + const active = await bucketStorage.getActiveSyncRulesContent(); if (active == null) { logger.info('No active instance to compact'); return; diff --git a/packages/service-core/src/replication/AbstractReplicator.ts b/packages/service-core/src/replication/AbstractReplicator.ts index bcf480fc9..6c5a2a934 100644 --- a/packages/service-core/src/replication/AbstractReplicator.ts +++ b/packages/service-core/src/replication/AbstractReplicator.ts @@ -166,8 +166,7 @@ export abstract class AbstractReplicator try { for await (const data of sync.streamResponse({ storage: activeBucketStorage, + parseOptions: routerEngine!.getAPI().getParseSyncRulesOptions(), params: { ...params, binary_data: true // always true for web sockets diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 605d3c82d..1129a5855 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -53,7 +53,12 @@ export const deploySyncRules = routeDefinition({ const content = payload.params.content; try { - SqlSyncRules.fromYaml(payload.params.content); + const apiHandler = service_context.routerEngine!.getAPI(); + SqlSyncRules.fromYaml(payload.params.content, { + ...apiHandler.getParseSyncRulesOptions(), + // We don't do any schema-level validation at this point + schema: undefined + }); } catch (e) { throw new errors.JourneyError({ status: 422, @@ -151,7 +156,8 @@ export const reprocessSyncRules = routeDefinition({ const { storageEngine: { activeBucketStorage } } = payload.context.service_context; - const sync_rules = await activeBucketStorage.getActiveSyncRules(); + const apiHandler = payload.context.service_context.routerEngine!.getAPI(); + const sync_rules = await activeBucketStorage.getActiveSyncRules(apiHandler.getParseSyncRulesOptions()); if (sync_rules == null) { throw new errors.JourneyError({ status: 422, @@ -181,7 +187,11 @@ function replyPrettyJson(payload: any) { async function debugSyncRules(apiHandler: RouteAPI, sync_rules: string) { try { - const rules = SqlSyncRules.fromYaml(sync_rules); + const rules = SqlSyncRules.fromYaml(sync_rules, { + ...apiHandler.getParseSyncRulesOptions(), + // No schema-based validation at this point + schema: undefined + }); const source_table_patterns = rules.getSourceTables(); const resolved_tables = await apiHandler.getDebugTablesInfo(source_table_patterns, rules); diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index e637e99ea..83d8f3994 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -54,6 +54,7 @@ export const syncStreamed = routeDefinition({ sync.ndjson( sync.streamResponse({ storage: storageEngine.activeBucketStorage, + parseOptions: routerEngine!.getAPI().getParseSyncRulesOptions(), params, syncParams, token: payload.context.token_payload!, diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index 49e2bc66d..583635642 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -51,7 +51,7 @@ async function terminateSyncRules(storageFactory: storage.BucketStorageFactory, // Mark the sync rules as terminated for (let syncRules of combinedSyncRules) { - const syncRulesStorage = storageFactory.getInstance(syncRules.parsed()); + const syncRulesStorage = storageFactory.getInstance(syncRules); // The storage will be dropped at the end of the teardown, so we don't need to clear it here await syncRulesStorage.terminate({ clearStorage: false }); } diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 2cf706528..15aaf61d5 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -24,7 +24,7 @@ export interface BucketStorageFactory { /** * Get a storage instance to query sync data for specific sync rules. */ - getInstance(options: PersistedSyncRules): SyncRulesBucketStorage; + getInstance(options: PersistedSyncRulesContent): SyncRulesBucketStorage; /** * Deploy new sync rules. @@ -48,7 +48,7 @@ export interface BucketStorageFactory { /** * Get the sync rules used for querying. */ - getActiveSyncRules(): Promise; + getActiveSyncRules(options: ParseSyncRulesOptions): Promise; /** * Get the sync rules used for querying. @@ -58,7 +58,7 @@ export interface BucketStorageFactory { /** * Get the sync rules that will be active next once done with initial replicatino. */ - getNextSyncRules(): Promise; + getNextSyncRules(options: ParseSyncRulesOptions): Promise; /** * Get the sync rules that will be active next once done with initial replicatino. @@ -131,6 +131,10 @@ export interface StorageMetrics { replication_size_bytes: number; } +export interface ParseSyncRulesOptions { + defaultSchema: string; +} + export interface PersistedSyncRulesContent { readonly id: number; readonly sync_rules_content: string; @@ -140,7 +144,7 @@ export interface PersistedSyncRulesContent { readonly last_keepalive_ts?: Date | null; readonly last_checkpoint_ts?: Date | null; - parsed(): PersistedSyncRules; + parsed(options: ParseSyncRulesOptions): PersistedSyncRules; lock(): Promise; } @@ -186,12 +190,11 @@ export interface BucketDataBatchOptions { chunkLimitBytes?: number; } -export interface StartBatchOptions { +export interface StartBatchOptions extends ParseSyncRulesOptions { zeroLSN: string; } export interface SyncRulesBucketStorage { - readonly sync_rules: SqlSyncRules; readonly group_id: number; readonly slot_name: string; @@ -206,6 +209,8 @@ export interface SyncRulesBucketStorage { getCheckpoint(): Promise<{ checkpoint: util.OpId }>; + getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules; + getParameterSets(checkpoint: util.OpId, lookups: SqliteJsonValue[][]): Promise; /** @@ -359,7 +364,7 @@ export interface SaveBucketData { export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete; -export type ReplicaId = string | bson.UUID | bson.Document; +export type ReplicaId = string | bson.UUID | bson.Document | any; export interface SaveInsert { tag: 'insert'; diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index 2cf45c436..c3e59d0b7 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -13,6 +13,7 @@ import { v4 as uuid } from 'uuid'; import { ActiveCheckpoint, BucketStorageFactory, + ParseSyncRulesOptions, PersistedSyncRules, PersistedSyncRulesContent, StorageMetrics, @@ -47,7 +48,7 @@ export class MongoBucketStorage implements BucketStorageFactory { return undefined; } const rules = new MongoPersistedSyncRulesContent(this.db, doc2); - return this.getInstance(rules.parsed()); + return this.getInstance(rules); } }); @@ -60,12 +61,12 @@ export class MongoBucketStorage implements BucketStorageFactory { this.slot_name_prefix = options.slot_name_prefix; } - getInstance(options: PersistedSyncRules): MongoSyncBucketStorage { - let { id, sync_rules, slot_name } = options; + getInstance(options: PersistedSyncRulesContent): MongoSyncBucketStorage { + let { id, slot_name } = options; if ((typeof id as any) == 'bigint') { id = Number(id); } - return new MongoSyncBucketStorage(this, id, sync_rules, slot_name); + return new MongoSyncBucketStorage(this, id, options, slot_name); } async configureSyncRules(sync_rules: string, options?: { lock?: boolean }) { @@ -135,7 +136,12 @@ export class MongoBucketStorage implements BucketStorageFactory { async updateSyncRules(options: UpdateSyncRulesOptions): Promise { // Parse and validate before applying any changes - const parsed = SqlSyncRules.fromYaml(options.content); + const parsed = SqlSyncRules.fromYaml(options.content, { + // No schema-based validation at this point + schema: undefined, + defaultSchema: 'not_applicable', // Not needed for validation + throwOnError: true + }); let rules: MongoPersistedSyncRulesContent | undefined = undefined; @@ -203,9 +209,9 @@ export class MongoBucketStorage implements BucketStorageFactory { return new MongoPersistedSyncRulesContent(this.db, doc); } - async getActiveSyncRules(): Promise { + async getActiveSyncRules(options: ParseSyncRulesOptions): Promise { const content = await this.getActiveSyncRulesContent(); - return content?.parsed() ?? null; + return content?.parsed(options) ?? null; } async getNextSyncRulesContent(): Promise { @@ -222,9 +228,9 @@ export class MongoBucketStorage implements BucketStorageFactory { return new MongoPersistedSyncRulesContent(this.db, doc); } - async getNextSyncRules(): Promise { + async getNextSyncRules(options: ParseSyncRulesOptions): Promise { const content = await this.getNextSyncRulesContent(); - return content?.parsed() ?? null; + return content?.parsed(options) ?? null; } async getReplicatingSyncRules(): Promise { @@ -293,14 +299,6 @@ export class MongoBucketStorage implements BucketStorageFactory { } async getStorageMetrics(): Promise { - const active_sync_rules = await this.getActiveSyncRules(); - if (active_sync_rules == null) { - return { - operations_size_bytes: 0, - parameters_size_bytes: 0, - replication_size_bytes: 0 - }; - } const operations_aggregate = await this.db.bucket_data .aggregate([ diff --git a/packages/service-core/src/storage/SourceTable.ts b/packages/service-core/src/storage/SourceTable.ts index 6379732f5..2f5364633 100644 --- a/packages/service-core/src/storage/SourceTable.ts +++ b/packages/service-core/src/storage/SourceTable.ts @@ -1,9 +1,8 @@ -import { DEFAULT_SCHEMA, DEFAULT_TAG } from '@powersync/service-sync-rules'; +import { DEFAULT_TAG } from '@powersync/service-sync-rules'; import * as util from '../util/util-index.js'; import { ColumnDescriptor } from './SourceEntity.js'; export class SourceTable { - static readonly DEFAULT_SCHEMA = DEFAULT_SCHEMA; static readonly DEFAULT_TAG = DEFAULT_TAG; /** diff --git a/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts b/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts index a32cf6fc1..363766df4 100644 --- a/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts +++ b/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts @@ -1,7 +1,7 @@ import { SqlSyncRules } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; -import { PersistedSyncRulesContent } from '../BucketStorage.js'; +import { ParseSyncRulesOptions, PersistedSyncRulesContent } from '../BucketStorage.js'; import { MongoPersistedSyncRules } from './MongoPersistedSyncRules.js'; import { MongoSyncRulesLock } from './MongoSyncRulesLock.js'; import { PowerSyncMongo } from './db.js'; @@ -30,10 +30,10 @@ export class MongoPersistedSyncRulesContent implements PersistedSyncRulesContent this.last_keepalive_ts = doc.last_keepalive_ts; } - parsed() { + parsed(options: ParseSyncRulesOptions) { return new MongoPersistedSyncRules( this.id, - SqlSyncRules.fromYaml(this.sync_rules_content), + SqlSyncRules.fromYaml(this.sync_rules_content, options), this.last_checkpoint_lsn, this.slot_name ); diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 95715d45c..771f5093d 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -11,6 +11,9 @@ import { DEFAULT_DOCUMENT_BATCH_LIMIT, DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES, FlushedResult, + ParseSyncRulesOptions, + PersistedSyncRules, + PersistedSyncRulesContent, ResolveTableOptions, ResolveTableResult, StartBatchOptions, @@ -36,15 +39,22 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { } }); + private parsedSyncRulesCache: SqlSyncRules | undefined; + constructor( public readonly factory: MongoBucketStorage, public readonly group_id: number, - public readonly sync_rules: SqlSyncRules, + private readonly sync_rules: PersistedSyncRulesContent, public readonly slot_name: string ) { this.db = factory.db; } + getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules { + this.parsedSyncRulesCache ??= this.sync_rules.parsed(options).sync_rules; + return this.parsedSyncRulesCache; + } + async getCheckpoint() { const doc = await this.db.sync_rules.findOne( { _id: this.group_id }, @@ -71,7 +81,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { const batch = new MongoBucketBatch( this.db, - this.sync_rules, + this.sync_rules.parsed(options).sync_rules, this.group_id, this.slot_name, checkpoint_lsn, diff --git a/packages/service-core/src/storage/mongo/OperationBatch.ts b/packages/service-core/src/storage/mongo/OperationBatch.ts index 75babb5a6..74b4b651f 100644 --- a/packages/service-core/src/storage/mongo/OperationBatch.ts +++ b/packages/service-core/src/storage/mongo/OperationBatch.ts @@ -2,6 +2,7 @@ import { ToastableSqliteRow } from '@powersync/service-sync-rules'; import * as bson from 'bson'; import { ReplicaId, SaveOptions } from '../BucketStorage.js'; +import { isUUID } from './util.js'; /** * Maximum number of operations in a batch. @@ -80,13 +81,16 @@ export class RecordOperation { } } +/** + * In-memory cache key - must not be persisted. + */ export function cacheKey(table: bson.ObjectId, id: ReplicaId) { - if (id instanceof bson.UUID) { + if (isUUID(id)) { return `${table.toHexString()}.${id.toHexString()}`; } else if (typeof id == 'string') { return `${table.toHexString()}.${id}`; } else { - return `${table.toHexString()}.${(bson.serialize(id) as Buffer).toString('base64')}`; + return `${table.toHexString()}.${(bson.serialize({ id: id }) as Buffer).toString('base64')}`; } } diff --git a/packages/service-core/src/storage/mongo/util.ts b/packages/service-core/src/storage/mongo/util.ts index 8dc5ac40b..58a9bc36c 100644 --- a/packages/service-core/src/storage/mongo/util.ts +++ b/packages/service-core/src/storage/mongo/util.ts @@ -115,9 +115,11 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { } export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { - if (typeof a == 'string' && typeof b == 'string') { + if (a === b) { + return true; + } else if (typeof a == 'string' && typeof b == 'string') { return a == b; - } else if (a instanceof bson.UUID && b instanceof bson.UUID) { + } else if (isUUID(a) && isUUID(b)) { return a.equals(b); } else if (a == null && b == null) { return true; @@ -129,13 +131,22 @@ export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { } export function replicaIdToSubkey(id: ReplicaId) { - if (id instanceof bson.UUID) { + if (isUUID(id)) { + // Special case for UUID for backwards-compatiblity return id.toHexString(); } else if (typeof id == 'string') { return id; } else { - const repr = bson.serialize(id); + const repr = bson.serialize({ id: id }); return uuid.v5(repr, ID_NAMESPACE); return; } } + +export function isUUID(value: any): value is bson.UUID { + if (value == null || typeof value != 'object') { + return false; + } + const uuid = value as bson.UUID; + return uuid._bsontype == 'Binary' && uuid.sub_type == bson.Binary.SUBTYPE_UUID; +} diff --git a/packages/service-core/src/sync/sync.ts b/packages/service-core/src/sync/sync.ts index e439888f8..7a71540e6 100644 --- a/packages/service-core/src/sync/sync.ts +++ b/packages/service-core/src/sync/sync.ts @@ -1,5 +1,5 @@ import { JSONBig, JsonContainer } from '@powersync/service-jsonbig'; -import { RequestParameters } from '@powersync/service-sync-rules'; +import { RequestParameters, SqlSyncRules } from '@powersync/service-sync-rules'; import { Semaphore } from 'async-mutex'; import { AbortError } from 'ix/aborterror.js'; @@ -23,6 +23,7 @@ export interface SyncStreamParameters { params: util.StreamingSyncRequest; syncParams: RequestParameters; token: auth.JwtPayload; + parseOptions: storage.ParseSyncRulesOptions; /** * If this signal is aborted, the stream response ends as soon as possible, without error. */ @@ -35,7 +36,7 @@ export interface SyncStreamParameters { export async function* streamResponse( options: SyncStreamParameters ): AsyncIterable { - const { storage, params, syncParams, token, tokenStreamOptions, tracker, signal } = options; + const { storage, params, syncParams, token, tokenStreamOptions, tracker, signal, parseOptions } = options; // We also need to be able to abort, so we create our own controller. const controller = new AbortController(); if (signal) { @@ -51,7 +52,7 @@ export async function* streamResponse( } } const ki = tokenStream(token, controller.signal, tokenStreamOptions); - const stream = streamResponseInner(storage, params, syncParams, tracker, controller.signal); + const stream = streamResponseInner(storage, params, syncParams, tracker, parseOptions, controller.signal); // Merge the two streams, and abort as soon as one of the streams end. const merged = mergeAsyncIterables([stream, ki], controller.signal); @@ -75,6 +76,7 @@ async function* streamResponseInner( params: util.StreamingSyncRequest, syncParams: RequestParameters, tracker: RequestTracker, + parseOptions: storage.ParseSyncRulesOptions, signal: AbortSignal ): AsyncGenerator { // Bucket state of bucket id -> op_id. @@ -103,9 +105,9 @@ async function* streamResponseInner( // Sync rules deleted in the meantime - try again with the next checkpoint. continue; } - const sync_rules = storage.sync_rules; + const syncRules = storage.getParsedSyncRules(parseOptions); - const allBuckets = await sync_rules.queryBucketIds({ + const allBuckets = await syncRules.queryBucketIds({ getParameterSets(lookups) { return storage.getParameterSets(checkpoint, lookups); }, diff --git a/packages/sync-rules/src/SqlBucketDescriptor.ts b/packages/sync-rules/src/SqlBucketDescriptor.ts index 459178ee1..a140a1fc2 100644 --- a/packages/sync-rules/src/SqlBucketDescriptor.ts +++ b/packages/sync-rules/src/SqlBucketDescriptor.ts @@ -2,6 +2,7 @@ import { IdSequence } from './IdSequence.js'; import { SourceTableInterface } from './SourceTableInterface.js'; import { SqlDataQuery } from './SqlDataQuery.js'; import { SqlParameterQuery } from './SqlParameterQuery.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; import { StaticSqlParameterQuery } from './StaticSqlParameterQuery.js'; import { TablePattern } from './TablePattern.js'; import { SqlRuleError } from './errors.js'; @@ -42,11 +43,11 @@ export class SqlBucketDescriptor { parameterIdSequence = new IdSequence(); - addDataQuery(sql: string, schema?: SourceSchema): QueryParseResult { + addDataQuery(sql: string, options: SyncRulesOptions): QueryParseResult { if (this.bucket_parameters == null) { throw new Error('Bucket parameters must be defined'); } - const dataRows = SqlDataQuery.fromSql(this.name, this.bucket_parameters, sql, schema); + const dataRows = SqlDataQuery.fromSql(this.name, this.bucket_parameters, sql, options); dataRows.ruleId = this.idSequence.nextId(); @@ -58,8 +59,8 @@ export class SqlBucketDescriptor { }; } - addParameterQuery(sql: string, schema: SourceSchema | undefined, options: QueryParseOptions): QueryParseResult { - const parameterQuery = SqlParameterQuery.fromSql(this.name, sql, schema, options); + addParameterQuery(sql: string, options: QueryParseOptions): QueryParseResult { + const parameterQuery = SqlParameterQuery.fromSql(this.name, sql, options); if (this.bucket_parameters == null) { this.bucket_parameters = parameterQuery.bucket_parameters; } else { diff --git a/packages/sync-rules/src/SqlDataQuery.ts b/packages/sync-rules/src/SqlDataQuery.ts index c1176bc9b..b5377d436 100644 --- a/packages/sync-rules/src/SqlDataQuery.ts +++ b/packages/sync-rules/src/SqlDataQuery.ts @@ -19,6 +19,7 @@ import { } from './types.js'; import { filterJsonRow, getBucketId, isSelectStatement } from './utils.js'; import { TableQuerySchema } from './TableQuerySchema.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; interface RowValueExtractor { extract(tables: QueryParameters, into: SqliteRow): void; @@ -26,9 +27,10 @@ interface RowValueExtractor { } export class SqlDataQuery { - static fromSql(descriptor_name: string, bucket_parameters: string[], sql: string, schema?: SourceSchema) { + static fromSql(descriptor_name: string, bucket_parameters: string[], sql: string, options: SyncRulesOptions) { const parsed = parse(sql, { locationTracking: true }); const rows = new SqlDataQuery(); + const schema = options.schema; if (parsed.length > 1) { throw new SqlRuleError('Only a single SELECT statement is supported', sql, parsed[1]?._location); @@ -50,7 +52,7 @@ export class SqlDataQuery { } const alias: string = tableRef.alias ?? tableRef.name; - const sourceTable = new TablePattern(tableRef.schema, tableRef.name); + const sourceTable = new TablePattern(tableRef.schema ?? options.defaultSchema, tableRef.name); let querySchema: QuerySchema | undefined = undefined; if (schema) { const tables = schema.getTables(sourceTable); diff --git a/packages/sync-rules/src/SqlParameterQuery.ts b/packages/sync-rules/src/SqlParameterQuery.ts index f13352177..2b5a4d14a 100644 --- a/packages/sync-rules/src/SqlParameterQuery.ts +++ b/packages/sync-rules/src/SqlParameterQuery.ts @@ -23,6 +23,7 @@ import { SqliteRow } from './types.js'; import { filterJsonRow, getBucketId, isJsonValue, isSelectStatement } from './utils.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; /** * Represents a parameter query, such as: @@ -34,11 +35,11 @@ export class SqlParameterQuery { static fromSql( descriptor_name: string, sql: string, - schema?: SourceSchema, - options?: QueryParseOptions + options: QueryParseOptions ): SqlParameterQuery | StaticSqlParameterQuery { const parsed = parse(sql, { locationTracking: true }); const rows = new SqlParameterQuery(); + const schema = options?.schema; if (parsed.length > 1) { throw new SqlRuleError('Only a single SELECT statement is supported', sql, parsed[1]?._location); @@ -70,7 +71,7 @@ export class SqlParameterQuery { new SqlRuleError('Table aliases not supported in parameter queries', sql, q.from?.[0]._location) ); } - const sourceTable = new TablePattern(tableRef.schema, tableRef.name); + const sourceTable = new TablePattern(tableRef.schema ?? options.defaultSchema, tableRef.name); let querySchema: QuerySchema | undefined = undefined; if (schema) { const tables = schema.getTables(sourceTable); @@ -139,7 +140,7 @@ export class SqlParameterQuery { rows.tools = tools; rows.errors.push(...tools.errors); - if (rows.usesDangerousRequestParameters && !options?.accept_potentially_dangerous_queries) { + if (rows.usesDangerousRequestParameters && !options.accept_potentially_dangerous_queries) { let err = new SqlRuleError( "Potentially dangerous query based on parameters set by the client. The client can send any value for these parameters so it's not a good place to do authorization.", sql diff --git a/packages/sync-rules/src/SqlSyncRules.ts b/packages/sync-rules/src/SqlSyncRules.ts index 15b84adc6..d93292b49 100644 --- a/packages/sync-rules/src/SqlSyncRules.ts +++ b/packages/sync-rules/src/SqlSyncRules.ts @@ -25,6 +25,16 @@ import { const ACCEPT_POTENTIALLY_DANGEROUS_QUERIES = Symbol('ACCEPT_POTENTIALLY_DANGEROUS_QUERIES'); +export interface SyncRulesOptions { + schema?: SourceSchema; + /** + * 'public' for Postgres, default database for MongoDB/MySQL. + */ + defaultSchema: string; + + throwOnError?: boolean; +} + export class SqlSyncRules implements SyncRules { bucket_descriptors: SqlBucketDescriptor[] = []; idSequence = new IdSequence(); @@ -33,7 +43,7 @@ export class SqlSyncRules implements SyncRules { errors: YamlError[] = []; - static validate(yaml: string, options?: { schema?: SourceSchema }): YamlError[] { + static validate(yaml: string, options: SyncRulesOptions): YamlError[] { try { const rules = this.fromYaml(yaml, options); return rules.errors; @@ -48,9 +58,9 @@ export class SqlSyncRules implements SyncRules { } } - static fromYaml(yaml: string, options?: { throwOnError?: boolean; schema?: SourceSchema }) { - const throwOnError = options?.throwOnError ?? true; - const schema = options?.schema; + static fromYaml(yaml: string, options: SyncRulesOptions) { + const throwOnError = options.throwOnError ?? true; + const schema = options.schema; const lineCounter = new LineCounter(); const parsed = parseDocument(yaml, { @@ -98,7 +108,8 @@ export class SqlSyncRules implements SyncRules { const accept_potentially_dangerous_queries = value.get('accept_potentially_dangerous_queries', true)?.value == true; - const options: QueryParseOptions = { + const queryOptions: QueryParseOptions = { + ...options, accept_potentially_dangerous_queries }; const parameters = value.get('parameters', true) as unknown; @@ -108,16 +119,16 @@ export class SqlSyncRules implements SyncRules { if (parameters instanceof Scalar) { rules.withScalar(parameters, (q) => { - return descriptor.addParameterQuery(q, schema, options); + return descriptor.addParameterQuery(q, queryOptions); }); } else if (parameters instanceof YAMLSeq) { for (let item of parameters.items) { rules.withScalar(item, (q) => { - return descriptor.addParameterQuery(q, schema, options); + return descriptor.addParameterQuery(q, queryOptions); }); } } else { - descriptor.addParameterQuery('SELECT', schema, options); + descriptor.addParameterQuery('SELECT', queryOptions); } if (!(dataQueries instanceof YAMLSeq)) { @@ -126,7 +137,7 @@ export class SqlSyncRules implements SyncRules { } for (let query of dataQueries.items) { rules.withScalar(query, (q) => { - return descriptor.addDataQuery(q, schema); + return descriptor.addDataQuery(q, queryOptions); }); } rules.bucket_descriptors.push(descriptor); diff --git a/packages/sync-rules/src/TablePattern.ts b/packages/sync-rules/src/TablePattern.ts index d6d3494ba..55c90ec9e 100644 --- a/packages/sync-rules/src/TablePattern.ts +++ b/packages/sync-rules/src/TablePattern.ts @@ -1,7 +1,6 @@ import { SourceTableInterface } from './SourceTableInterface.js'; export const DEFAULT_TAG = 'default'; -export const DEFAULT_SCHEMA = 'public'; /** * Some pattern matching SourceTables. @@ -12,8 +11,7 @@ export class TablePattern { public readonly schema: string; public readonly tablePattern: string; - constructor(schema: string | undefined, tablePattern: string) { - schema ??= DEFAULT_SCHEMA; + constructor(schema: string, tablePattern: string) { const splitSchema = schema.split('.'); if (splitSchema.length > 2) { throw new Error(`Invalid schema: ${schema}`); diff --git a/packages/sync-rules/src/types.ts b/packages/sync-rules/src/types.ts index e54d508df..e27489435 100644 --- a/packages/sync-rules/src/types.ts +++ b/packages/sync-rules/src/types.ts @@ -3,6 +3,7 @@ import { SourceTableInterface } from './SourceTableInterface.js'; import { ColumnDefinition, ExpressionType } from './ExpressionType.js'; import { TablePattern } from './TablePattern.js'; import { toSyncRulesParameters } from './utils.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; export interface SyncRules { evaluateRow(options: EvaluateRowOptions): EvaluationResult[]; @@ -10,7 +11,7 @@ export interface SyncRules { evaluateParameterRow(table: SourceTableInterface, row: SqliteRow): EvaluatedParametersResult[]; } -export interface QueryParseOptions { +export interface QueryParseOptions extends SyncRulesOptions { accept_potentially_dangerous_queries?: boolean; } From e3576d6f9d06b573a01e6ad068979cefd8296894 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 12:20:14 +0200 Subject: [PATCH 172/298] Use _id directly as replica identity. --- .../src/replication/WalStream.ts | 38 ++++++++++++++++--- .../service-core/src/storage/BucketStorage.ts | 11 +++++- .../src/storage/mongo/MongoBucketBatch.ts | 6 +-- .../src/storage/mongo/MongoCompactor.ts | 3 +- .../src/storage/mongo/OperationBatch.ts | 26 +++++++------ .../src/storage/mongo/PersistedBatch.ts | 5 ++- .../service-core/src/storage/mongo/models.ts | 5 ++- .../service-core/src/storage/mongo/util.ts | 32 +++++++++++++++- packages/service-core/src/util/utils.ts | 16 -------- 9 files changed, 98 insertions(+), 44 deletions(-) diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 29ac6bfa5..c519a63d4 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -3,7 +3,7 @@ import * as util from '../utils/pgwire_utils.js'; import { container, errors, logger } from '@powersync/lib-services-framework'; import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; import { getPgOutputRelation, getRelId } from './PgRelation.js'; -import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; +import { getUuidReplicaIdentityBson, Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; import { checkSourceConfiguration, getReplicationIdentityColumns } from './replication-utils.js'; import { PgManager } from './PgManager.js'; @@ -389,7 +389,14 @@ WHERE oid = $1::regclass`, for (let record of WalStream.getQueryData(rows)) { // This auto-flushes when the batch reaches its size limit - await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record }); + await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: record, + afterReplicaId: getUuidReplicaIdentityBson(record, table.replicaIdColumns) + }); } at += rows.length; Metrics.getInstance().rows_replicated_total.add(rows.length); @@ -481,19 +488,40 @@ WHERE oid = $1::regclass`, if (msg.tag == 'insert') { Metrics.getInstance().rows_replicated_total.add(1); const baseRecord = util.constructAfterRecord(msg); - return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord }); + return await batch.save({ + tag: 'insert', + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: baseRecord, + afterReplicaId: getUuidReplicaIdentityBson(baseRecord, table.replicaIdColumns) + }); } else if (msg.tag == 'update') { Metrics.getInstance().rows_replicated_total.add(1); // "before" may be null if the replica id columns are unchanged // It's fine to treat that the same as an insert. const before = util.constructBeforeRecord(msg); const after = util.constructAfterRecord(msg); - return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after }); + return await batch.save({ + tag: 'update', + sourceTable: table, + before: before, + beforeReplicaId: before ? getUuidReplicaIdentityBson(before, table.replicaIdColumns) : undefined, + after: after, + afterReplicaId: getUuidReplicaIdentityBson(after, table.replicaIdColumns) + }); } else if (msg.tag == 'delete') { Metrics.getInstance().rows_replicated_total.add(1); const before = util.constructBeforeRecord(msg)!; - return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined }); + return await batch.save({ + tag: 'delete', + sourceTable: table, + before: before, + beforeReplicaId: getUuidReplicaIdentityBson(before, table.replicaIdColumns), + after: undefined, + afterReplicaId: undefined + }); } } else if (msg.tag == 'truncate') { let tables: storage.SourceTable[] = []; diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 6f8ea0220..5441b2503 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -10,6 +10,7 @@ import { import * as util from '../util/util-index.js'; import { SourceTable } from './SourceTable.js'; import { SourceEntityDescriptor } from './SourceEntity.js'; +import * as bson from 'bson'; export interface BucketStorageFactory { /** @@ -353,11 +354,15 @@ export interface SaveBucketData { export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete; +export type ReplicaId = string | bson.UUID | bson.Document; + export interface SaveInsert { tag: 'insert'; sourceTable: SourceTable; before?: undefined; + beforeReplicaId?: undefined; after: SqliteRow; + afterReplicaId: ReplicaId; } export interface SaveUpdate { @@ -368,6 +373,7 @@ export interface SaveUpdate { * This is only present when the id has changed, and will only contain replica identity columns. */ before?: SqliteRow; + beforeReplicaId?: ReplicaId; /** * A null value means null column. @@ -375,13 +381,16 @@ export interface SaveUpdate { * An undefined value means it's a TOAST value - must be copied from another record. */ after: ToastableSqliteRow; + afterReplicaId: ReplicaId; } export interface SaveDelete { tag: 'delete'; sourceTable: SourceTable; - before: SqliteRow; + before?: SqliteRow; + beforeReplicaId: ReplicaId; after?: undefined; + afterReplicaId?: undefined; } export interface SyncBucketDataBatch { diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index 973f6a28a..81c03e3e5 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -11,7 +11,7 @@ import { CurrentBucket, CurrentDataDocument, SourceKey } from './models.js'; import { MongoIdSequence } from './MongoIdSequence.js'; import { cacheKey, OperationBatch, RecordOperation } from './OperationBatch.js'; import { PersistedBatch } from './PersistedBatch.js'; -import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, serializeLookup } from './util.js'; +import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, replicaIdEquals, serializeLookup } from './util.js'; /** * 15MB @@ -301,7 +301,7 @@ export class MongoBucketBatch implements BucketStorageBatch { } // 2. Save bucket data - if (beforeId != null && (afterId == null || !beforeId.equals(afterId))) { + if (beforeId != null && (afterId == null || !replicaIdEquals(beforeId, afterId))) { // Source ID updated if (sourceTable.syncData) { // Delete old record @@ -431,7 +431,7 @@ export class MongoBucketBatch implements BucketStorageBatch { }; } - if (afterId == null || !beforeId.equals(afterId)) { + if (afterId == null || !replicaIdEquals(beforeId, afterId)) { // Either a delete (afterId == null), or replaced the old replication id batch.deleteCurrentData(before_key); } diff --git a/packages/service-core/src/storage/mongo/MongoCompactor.ts b/packages/service-core/src/storage/mongo/MongoCompactor.ts index 3c52936ba..ff754973d 100644 --- a/packages/service-core/src/storage/mongo/MongoCompactor.ts +++ b/packages/service-core/src/storage/mongo/MongoCompactor.ts @@ -4,6 +4,7 @@ import { addChecksums } from '../../util/utils.js'; import { PowerSyncMongo } from './db.js'; import { BucketDataDocument, BucketDataKey } from './models.js'; import { CompactOptions } from '../BucketStorage.js'; +import { cacheKey } from './OperationBatch.js'; interface CurrentBucketState { /** Bucket name */ @@ -168,7 +169,7 @@ export class MongoCompactor { let isPersistentPut = doc.op == 'PUT'; if (doc.op == 'REMOVE' || doc.op == 'PUT') { - const key = `${doc.table}/${doc.row_id}/${doc.source_table}/${doc.source_key?.toHexString()}`; + const key = `${doc.table}/${doc.row_id}/${cacheKey(doc.source_table!, doc.source_key!)}`; const targetOp = currentState.seen.get(key); if (targetOp) { // Will convert to MOVE, so don't count as PUT diff --git a/packages/service-core/src/storage/mongo/OperationBatch.ts b/packages/service-core/src/storage/mongo/OperationBatch.ts index 9e1edbb6d..75babb5a6 100644 --- a/packages/service-core/src/storage/mongo/OperationBatch.ts +++ b/packages/service-core/src/storage/mongo/OperationBatch.ts @@ -1,8 +1,7 @@ -import * as bson from 'bson'; import { ToastableSqliteRow } from '@powersync/service-sync-rules'; +import * as bson from 'bson'; -import * as util from '../../util/util-index.js'; -import { SaveOptions } from '../BucketStorage.js'; +import { ReplicaId, SaveOptions } from '../BucketStorage.js'; /** * Maximum number of operations in a batch. @@ -63,18 +62,15 @@ export class OperationBatch { } export class RecordOperation { - public readonly afterId: bson.UUID | null; - public readonly beforeId: bson.UUID; + public readonly afterId: ReplicaId | null; + public readonly beforeId: ReplicaId; public readonly internalBeforeKey: string; public readonly internalAfterKey: string | null; public readonly estimatedSize: number; constructor(public readonly record: SaveOptions) { - const after = record.after; - const afterId = after ? util.getUuidReplicaIdentityBson(after, record.sourceTable.replicaIdColumns!) : null; - const beforeId = record.before - ? util.getUuidReplicaIdentityBson(record.before, record.sourceTable.replicaIdColumns!) - : afterId!; + const afterId = record.afterReplicaId ?? null; + const beforeId = record.beforeReplicaId ?? record.afterReplicaId; this.afterId = afterId; this.beforeId = beforeId; this.internalBeforeKey = cacheKey(record.sourceTable.id, beforeId); @@ -84,8 +80,14 @@ export class RecordOperation { } } -export function cacheKey(table: bson.ObjectId, id: bson.UUID) { - return `${table.toHexString()}.${id.toHexString()}`; +export function cacheKey(table: bson.ObjectId, id: ReplicaId) { + if (id instanceof bson.UUID) { + return `${table.toHexString()}.${id.toHexString()}`; + } else if (typeof id == 'string') { + return `${table.toHexString()}.${id}`; + } else { + return `${table.toHexString()}.${(bson.serialize(id) as Buffer).toString('base64')}`; + } } /** diff --git a/packages/service-core/src/storage/mongo/PersistedBatch.ts b/packages/service-core/src/storage/mongo/PersistedBatch.ts index 486c9d800..0e5d2fabc 100644 --- a/packages/service-core/src/storage/mongo/PersistedBatch.ts +++ b/packages/service-core/src/storage/mongo/PersistedBatch.ts @@ -17,6 +17,7 @@ import { } from './models.js'; import { serializeLookup } from './util.js'; import { logger } from '@powersync/lib-services-framework'; +import { ReplicaId } from '../BucketStorage.js'; /** * Maximum size of operations we write in a single transaction. @@ -59,7 +60,7 @@ export class PersistedBatch { saveBucketData(options: { op_seq: MongoIdSequence; - sourceKey: bson.UUID; + sourceKey: ReplicaId; table: SourceTable; evaluated: EvaluatedRow[]; before_buckets: CurrentBucket[]; @@ -134,7 +135,7 @@ export class PersistedBatch { saveParameterData(data: { op_seq: MongoIdSequence; - sourceKey: bson.UUID; + sourceKey: ReplicaId; sourceTable: SourceTable; evaluated: EvaluatedParameters[]; existing_lookups: bson.Binary[]; diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index ef26564bc..19dbf0fbc 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -1,5 +1,6 @@ import * as bson from 'bson'; import { SqliteJsonValue } from '@powersync/service-sync-rules'; +import { ReplicaId } from '../BucketStorage.js'; export interface SourceKey { /** group_id */ @@ -7,7 +8,7 @@ export interface SourceKey { /** source table id */ t: bson.ObjectId; /** source key */ - k: bson.UUID; + k: ReplicaId; } export interface BucketDataKey { @@ -43,7 +44,7 @@ export interface BucketDataDocument { _id: BucketDataKey; op: OpType; source_table?: bson.ObjectId; - source_key?: bson.UUID; + source_key?: ReplicaId; table?: string; row_id?: string; checksum: number; diff --git a/packages/service-core/src/storage/mongo/util.ts b/packages/service-core/src/storage/mongo/util.ts index fef4bc396..8dc5ac40b 100644 --- a/packages/service-core/src/storage/mongo/util.ts +++ b/packages/service-core/src/storage/mongo/util.ts @@ -3,8 +3,10 @@ import * as bson from 'bson'; import * as mongo from 'mongodb'; import * as crypto from 'crypto'; import { BucketDataDocument } from './models.js'; -import { timestampToOpId } from '../../util/utils.js'; +import { ID_NAMESPACE, timestampToOpId } from '../../util/utils.js'; import { OplogEntry } from '../../util/protocol-types.js'; +import { ReplicaId } from '../BucketStorage.js'; +import * as uuid from 'uuid'; /** * Lookup serialization must be number-agnostic. I.e. normalize numbers, instead of preserving numbers. @@ -98,7 +100,7 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { object_type: row.table, object_id: row.row_id, checksum: Number(row.checksum), - subkey: `${row.source_table}/${row.source_key!.toHexString()}`, + subkey: `${row.source_table}/${replicaIdToSubkey(row.source_key!)}`, data: row.data }; } else { @@ -111,3 +113,29 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { }; } } + +export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { + if (typeof a == 'string' && typeof b == 'string') { + return a == b; + } else if (a instanceof bson.UUID && b instanceof bson.UUID) { + return a.equals(b); + } else if (a == null && b == null) { + return true; + } else if (a != null || b != null) { + return false; + } else { + return (bson.serialize(a) as Buffer).equals(bson.serialize(b)); + } +} + +export function replicaIdToSubkey(id: ReplicaId) { + if (id instanceof bson.UUID) { + return id.toHexString(); + } else if (typeof id == 'string') { + return id; + } else { + const repr = bson.serialize(id); + return uuid.v5(repr, ID_NAMESPACE); + return; + } +} diff --git a/packages/service-core/src/util/utils.ts b/packages/service-core/src/util/utils.ts index 84cfa634e..9a40785cd 100644 --- a/packages/service-core/src/util/utils.ts +++ b/packages/service-core/src/util/utils.ts @@ -93,22 +93,6 @@ function getRawReplicaIdentity( return result; } -export function getUuidReplicaIdentityString( - tuple: sync_rules.ToastableSqliteRow, - columns: storage.ColumnDescriptor[] -): string { - const rawIdentity = getRawReplicaIdentity(tuple, columns); - - return uuidForRow(rawIdentity); -} - -export function uuidForRow(row: sync_rules.SqliteRow): string { - // Important: This must not change, since it will affect how ids are generated. - // Use BSON so that it's a well-defined format without encoding ambiguities. - const repr = bson.serialize(row); - return uuid.v5(repr, ID_NAMESPACE); -} - export function getUuidReplicaIdentityBson( tuple: sync_rules.ToastableSqliteRow, columns: storage.ColumnDescriptor[] From 72ded17f989117d04c549b8af4697a3a9d1fcd74 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 13:49:16 +0200 Subject: [PATCH 173/298] Configurable defaultSchema. --- .../src/api/PostgresRouteAPIAdapter.ts | 8 ++++- .../src/replication/WalStream.ts | 7 ++-- .../test/src/wal_stream_utils.ts | 2 +- packages/service-core/src/api/RouteAPI.ts | 6 ++++ packages/service-core/src/api/diagnostics.ts | 4 +-- .../src/entry/commands/compact-action.ts | 2 +- .../src/replication/AbstractReplicator.ts | 5 ++- .../src/routes/endpoints/admin.ts | 12 ++++--- .../src/routes/endpoints/socket-route.ts | 3 +- .../src/routes/endpoints/sync-rules.ts | 16 ++++++++-- .../src/routes/endpoints/sync-stream.ts | 1 + packages/service-core/src/runner/teardown.ts | 2 +- .../service-core/src/storage/BucketStorage.ts | 19 +++++++---- .../src/storage/MongoBucketStorage.ts | 32 +++++++++---------- .../service-core/src/storage/SourceTable.ts | 3 +- .../mongo/MongoPersistedSyncRulesContent.ts | 6 ++-- .../storage/mongo/MongoSyncBucketStorage.ts | 14 ++++++-- .../src/storage/mongo/OperationBatch.ts | 8 +++-- .../service-core/src/storage/mongo/util.ts | 19 ++++++++--- packages/service-core/src/sync/sync.ts | 11 ++++--- .../sync-rules/src/SqlBucketDescriptor.ts | 9 +++--- packages/sync-rules/src/SqlDataQuery.ts | 6 ++-- packages/sync-rules/src/SqlParameterQuery.ts | 9 +++--- packages/sync-rules/src/SqlSyncRules.ts | 29 +++++++++++------ packages/sync-rules/src/TablePattern.ts | 4 +-- packages/sync-rules/src/types.ts | 3 +- 26 files changed, 155 insertions(+), 85 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index cfd11b132..205a2c744 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -1,4 +1,4 @@ -import { api } from '@powersync/service-core'; +import { api, ParseSyncRulesOptions } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; import * as sync_rules from '@powersync/service-sync-rules'; @@ -23,6 +23,12 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG; } + getParseSyncRulesOptions(): ParseSyncRulesOptions { + return { + defaultSchema: 'public' + }; + } + async shutdown(): Promise { await this.pool.end(); } diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index c519a63d4..69cffc80e 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -9,6 +9,7 @@ import { PgManager } from './PgManager.js'; export const ZERO_LSN = '00000000/00000000'; export const PUBLICATION_NAME = 'powersync'; +export const POSTGRES_DEFAULT_SCHEMA = 'public'; export interface WalStreamOptions { connections: PgManager; @@ -46,7 +47,7 @@ export class WalStream { constructor(options: WalStreamOptions) { this.storage = options.storage; - this.sync_rules = options.storage.sync_rules; + this.sync_rules = options.storage.getParsedSyncRules({ defaultSchema: POSTGRES_DEFAULT_SCHEMA }); this.group_id = options.storage.group_id; this.slot_name = options.storage.slot_name; this.connections = options.connections; @@ -333,7 +334,7 @@ WHERE oid = $1::regclass`, async initialReplication(db: pgwire.PgConnection, lsn: string) { const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA }, async (batch) => { for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, db, tablePattern); for (let table of tables) { @@ -569,7 +570,7 @@ WHERE oid = $1::regclass`, // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA }, async (batch) => { // Replication never starts in the middle of a transaction let inTx = false; let count = 0; diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index 0de968e8e..d9e4f2609 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -58,7 +58,7 @@ export class WalStreamTestContext { async updateSyncRules(content: string) { const syncRules = await this.factory.updateSyncRules({ content: content }); - this.storage = this.factory.getInstance(syncRules.parsed()); + this.storage = this.factory.getInstance(syncRules); return this.storage!; } diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index b5ad5ccab..393d56342 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -1,5 +1,6 @@ import { SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; +import { ParseSyncRulesOptions } from '../storage/BucketStorage.js'; export interface PatternResult { schema: string; @@ -69,4 +70,9 @@ export interface RouteAPI { * Close any resources that need graceful termination. */ shutdown(): Promise; + + /** + * Get the default schema (or database) when only a table name is specified in sync rules. + */ + getParseSyncRulesOptions(): ParseSyncRulesOptions; } diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 7fd4ef440..7b6da99ce 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -43,7 +43,7 @@ export async function getSyncRulesStatus( let rules: SqlSyncRules; let persisted: storage.PersistedSyncRules; try { - persisted = sync_rules.parsed(); + persisted = sync_rules.parsed(apiHandler.getParseSyncRulesOptions()); rules = persisted.sync_rules; } catch (e) { return { @@ -53,7 +53,7 @@ export async function getSyncRulesStatus( }; } - const systemStorage = live_status ? bucketStorage.getInstance(persisted) : undefined; + const systemStorage = live_status ? bucketStorage.getInstance(sync_rules) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index 6016644e5..5a6636bf1 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -34,7 +34,7 @@ export function registerCompactAction(program: Command) { await client.connect(); try { const bucketStorage = new storage.MongoBucketStorage(psdb, { slot_name_prefix: configuration.slot_name_prefix }); - const active = await bucketStorage.getActiveSyncRules(); + const active = await bucketStorage.getActiveSyncRulesContent(); if (active == null) { logger.info('No active instance to compact'); return; diff --git a/packages/service-core/src/replication/AbstractReplicator.ts b/packages/service-core/src/replication/AbstractReplicator.ts index bcf480fc9..6c5a2a934 100644 --- a/packages/service-core/src/replication/AbstractReplicator.ts +++ b/packages/service-core/src/replication/AbstractReplicator.ts @@ -166,8 +166,7 @@ export abstract class AbstractReplicator router.reactiveStream(SyncRoutes.STREAM, { validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }), - handler: async ({ context, params, responder, observer, initialN, signal: upstreamSignal}) => { + handler: async ({ context, params, responder, observer, initialN, signal: upstreamSignal }) => { const { service_context } = context; const { routerEngine } = service_context; @@ -72,6 +72,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) => try { for await (const data of sync.streamResponse({ storage: activeBucketStorage, + parseOptions: routerEngine!.getAPI().getParseSyncRulesOptions(), params: { ...params, binary_data: true // always true for web sockets diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 605d3c82d..1129a5855 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -53,7 +53,12 @@ export const deploySyncRules = routeDefinition({ const content = payload.params.content; try { - SqlSyncRules.fromYaml(payload.params.content); + const apiHandler = service_context.routerEngine!.getAPI(); + SqlSyncRules.fromYaml(payload.params.content, { + ...apiHandler.getParseSyncRulesOptions(), + // We don't do any schema-level validation at this point + schema: undefined + }); } catch (e) { throw new errors.JourneyError({ status: 422, @@ -151,7 +156,8 @@ export const reprocessSyncRules = routeDefinition({ const { storageEngine: { activeBucketStorage } } = payload.context.service_context; - const sync_rules = await activeBucketStorage.getActiveSyncRules(); + const apiHandler = payload.context.service_context.routerEngine!.getAPI(); + const sync_rules = await activeBucketStorage.getActiveSyncRules(apiHandler.getParseSyncRulesOptions()); if (sync_rules == null) { throw new errors.JourneyError({ status: 422, @@ -181,7 +187,11 @@ function replyPrettyJson(payload: any) { async function debugSyncRules(apiHandler: RouteAPI, sync_rules: string) { try { - const rules = SqlSyncRules.fromYaml(sync_rules); + const rules = SqlSyncRules.fromYaml(sync_rules, { + ...apiHandler.getParseSyncRulesOptions(), + // No schema-based validation at this point + schema: undefined + }); const source_table_patterns = rules.getSourceTables(); const resolved_tables = await apiHandler.getDebugTablesInfo(source_table_patterns, rules); diff --git a/packages/service-core/src/routes/endpoints/sync-stream.ts b/packages/service-core/src/routes/endpoints/sync-stream.ts index e637e99ea..83d8f3994 100644 --- a/packages/service-core/src/routes/endpoints/sync-stream.ts +++ b/packages/service-core/src/routes/endpoints/sync-stream.ts @@ -54,6 +54,7 @@ export const syncStreamed = routeDefinition({ sync.ndjson( sync.streamResponse({ storage: storageEngine.activeBucketStorage, + parseOptions: routerEngine!.getAPI().getParseSyncRulesOptions(), params, syncParams, token: payload.context.token_payload!, diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index 49e2bc66d..583635642 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -51,7 +51,7 @@ async function terminateSyncRules(storageFactory: storage.BucketStorageFactory, // Mark the sync rules as terminated for (let syncRules of combinedSyncRules) { - const syncRulesStorage = storageFactory.getInstance(syncRules.parsed()); + const syncRulesStorage = storageFactory.getInstance(syncRules); // The storage will be dropped at the end of the teardown, so we don't need to clear it here await syncRulesStorage.terminate({ clearStorage: false }); } diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 5441b2503..03a29dd57 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -24,7 +24,7 @@ export interface BucketStorageFactory { /** * Get a storage instance to query sync data for specific sync rules. */ - getInstance(options: PersistedSyncRules): SyncRulesBucketStorage; + getInstance(options: PersistedSyncRulesContent): SyncRulesBucketStorage; /** * Deploy new sync rules. @@ -48,7 +48,7 @@ export interface BucketStorageFactory { /** * Get the sync rules used for querying. */ - getActiveSyncRules(): Promise; + getActiveSyncRules(options: ParseSyncRulesOptions): Promise; /** * Get the sync rules used for querying. @@ -58,7 +58,7 @@ export interface BucketStorageFactory { /** * Get the sync rules that will be active next once done with initial replicatino. */ - getNextSyncRules(): Promise; + getNextSyncRules(options: ParseSyncRulesOptions): Promise; /** * Get the sync rules that will be active next once done with initial replicatino. @@ -131,6 +131,10 @@ export interface StorageMetrics { replication_size_bytes: number; } +export interface ParseSyncRulesOptions { + defaultSchema: string; +} + export interface PersistedSyncRulesContent { readonly id: number; readonly sync_rules_content: string; @@ -140,7 +144,7 @@ export interface PersistedSyncRulesContent { readonly last_keepalive_ts?: Date | null; readonly last_checkpoint_ts?: Date | null; - parsed(): PersistedSyncRules; + parsed(options: ParseSyncRulesOptions): PersistedSyncRules; lock(): Promise; } @@ -186,12 +190,11 @@ export interface BucketDataBatchOptions { chunkLimitBytes?: number; } -export interface StartBatchOptions { +export interface StartBatchOptions extends ParseSyncRulesOptions { zeroLSN: string; } export interface SyncRulesBucketStorage { - readonly sync_rules: SqlSyncRules; readonly group_id: number; readonly slot_name: string; @@ -206,6 +209,8 @@ export interface SyncRulesBucketStorage { getCheckpoint(): Promise<{ checkpoint: util.OpId }>; + getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules; + getParameterSets(checkpoint: util.OpId, lookups: SqliteJsonValue[][]): Promise; /** @@ -354,7 +359,7 @@ export interface SaveBucketData { export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete; -export type ReplicaId = string | bson.UUID | bson.Document; +export type ReplicaId = string | bson.UUID | bson.Document | any; export interface SaveInsert { tag: 'insert'; diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index 2cf45c436..c3e59d0b7 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -13,6 +13,7 @@ import { v4 as uuid } from 'uuid'; import { ActiveCheckpoint, BucketStorageFactory, + ParseSyncRulesOptions, PersistedSyncRules, PersistedSyncRulesContent, StorageMetrics, @@ -47,7 +48,7 @@ export class MongoBucketStorage implements BucketStorageFactory { return undefined; } const rules = new MongoPersistedSyncRulesContent(this.db, doc2); - return this.getInstance(rules.parsed()); + return this.getInstance(rules); } }); @@ -60,12 +61,12 @@ export class MongoBucketStorage implements BucketStorageFactory { this.slot_name_prefix = options.slot_name_prefix; } - getInstance(options: PersistedSyncRules): MongoSyncBucketStorage { - let { id, sync_rules, slot_name } = options; + getInstance(options: PersistedSyncRulesContent): MongoSyncBucketStorage { + let { id, slot_name } = options; if ((typeof id as any) == 'bigint') { id = Number(id); } - return new MongoSyncBucketStorage(this, id, sync_rules, slot_name); + return new MongoSyncBucketStorage(this, id, options, slot_name); } async configureSyncRules(sync_rules: string, options?: { lock?: boolean }) { @@ -135,7 +136,12 @@ export class MongoBucketStorage implements BucketStorageFactory { async updateSyncRules(options: UpdateSyncRulesOptions): Promise { // Parse and validate before applying any changes - const parsed = SqlSyncRules.fromYaml(options.content); + const parsed = SqlSyncRules.fromYaml(options.content, { + // No schema-based validation at this point + schema: undefined, + defaultSchema: 'not_applicable', // Not needed for validation + throwOnError: true + }); let rules: MongoPersistedSyncRulesContent | undefined = undefined; @@ -203,9 +209,9 @@ export class MongoBucketStorage implements BucketStorageFactory { return new MongoPersistedSyncRulesContent(this.db, doc); } - async getActiveSyncRules(): Promise { + async getActiveSyncRules(options: ParseSyncRulesOptions): Promise { const content = await this.getActiveSyncRulesContent(); - return content?.parsed() ?? null; + return content?.parsed(options) ?? null; } async getNextSyncRulesContent(): Promise { @@ -222,9 +228,9 @@ export class MongoBucketStorage implements BucketStorageFactory { return new MongoPersistedSyncRulesContent(this.db, doc); } - async getNextSyncRules(): Promise { + async getNextSyncRules(options: ParseSyncRulesOptions): Promise { const content = await this.getNextSyncRulesContent(); - return content?.parsed() ?? null; + return content?.parsed(options) ?? null; } async getReplicatingSyncRules(): Promise { @@ -293,14 +299,6 @@ export class MongoBucketStorage implements BucketStorageFactory { } async getStorageMetrics(): Promise { - const active_sync_rules = await this.getActiveSyncRules(); - if (active_sync_rules == null) { - return { - operations_size_bytes: 0, - parameters_size_bytes: 0, - replication_size_bytes: 0 - }; - } const operations_aggregate = await this.db.bucket_data .aggregate([ diff --git a/packages/service-core/src/storage/SourceTable.ts b/packages/service-core/src/storage/SourceTable.ts index 6379732f5..2f5364633 100644 --- a/packages/service-core/src/storage/SourceTable.ts +++ b/packages/service-core/src/storage/SourceTable.ts @@ -1,9 +1,8 @@ -import { DEFAULT_SCHEMA, DEFAULT_TAG } from '@powersync/service-sync-rules'; +import { DEFAULT_TAG } from '@powersync/service-sync-rules'; import * as util from '../util/util-index.js'; import { ColumnDescriptor } from './SourceEntity.js'; export class SourceTable { - static readonly DEFAULT_SCHEMA = DEFAULT_SCHEMA; static readonly DEFAULT_TAG = DEFAULT_TAG; /** diff --git a/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts b/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts index a32cf6fc1..363766df4 100644 --- a/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts +++ b/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts @@ -1,7 +1,7 @@ import { SqlSyncRules } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; -import { PersistedSyncRulesContent } from '../BucketStorage.js'; +import { ParseSyncRulesOptions, PersistedSyncRulesContent } from '../BucketStorage.js'; import { MongoPersistedSyncRules } from './MongoPersistedSyncRules.js'; import { MongoSyncRulesLock } from './MongoSyncRulesLock.js'; import { PowerSyncMongo } from './db.js'; @@ -30,10 +30,10 @@ export class MongoPersistedSyncRulesContent implements PersistedSyncRulesContent this.last_keepalive_ts = doc.last_keepalive_ts; } - parsed() { + parsed(options: ParseSyncRulesOptions) { return new MongoPersistedSyncRules( this.id, - SqlSyncRules.fromYaml(this.sync_rules_content), + SqlSyncRules.fromYaml(this.sync_rules_content, options), this.last_checkpoint_lsn, this.slot_name ); diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 95715d45c..771f5093d 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -11,6 +11,9 @@ import { DEFAULT_DOCUMENT_BATCH_LIMIT, DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES, FlushedResult, + ParseSyncRulesOptions, + PersistedSyncRules, + PersistedSyncRulesContent, ResolveTableOptions, ResolveTableResult, StartBatchOptions, @@ -36,15 +39,22 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { } }); + private parsedSyncRulesCache: SqlSyncRules | undefined; + constructor( public readonly factory: MongoBucketStorage, public readonly group_id: number, - public readonly sync_rules: SqlSyncRules, + private readonly sync_rules: PersistedSyncRulesContent, public readonly slot_name: string ) { this.db = factory.db; } + getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules { + this.parsedSyncRulesCache ??= this.sync_rules.parsed(options).sync_rules; + return this.parsedSyncRulesCache; + } + async getCheckpoint() { const doc = await this.db.sync_rules.findOne( { _id: this.group_id }, @@ -71,7 +81,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { const batch = new MongoBucketBatch( this.db, - this.sync_rules, + this.sync_rules.parsed(options).sync_rules, this.group_id, this.slot_name, checkpoint_lsn, diff --git a/packages/service-core/src/storage/mongo/OperationBatch.ts b/packages/service-core/src/storage/mongo/OperationBatch.ts index 75babb5a6..74b4b651f 100644 --- a/packages/service-core/src/storage/mongo/OperationBatch.ts +++ b/packages/service-core/src/storage/mongo/OperationBatch.ts @@ -2,6 +2,7 @@ import { ToastableSqliteRow } from '@powersync/service-sync-rules'; import * as bson from 'bson'; import { ReplicaId, SaveOptions } from '../BucketStorage.js'; +import { isUUID } from './util.js'; /** * Maximum number of operations in a batch. @@ -80,13 +81,16 @@ export class RecordOperation { } } +/** + * In-memory cache key - must not be persisted. + */ export function cacheKey(table: bson.ObjectId, id: ReplicaId) { - if (id instanceof bson.UUID) { + if (isUUID(id)) { return `${table.toHexString()}.${id.toHexString()}`; } else if (typeof id == 'string') { return `${table.toHexString()}.${id}`; } else { - return `${table.toHexString()}.${(bson.serialize(id) as Buffer).toString('base64')}`; + return `${table.toHexString()}.${(bson.serialize({ id: id }) as Buffer).toString('base64')}`; } } diff --git a/packages/service-core/src/storage/mongo/util.ts b/packages/service-core/src/storage/mongo/util.ts index 8dc5ac40b..58a9bc36c 100644 --- a/packages/service-core/src/storage/mongo/util.ts +++ b/packages/service-core/src/storage/mongo/util.ts @@ -115,9 +115,11 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { } export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { - if (typeof a == 'string' && typeof b == 'string') { + if (a === b) { + return true; + } else if (typeof a == 'string' && typeof b == 'string') { return a == b; - } else if (a instanceof bson.UUID && b instanceof bson.UUID) { + } else if (isUUID(a) && isUUID(b)) { return a.equals(b); } else if (a == null && b == null) { return true; @@ -129,13 +131,22 @@ export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { } export function replicaIdToSubkey(id: ReplicaId) { - if (id instanceof bson.UUID) { + if (isUUID(id)) { + // Special case for UUID for backwards-compatiblity return id.toHexString(); } else if (typeof id == 'string') { return id; } else { - const repr = bson.serialize(id); + const repr = bson.serialize({ id: id }); return uuid.v5(repr, ID_NAMESPACE); return; } } + +export function isUUID(value: any): value is bson.UUID { + if (value == null || typeof value != 'object') { + return false; + } + const uuid = value as bson.UUID; + return uuid._bsontype == 'Binary' && uuid.sub_type == bson.Binary.SUBTYPE_UUID; +} diff --git a/packages/service-core/src/sync/sync.ts b/packages/service-core/src/sync/sync.ts index a04c53182..8f2f900a0 100644 --- a/packages/service-core/src/sync/sync.ts +++ b/packages/service-core/src/sync/sync.ts @@ -1,6 +1,7 @@ import { JSONBig, JsonContainer } from '@powersync/service-jsonbig'; import { RequestParameters } from '@powersync/service-sync-rules'; import { Semaphore, withTimeout } from 'async-mutex'; + import { AbortError } from 'ix/aborterror.js'; import * as auth from '../auth/auth-index.js'; @@ -35,6 +36,7 @@ export interface SyncStreamParameters { params: util.StreamingSyncRequest; syncParams: RequestParameters; token: auth.JwtPayload; + parseOptions: storage.ParseSyncRulesOptions; /** * If this signal is aborted, the stream response ends as soon as possible, without error. */ @@ -47,7 +49,7 @@ export interface SyncStreamParameters { export async function* streamResponse( options: SyncStreamParameters ): AsyncIterable { - const { storage, params, syncParams, token, tokenStreamOptions, tracker, signal } = options; + const { storage, params, syncParams, token, tokenStreamOptions, tracker, signal, parseOptions } = options; // We also need to be able to abort, so we create our own controller. const controller = new AbortController(); if (signal) { @@ -63,7 +65,7 @@ export async function* streamResponse( } } const ki = tokenStream(token, controller.signal, tokenStreamOptions); - const stream = streamResponseInner(storage, params, syncParams, tracker, controller.signal); + const stream = streamResponseInner(storage, params, syncParams, tracker, parseOptions, controller.signal); // Merge the two streams, and abort as soon as one of the streams end. const merged = mergeAsyncIterables([stream, ki], controller.signal); @@ -87,6 +89,7 @@ async function* streamResponseInner( params: util.StreamingSyncRequest, syncParams: RequestParameters, tracker: RequestTracker, + parseOptions: storage.ParseSyncRulesOptions, signal: AbortSignal ): AsyncGenerator { // Bucket state of bucket id -> op_id. @@ -115,9 +118,9 @@ async function* streamResponseInner( // Sync rules deleted in the meantime - try again with the next checkpoint. continue; } - const sync_rules = storage.sync_rules; + const syncRules = storage.getParsedSyncRules(parseOptions); - const allBuckets = await sync_rules.queryBucketIds({ + const allBuckets = await syncRules.queryBucketIds({ getParameterSets(lookups) { return storage.getParameterSets(checkpoint, lookups); }, diff --git a/packages/sync-rules/src/SqlBucketDescriptor.ts b/packages/sync-rules/src/SqlBucketDescriptor.ts index 459178ee1..a140a1fc2 100644 --- a/packages/sync-rules/src/SqlBucketDescriptor.ts +++ b/packages/sync-rules/src/SqlBucketDescriptor.ts @@ -2,6 +2,7 @@ import { IdSequence } from './IdSequence.js'; import { SourceTableInterface } from './SourceTableInterface.js'; import { SqlDataQuery } from './SqlDataQuery.js'; import { SqlParameterQuery } from './SqlParameterQuery.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; import { StaticSqlParameterQuery } from './StaticSqlParameterQuery.js'; import { TablePattern } from './TablePattern.js'; import { SqlRuleError } from './errors.js'; @@ -42,11 +43,11 @@ export class SqlBucketDescriptor { parameterIdSequence = new IdSequence(); - addDataQuery(sql: string, schema?: SourceSchema): QueryParseResult { + addDataQuery(sql: string, options: SyncRulesOptions): QueryParseResult { if (this.bucket_parameters == null) { throw new Error('Bucket parameters must be defined'); } - const dataRows = SqlDataQuery.fromSql(this.name, this.bucket_parameters, sql, schema); + const dataRows = SqlDataQuery.fromSql(this.name, this.bucket_parameters, sql, options); dataRows.ruleId = this.idSequence.nextId(); @@ -58,8 +59,8 @@ export class SqlBucketDescriptor { }; } - addParameterQuery(sql: string, schema: SourceSchema | undefined, options: QueryParseOptions): QueryParseResult { - const parameterQuery = SqlParameterQuery.fromSql(this.name, sql, schema, options); + addParameterQuery(sql: string, options: QueryParseOptions): QueryParseResult { + const parameterQuery = SqlParameterQuery.fromSql(this.name, sql, options); if (this.bucket_parameters == null) { this.bucket_parameters = parameterQuery.bucket_parameters; } else { diff --git a/packages/sync-rules/src/SqlDataQuery.ts b/packages/sync-rules/src/SqlDataQuery.ts index c1176bc9b..b5377d436 100644 --- a/packages/sync-rules/src/SqlDataQuery.ts +++ b/packages/sync-rules/src/SqlDataQuery.ts @@ -19,6 +19,7 @@ import { } from './types.js'; import { filterJsonRow, getBucketId, isSelectStatement } from './utils.js'; import { TableQuerySchema } from './TableQuerySchema.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; interface RowValueExtractor { extract(tables: QueryParameters, into: SqliteRow): void; @@ -26,9 +27,10 @@ interface RowValueExtractor { } export class SqlDataQuery { - static fromSql(descriptor_name: string, bucket_parameters: string[], sql: string, schema?: SourceSchema) { + static fromSql(descriptor_name: string, bucket_parameters: string[], sql: string, options: SyncRulesOptions) { const parsed = parse(sql, { locationTracking: true }); const rows = new SqlDataQuery(); + const schema = options.schema; if (parsed.length > 1) { throw new SqlRuleError('Only a single SELECT statement is supported', sql, parsed[1]?._location); @@ -50,7 +52,7 @@ export class SqlDataQuery { } const alias: string = tableRef.alias ?? tableRef.name; - const sourceTable = new TablePattern(tableRef.schema, tableRef.name); + const sourceTable = new TablePattern(tableRef.schema ?? options.defaultSchema, tableRef.name); let querySchema: QuerySchema | undefined = undefined; if (schema) { const tables = schema.getTables(sourceTable); diff --git a/packages/sync-rules/src/SqlParameterQuery.ts b/packages/sync-rules/src/SqlParameterQuery.ts index f13352177..2b5a4d14a 100644 --- a/packages/sync-rules/src/SqlParameterQuery.ts +++ b/packages/sync-rules/src/SqlParameterQuery.ts @@ -23,6 +23,7 @@ import { SqliteRow } from './types.js'; import { filterJsonRow, getBucketId, isJsonValue, isSelectStatement } from './utils.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; /** * Represents a parameter query, such as: @@ -34,11 +35,11 @@ export class SqlParameterQuery { static fromSql( descriptor_name: string, sql: string, - schema?: SourceSchema, - options?: QueryParseOptions + options: QueryParseOptions ): SqlParameterQuery | StaticSqlParameterQuery { const parsed = parse(sql, { locationTracking: true }); const rows = new SqlParameterQuery(); + const schema = options?.schema; if (parsed.length > 1) { throw new SqlRuleError('Only a single SELECT statement is supported', sql, parsed[1]?._location); @@ -70,7 +71,7 @@ export class SqlParameterQuery { new SqlRuleError('Table aliases not supported in parameter queries', sql, q.from?.[0]._location) ); } - const sourceTable = new TablePattern(tableRef.schema, tableRef.name); + const sourceTable = new TablePattern(tableRef.schema ?? options.defaultSchema, tableRef.name); let querySchema: QuerySchema | undefined = undefined; if (schema) { const tables = schema.getTables(sourceTable); @@ -139,7 +140,7 @@ export class SqlParameterQuery { rows.tools = tools; rows.errors.push(...tools.errors); - if (rows.usesDangerousRequestParameters && !options?.accept_potentially_dangerous_queries) { + if (rows.usesDangerousRequestParameters && !options.accept_potentially_dangerous_queries) { let err = new SqlRuleError( "Potentially dangerous query based on parameters set by the client. The client can send any value for these parameters so it's not a good place to do authorization.", sql diff --git a/packages/sync-rules/src/SqlSyncRules.ts b/packages/sync-rules/src/SqlSyncRules.ts index 15b84adc6..d93292b49 100644 --- a/packages/sync-rules/src/SqlSyncRules.ts +++ b/packages/sync-rules/src/SqlSyncRules.ts @@ -25,6 +25,16 @@ import { const ACCEPT_POTENTIALLY_DANGEROUS_QUERIES = Symbol('ACCEPT_POTENTIALLY_DANGEROUS_QUERIES'); +export interface SyncRulesOptions { + schema?: SourceSchema; + /** + * 'public' for Postgres, default database for MongoDB/MySQL. + */ + defaultSchema: string; + + throwOnError?: boolean; +} + export class SqlSyncRules implements SyncRules { bucket_descriptors: SqlBucketDescriptor[] = []; idSequence = new IdSequence(); @@ -33,7 +43,7 @@ export class SqlSyncRules implements SyncRules { errors: YamlError[] = []; - static validate(yaml: string, options?: { schema?: SourceSchema }): YamlError[] { + static validate(yaml: string, options: SyncRulesOptions): YamlError[] { try { const rules = this.fromYaml(yaml, options); return rules.errors; @@ -48,9 +58,9 @@ export class SqlSyncRules implements SyncRules { } } - static fromYaml(yaml: string, options?: { throwOnError?: boolean; schema?: SourceSchema }) { - const throwOnError = options?.throwOnError ?? true; - const schema = options?.schema; + static fromYaml(yaml: string, options: SyncRulesOptions) { + const throwOnError = options.throwOnError ?? true; + const schema = options.schema; const lineCounter = new LineCounter(); const parsed = parseDocument(yaml, { @@ -98,7 +108,8 @@ export class SqlSyncRules implements SyncRules { const accept_potentially_dangerous_queries = value.get('accept_potentially_dangerous_queries', true)?.value == true; - const options: QueryParseOptions = { + const queryOptions: QueryParseOptions = { + ...options, accept_potentially_dangerous_queries }; const parameters = value.get('parameters', true) as unknown; @@ -108,16 +119,16 @@ export class SqlSyncRules implements SyncRules { if (parameters instanceof Scalar) { rules.withScalar(parameters, (q) => { - return descriptor.addParameterQuery(q, schema, options); + return descriptor.addParameterQuery(q, queryOptions); }); } else if (parameters instanceof YAMLSeq) { for (let item of parameters.items) { rules.withScalar(item, (q) => { - return descriptor.addParameterQuery(q, schema, options); + return descriptor.addParameterQuery(q, queryOptions); }); } } else { - descriptor.addParameterQuery('SELECT', schema, options); + descriptor.addParameterQuery('SELECT', queryOptions); } if (!(dataQueries instanceof YAMLSeq)) { @@ -126,7 +137,7 @@ export class SqlSyncRules implements SyncRules { } for (let query of dataQueries.items) { rules.withScalar(query, (q) => { - return descriptor.addDataQuery(q, schema); + return descriptor.addDataQuery(q, queryOptions); }); } rules.bucket_descriptors.push(descriptor); diff --git a/packages/sync-rules/src/TablePattern.ts b/packages/sync-rules/src/TablePattern.ts index d6d3494ba..55c90ec9e 100644 --- a/packages/sync-rules/src/TablePattern.ts +++ b/packages/sync-rules/src/TablePattern.ts @@ -1,7 +1,6 @@ import { SourceTableInterface } from './SourceTableInterface.js'; export const DEFAULT_TAG = 'default'; -export const DEFAULT_SCHEMA = 'public'; /** * Some pattern matching SourceTables. @@ -12,8 +11,7 @@ export class TablePattern { public readonly schema: string; public readonly tablePattern: string; - constructor(schema: string | undefined, tablePattern: string) { - schema ??= DEFAULT_SCHEMA; + constructor(schema: string, tablePattern: string) { const splitSchema = schema.split('.'); if (splitSchema.length > 2) { throw new Error(`Invalid schema: ${schema}`); diff --git a/packages/sync-rules/src/types.ts b/packages/sync-rules/src/types.ts index e54d508df..e27489435 100644 --- a/packages/sync-rules/src/types.ts +++ b/packages/sync-rules/src/types.ts @@ -3,6 +3,7 @@ import { SourceTableInterface } from './SourceTableInterface.js'; import { ColumnDefinition, ExpressionType } from './ExpressionType.js'; import { TablePattern } from './TablePattern.js'; import { toSyncRulesParameters } from './utils.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; export interface SyncRules { evaluateRow(options: EvaluateRowOptions): EvaluationResult[]; @@ -10,7 +11,7 @@ export interface SyncRules { evaluateParameterRow(table: SourceTableInterface, row: SqliteRow): EvaluatedParametersResult[]; } -export interface QueryParseOptions { +export interface QueryParseOptions extends SyncRulesOptions { accept_potentially_dangerous_queries?: boolean; } From 48a185234cc89f27d4fb6103f81ad229b5378722 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 14:27:46 +0200 Subject: [PATCH 174/298] Fix tests. --- .../test/src/validation.test.ts | 4 +- .../sync-rules/test/src/data_queries.test.ts | 40 ++-- .../test/src/parameter_queries.test.ts | 105 +++++----- .../test/src/static_parameter_queries.test.ts | 25 +-- .../sync-rules/test/src/sync_rules.test.ts | 188 ++++++++++++------ packages/sync-rules/test/src/util.ts | 9 +- 6 files changed, 229 insertions(+), 142 deletions(-) diff --git a/modules/module-postgres/test/src/validation.test.ts b/modules/module-postgres/test/src/validation.test.ts index 54dced77e..b7b7b23f2 100644 --- a/modules/module-postgres/test/src/validation.test.ts +++ b/modules/module-postgres/test/src/validation.test.ts @@ -22,13 +22,13 @@ bucket_definitions: const syncRules = await context.factory.updateSyncRules({ content: syncRuleContent }); - const tablePatterns = syncRules.parsed().sync_rules.getSourceTables(); + const tablePatterns = syncRules.parsed({ defaultSchema: 'public' }).sync_rules.getSourceTables(); const tableInfo = await getDebugTablesInfo({ db: pool, publicationName: context.publicationName, connectionTag: context.connectionTag, tablePatterns: tablePatterns, - syncRules: syncRules.parsed().sync_rules + syncRules: syncRules.parsed({ defaultSchema: 'public' }).sync_rules }); expect(tableInfo).toEqual([ { diff --git a/packages/sync-rules/test/src/data_queries.test.ts b/packages/sync-rules/test/src/data_queries.test.ts index 134585cd4..00ad2a3ec 100644 --- a/packages/sync-rules/test/src/data_queries.test.ts +++ b/packages/sync-rules/test/src/data_queries.test.ts @@ -1,11 +1,11 @@ import { describe, expect, test } from 'vitest'; import { ExpressionType, SqlDataQuery } from '../../src/index.js'; -import { ASSETS, BASIC_SCHEMA } from './util.js'; +import { ASSETS, BASIC_SCHEMA, PARSE_OPTIONS } from './util.js'; describe('data queries', () => { test('bucket parameters = query', function () { const sql = 'SELECT * FROM assets WHERE assets.org_id = bucket.org_id'; - const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql); + const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql, PARSE_OPTIONS); expect(query.errors).toEqual([]); expect(query.evaluateRow(ASSETS, { id: 'asset1', org_id: 'org1' })).toEqual([ @@ -22,7 +22,7 @@ describe('data queries', () => { test('bucket parameters IN query', function () { const sql = 'SELECT * FROM assets WHERE bucket.category IN assets.categories'; - const query = SqlDataQuery.fromSql('mybucket', ['category'], sql); + const query = SqlDataQuery.fromSql('mybucket', ['category'], sql, PARSE_OPTIONS); expect(query.errors).toEqual([]); expect(query.evaluateRow(ASSETS, { id: 'asset1', categories: JSON.stringify(['red', 'green']) })).toMatchObject([ @@ -43,7 +43,7 @@ describe('data queries', () => { test('table alias', function () { const sql = 'SELECT * FROM assets as others WHERE others.org_id = bucket.org_id'; - const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql); + const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql, PARSE_OPTIONS); expect(query.errors).toEqual([]); expect(query.evaluateRow(ASSETS, { id: 'asset1', org_id: 'org1' })).toEqual([ @@ -59,7 +59,12 @@ describe('data queries', () => { test('types', () => { const schema = BASIC_SCHEMA; - const q1 = SqlDataQuery.fromSql('q1', ['user_id'], `SELECT * FROM assets WHERE owner_id = bucket.user_id`); + const q1 = SqlDataQuery.fromSql( + 'q1', + ['user_id'], + `SELECT * FROM assets WHERE owner_id = bucket.user_id`, + PARSE_OPTIONS + ); expect(q1.getColumnOutputs(schema)).toEqual([ { name: 'assets', @@ -84,7 +89,8 @@ describe('data queries', () => { count * '4' as count4, name ->> '$.attr' as json_value, ifnull(name, 2.0) as maybe_name - FROM assets WHERE owner_id = bucket.user_id` + FROM assets WHERE owner_id = bucket.user_id`, + PARSE_OPTIONS ); expect(q2.getColumnOutputs(schema)).toEqual([ { @@ -109,7 +115,7 @@ describe('data queries', () => { 'q1', ['user_id'], 'SELECT id, name, count FROM assets WHERE owner_id = bucket.user_id', - schema + { ...PARSE_OPTIONS, schema } ); expect(q1.errors).toEqual([]); @@ -117,7 +123,7 @@ describe('data queries', () => { 'q2', ['user_id'], 'SELECT id, upper(description) as d FROM assets WHERE other_id = bucket.user_id', - schema + { ...PARSE_OPTIONS, schema } ); expect(q2.errors).toMatchObject([ { @@ -134,7 +140,7 @@ describe('data queries', () => { 'q3', ['user_id'], 'SELECT id, description, * FROM nope WHERE other_id = bucket.user_id', - schema + { ...PARSE_OPTIONS, schema } ); expect(q3.errors).toMatchObject([ { @@ -143,7 +149,7 @@ describe('data queries', () => { } ]); - const q4 = SqlDataQuery.fromSql('q4', [], 'SELECT * FROM other', schema); + const q4 = SqlDataQuery.fromSql('q4', [], 'SELECT * FROM other', { ...PARSE_OPTIONS, schema }); expect(q4.errors).toMatchObject([ { message: `Query must return an "id" column`, @@ -151,19 +157,19 @@ describe('data queries', () => { } ]); - const q5 = SqlDataQuery.fromSql('q5', [], 'SELECT other_id as id, * FROM other', schema); + const q5 = SqlDataQuery.fromSql('q5', [], 'SELECT other_id as id, * FROM other', { ...PARSE_OPTIONS, schema }); expect(q5.errors).toMatchObject([]); }); test('invalid query - invalid IN', function () { const sql = 'SELECT * FROM assets WHERE assets.category IN bucket.categories'; - const query = SqlDataQuery.fromSql('mybucket', ['categories'], sql); + const query = SqlDataQuery.fromSql('mybucket', ['categories'], sql, PARSE_OPTIONS); expect(query.errors).toMatchObject([{ type: 'fatal', message: 'Unsupported usage of IN operator' }]); }); test('invalid query - not all parameters used', function () { const sql = 'SELECT * FROM assets WHERE 1'; - const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql); + const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql, PARSE_OPTIONS); expect(query.errors).toMatchObject([ { type: 'fatal', message: 'Query must cover all bucket parameters. Expected: ["bucket.org_id"] Got: []' } ]); @@ -171,7 +177,7 @@ describe('data queries', () => { test('invalid query - parameter not defined', function () { const sql = 'SELECT * FROM assets WHERE assets.org_id = bucket.org_id'; - const query = SqlDataQuery.fromSql('mybucket', [], sql); + const query = SqlDataQuery.fromSql('mybucket', [], sql, PARSE_OPTIONS); expect(query.errors).toMatchObject([ { type: 'fatal', message: 'Query must cover all bucket parameters. Expected: [] Got: ["bucket.org_id"]' } ]); @@ -179,19 +185,19 @@ describe('data queries', () => { test('invalid query - function on parameter (1)', function () { const sql = 'SELECT * FROM assets WHERE assets.org_id = upper(bucket.org_id)'; - const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql); + const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql, PARSE_OPTIONS); expect(query.errors).toMatchObject([{ type: 'fatal', message: 'Cannot use bucket parameters in expressions' }]); }); test('invalid query - function on parameter (2)', function () { const sql = 'SELECT * FROM assets WHERE assets.org_id = upper(bucket.org_id)'; - const query = SqlDataQuery.fromSql('mybucket', [], sql); + const query = SqlDataQuery.fromSql('mybucket', [], sql, PARSE_OPTIONS); expect(query.errors).toMatchObject([{ type: 'fatal', message: 'Cannot use bucket parameters in expressions' }]); }); test('invalid query - match clause in select', () => { const sql = 'SELECT id, (bucket.org_id = assets.org_id) as org_matches FROM assets where org_id = bucket.org_id'; - const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql); + const query = SqlDataQuery.fromSql('mybucket', ['org_id'], sql, PARSE_OPTIONS); expect(query.errors[0].message).toMatch(/Parameter match expression is not allowed here/); }); }); diff --git a/packages/sync-rules/test/src/parameter_queries.test.ts b/packages/sync-rules/test/src/parameter_queries.test.ts index 7e6a0ee34..78468f08a 100644 --- a/packages/sync-rules/test/src/parameter_queries.test.ts +++ b/packages/sync-rules/test/src/parameter_queries.test.ts @@ -1,11 +1,11 @@ import { describe, expect, test } from 'vitest'; import { SqlParameterQuery } from '../../src/index.js'; -import { BASIC_SCHEMA, normalizeTokenParameters } from './util.js'; +import { BASIC_SCHEMA, normalizeTokenParameters, PARSE_OPTIONS } from './util.js'; describe('parameter queries', () => { test('token_parameters IN query', function () { const sql = 'SELECT id as group_id FROM groups WHERE token_parameters.user_id IN groups.user_ids'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; expect(query.evaluateParameterRow({ id: 'group1', user_ids: JSON.stringify(['user1', 'user2']) })).toEqual([ @@ -37,7 +37,7 @@ describe('parameter queries', () => { test('IN token_parameters query', function () { const sql = 'SELECT id as region_id FROM regions WHERE name IN token_parameters.region_names'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; expect(query.evaluateParameterRow({ id: 'region1', name: 'colorado' })).toEqual([ @@ -65,7 +65,7 @@ describe('parameter queries', () => { test('queried numeric parameters', () => { const sql = 'SELECT users.int1, users.float1, users.float2 FROM users WHERE users.int1 = token_parameters.int1 AND users.float1 = token_parameters.float1 AND users.float2 = token_parameters.float2'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; // Note: We don't need to worry about numeric vs decimal types in the lookup - JSONB handles normalization for us. @@ -95,7 +95,7 @@ describe('parameter queries', () => { test('plain token_parameter (baseline)', () => { const sql = 'SELECT id from users WHERE filter_param = token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'test_id', filter_param: 'test_param' })).toEqual([ @@ -111,7 +111,7 @@ describe('parameter queries', () => { test('function on token_parameter', () => { const sql = 'SELECT id from users WHERE filter_param = upper(token_parameters.user_id)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'test_id', filter_param: 'test_param' })).toEqual([ @@ -127,7 +127,7 @@ describe('parameter queries', () => { test('token parameter member operator', () => { const sql = "SELECT id from users WHERE filter_param = token_parameters.some_param ->> 'description'"; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'test_id', filter_param: 'test_param' })).toEqual([ @@ -145,7 +145,7 @@ describe('parameter queries', () => { test('token parameter and binary operator', () => { const sql = 'SELECT id from users WHERE filter_param = token_parameters.some_param + 2'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getLookups(normalizeTokenParameters({ some_param: 3 }))).toEqual([['mybucket', undefined, 5n]]); @@ -153,7 +153,7 @@ describe('parameter queries', () => { test('token parameter IS NULL as filter', () => { const sql = 'SELECT id from users WHERE filter_param = (token_parameters.some_param IS NULL)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getLookups(normalizeTokenParameters({ some_param: null }))).toEqual([['mybucket', undefined, 1n]]); @@ -162,7 +162,7 @@ describe('parameter queries', () => { test('direct token parameter', () => { const sql = 'SELECT FROM users WHERE token_parameters.some_param'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -182,7 +182,7 @@ describe('parameter queries', () => { test('token parameter IS NULL', () => { const sql = 'SELECT FROM users WHERE token_parameters.some_param IS NULL'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -202,7 +202,7 @@ describe('parameter queries', () => { test('token parameter IS NOT NULL', () => { const sql = 'SELECT FROM users WHERE token_parameters.some_param IS NOT NULL'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -222,7 +222,7 @@ describe('parameter queries', () => { test('token parameter NOT', () => { const sql = 'SELECT FROM users WHERE NOT token_parameters.is_admin'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -242,7 +242,7 @@ describe('parameter queries', () => { test('row filter and token parameter IS NULL', () => { const sql = 'SELECT FROM users WHERE users.id = token_parameters.user_id AND token_parameters.some_param IS NULL'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -262,7 +262,7 @@ describe('parameter queries', () => { test('row filter and direct token parameter', () => { const sql = 'SELECT FROM users WHERE users.id = token_parameters.user_id AND token_parameters.some_param'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -282,7 +282,7 @@ describe('parameter queries', () => { test('cast', () => { const sql = 'SELECT FROM users WHERE users.id = cast(token_parameters.user_id as text)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getLookups(normalizeTokenParameters({ user_id: 'user1' }))).toEqual([ @@ -293,7 +293,7 @@ describe('parameter queries', () => { test('IS NULL row filter', () => { const sql = 'SELECT id FROM users WHERE role IS NULL'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'user1', role: null })).toEqual([ @@ -311,7 +311,7 @@ describe('parameter queries', () => { // Not supported: token_parameters.is_admin != false // Support could be added later. const sql = 'SELECT FROM users WHERE users.id = token_parameters.user_id AND token_parameters.is_admin'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -334,7 +334,7 @@ describe('parameter queries', () => { test('token filter (2)', () => { const sql = 'SELECT users.id AS user_id, token_parameters.is_admin as is_admin FROM users WHERE users.id = token_parameters.user_id AND token_parameters.is_admin'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -357,7 +357,7 @@ describe('parameter queries', () => { test('case-sensitive parameter queries (1)', () => { const sql = 'SELECT users."userId" AS user_id FROM users WHERE users."userId" = token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -375,7 +375,7 @@ describe('parameter queries', () => { // This may change in the future - we should check against expected behavior for // Postgres and/or SQLite. const sql = 'SELECT users.userId AS user_id FROM users WHERE users.userId = token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -391,7 +391,7 @@ describe('parameter queries', () => { test('dynamic global parameter query', () => { const sql = "SELECT workspaces.id AS workspace_id FROM workspaces WHERE visibility = 'public'"; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -410,7 +410,7 @@ describe('parameter queries', () => { // This is treated as two separate lookup index values const sql = 'SELECT id from users WHERE filter_param = upper(token_parameters.user_id) AND filter_param = lower(token_parameters.user_id)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'test_id', filter_param: 'test_param' })).toEqual([ @@ -430,7 +430,7 @@ describe('parameter queries', () => { // This is treated as the same index lookup value, can use OR with the two clauses const sql = 'SELECT id from users WHERE filter_param1 = upper(token_parameters.user_id) OR filter_param2 = upper(token_parameters.user_id)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.evaluateParameterRow({ id: 'test_id', filter_param1: 'test1', filter_param2: 'test2' })).toEqual([ @@ -449,8 +449,9 @@ describe('parameter queries', () => { test('request.parameters()', function () { const sql = "SELECT FROM posts WHERE category = request.parameters() ->> 'category_id'"; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, { - accept_potentially_dangerous_queries: true + const query = SqlParameterQuery.fromSql('mybucket', sql, { + accept_potentially_dangerous_queries: true, + ...PARSE_OPTIONS }) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -465,8 +466,9 @@ describe('parameter queries', () => { test('nested request.parameters() (1)', function () { const sql = "SELECT FROM posts WHERE category = request.parameters() -> 'details' ->> 'category'"; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, { - accept_potentially_dangerous_queries: true + const query = SqlParameterQuery.fromSql('mybucket', sql, { + accept_potentially_dangerous_queries: true, + ...PARSE_OPTIONS }) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -477,8 +479,9 @@ describe('parameter queries', () => { test('nested request.parameters() (2)', function () { const sql = "SELECT FROM posts WHERE category = request.parameters() ->> 'details.category'"; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, { - accept_potentially_dangerous_queries: true + const query = SqlParameterQuery.fromSql('mybucket', sql, { + accept_potentially_dangerous_queries: true, + ...PARSE_OPTIONS }) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -490,8 +493,9 @@ describe('parameter queries', () => { test('IN request.parameters()', function () { // Can use -> or ->> here const sql = "SELECT id as region_id FROM regions WHERE name IN request.parameters() -> 'region_names'"; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, { - accept_potentially_dangerous_queries: true + const query = SqlParameterQuery.fromSql('mybucket', sql, { + accept_potentially_dangerous_queries: true, + ...PARSE_OPTIONS }) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; @@ -522,7 +526,7 @@ describe('parameter queries', () => { test('user_parameters in SELECT', function () { const sql = 'SELECT id, user_parameters.other_id as other_id FROM users WHERE id = token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -538,7 +542,7 @@ describe('parameter queries', () => { test('request.parameters() in SELECT', function () { const sql = "SELECT id, request.parameters() ->> 'other_id' as other_id FROM users WHERE id = token_parameters.user_id"; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); query.id = '1'; expect(query.evaluateParameterRow({ id: 'user1' })).toEqual([ @@ -553,7 +557,7 @@ describe('parameter queries', () => { test('request.jwt()', function () { const sql = "SELECT FROM users WHERE id = request.jwt() ->> 'sub'"; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); const requestParams = normalizeTokenParameters({ user_id: 'user1' }); @@ -562,7 +566,7 @@ describe('parameter queries', () => { test('request.user_id()', function () { const sql = 'SELECT FROM users WHERE id = request.user_id()'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); const requestParams = normalizeTokenParameters({ user_id: 'user1' }); @@ -574,68 +578,67 @@ describe('parameter queries', () => { // into separate queries, but it's a significant change. For now, developers should do that manually. const sql = "SELECT workspaces.id AS workspace_id FROM workspaces WHERE workspaces.user_id = token_parameters.user_id OR visibility = 'public'"; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/must use the same parameters/); }); test('invalid OR in parameter queries (2)', () => { const sql = 'SELECT id from users WHERE filter_param = upper(token_parameters.user_id) OR filter_param = lower(token_parameters.user_id)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/must use the same parameters/); }); test('invalid parameter match clause (1)', () => { const sql = 'SELECT FROM users WHERE (id = token_parameters.user_id) = false'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/Parameter match clauses cannot be used here/); }); test('invalid parameter match clause (2)', () => { const sql = 'SELECT FROM users WHERE NOT (id = token_parameters.user_id)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/Parameter match clauses cannot be used here/); }); test('invalid parameter match clause (3)', () => { // May be supported in the future const sql = 'SELECT FROM users WHERE token_parameters.start_at < users.created_at'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/Cannot use table values and parameters in the same clauses/); }); test('invalid parameter match clause (4)', () => { const sql = 'SELECT FROM users WHERE json_extract(users.description, token_parameters.path)'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/Cannot use table values and parameters in the same clauses/); }); test('invalid parameter match clause (5)', () => { const sql = 'SELECT (user_parameters.role = posts.roles) as r FROM posts'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/Parameter match expression is not allowed here/); }); test('invalid function schema', () => { const sql = 'SELECT FROM users WHERE something.length(users.id) = 0'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors[0].message).toMatch(/Function 'something.length' is not defined/); }); test('validate columns', () => { const schema = BASIC_SCHEMA; - const q1 = SqlParameterQuery.fromSql( - 'q4', - 'SELECT id FROM assets WHERE owner_id = token_parameters.user_id', + const q1 = SqlParameterQuery.fromSql('q4', 'SELECT id FROM assets WHERE owner_id = token_parameters.user_id', { + ...PARSE_OPTIONS, schema - ); + }); expect(q1.errors).toMatchObject([]); const q2 = SqlParameterQuery.fromSql( 'q5', 'SELECT id as asset_id FROM assets WHERE other_id = token_parameters.user_id', - schema + { ...PARSE_OPTIONS, schema } ); expect(q2.errors).toMatchObject([ @@ -649,7 +652,7 @@ describe('parameter queries', () => { describe('dangerous queries', function () { function testDangerousQuery(sql: string) { test(sql, function () { - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: @@ -661,7 +664,7 @@ describe('parameter queries', () => { } function testSafeQuery(sql: string) { test(sql, function () { - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.usesDangerousRequestParameters).toEqual(false); }); diff --git a/packages/sync-rules/test/src/static_parameter_queries.test.ts b/packages/sync-rules/test/src/static_parameter_queries.test.ts index d030b6f25..3e8b7fbfe 100644 --- a/packages/sync-rules/test/src/static_parameter_queries.test.ts +++ b/packages/sync-rules/test/src/static_parameter_queries.test.ts @@ -1,12 +1,12 @@ import { describe, expect, test } from 'vitest'; import { SqlParameterQuery } from '../../src/index.js'; import { StaticSqlParameterQuery } from '../../src/StaticSqlParameterQuery.js'; -import { normalizeTokenParameters } from './util.js'; +import { normalizeTokenParameters, PARSE_OPTIONS } from './util.js'; describe('static parameter queries', () => { test('basic query', function () { const sql = 'SELECT token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.bucket_parameters!).toEqual(['user_id']); expect(query.getStaticBucketIds(normalizeTokenParameters({ user_id: 'user1' }))).toEqual(['mybucket["user1"]']); @@ -14,7 +14,7 @@ describe('static parameter queries', () => { test('global query', function () { const sql = 'SELECT'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.bucket_parameters!).toEqual([]); expect(query.getStaticBucketIds(normalizeTokenParameters({ user_id: 'user1' }))).toEqual(['mybucket[]']); @@ -22,7 +22,7 @@ describe('static parameter queries', () => { test('query with filter', function () { const sql = 'SELECT token_parameters.user_id WHERE token_parameters.is_admin'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(normalizeTokenParameters({ user_id: 'user1', is_admin: true }))).toEqual([ 'mybucket["user1"]' @@ -32,7 +32,7 @@ describe('static parameter queries', () => { test('function in select clause', function () { const sql = 'SELECT upper(token_parameters.user_id) as upper_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(normalizeTokenParameters({ user_id: 'user1' }))).toEqual(['mybucket["USER1"]']); expect(query.bucket_parameters!).toEqual(['upper_id']); @@ -40,7 +40,7 @@ describe('static parameter queries', () => { test('function in filter clause', function () { const sql = "SELECT WHERE upper(token_parameters.role) = 'ADMIN'"; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(normalizeTokenParameters({ role: 'admin' }))).toEqual(['mybucket[]']); expect(query.getStaticBucketIds(normalizeTokenParameters({ role: 'user' }))).toEqual([]); @@ -48,7 +48,7 @@ describe('static parameter queries', () => { test('comparison in filter clause', function () { const sql = 'SELECT WHERE token_parameters.id1 = token_parameters.id2'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(normalizeTokenParameters({ id1: 't1', id2: 't1' }))).toEqual(['mybucket[]']); expect(query.getStaticBucketIds(normalizeTokenParameters({ id1: 't1', id2: 't2' }))).toEqual([]); @@ -56,7 +56,8 @@ describe('static parameter queries', () => { test('request.parameters()', function () { const sql = "SELECT request.parameters() ->> 'org_id' as org_id"; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, { + const query = SqlParameterQuery.fromSql('mybucket', sql, { + ...PARSE_OPTIONS, accept_potentially_dangerous_queries: true }) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); @@ -66,7 +67,7 @@ describe('static parameter queries', () => { test('request.jwt()', function () { const sql = "SELECT request.jwt() ->> 'sub' as user_id"; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.bucket_parameters).toEqual(['user_id']); @@ -75,7 +76,7 @@ describe('static parameter queries', () => { test('request.user_id()', function () { const sql = 'SELECT request.user_id() as user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.bucket_parameters).toEqual(['user_id']); @@ -85,7 +86,7 @@ describe('static parameter queries', () => { describe('dangerous queries', function () { function testDangerousQuery(sql: string) { test(sql, function () { - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: @@ -97,7 +98,7 @@ describe('static parameter queries', () => { } function testSafeQuery(sql: string) { test(sql, function () { - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toEqual([]); expect(query.usesDangerousRequestParameters).toEqual(false); }); diff --git a/packages/sync-rules/test/src/sync_rules.test.ts b/packages/sync-rules/test/src/sync_rules.test.ts index e5fafd1f9..089cdbf36 100644 --- a/packages/sync-rules/test/src/sync_rules.test.ts +++ b/packages/sync-rules/test/src/sync_rules.test.ts @@ -1,6 +1,5 @@ import { describe, expect, test } from 'vitest'; import { - DEFAULT_SCHEMA, DEFAULT_TAG, DartSchemaGenerator, JsLegacySchemaGenerator, @@ -9,21 +8,24 @@ import { TsSchemaGenerator } from '../../src/index.js'; -import { ASSETS, BASIC_SCHEMA, TestSourceTable, USERS, normalizeTokenParameters } from './util.js'; +import { ASSETS, BASIC_SCHEMA, PARSE_OPTIONS, TestSourceTable, USERS, normalizeTokenParameters } from './util.js'; describe('sync rules', () => { test('parse empty sync rules', () => { - const rules = SqlSyncRules.fromYaml('bucket_definitions: {}'); + const rules = SqlSyncRules.fromYaml('bucket_definitions: {}', PARSE_OPTIONS); expect(rules.bucket_descriptors).toEqual([]); }); test('parse global sync rules', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT id, description FROM assets - `); + `, + PARSE_OPTIONS + ); const bucket = rules.bucket_descriptors[0]; expect(bucket.name).toEqual('mybucket'); expect(bucket.bucket_parameters).toEqual([]); @@ -46,12 +48,15 @@ bucket_definitions: }); test('parse global sync rules with filter', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT WHERE token_parameters.is_admin data: [] - `); + `, + PARSE_OPTIONS + ); const bucket = rules.bucket_descriptors[0]; expect(bucket.bucket_parameters).toEqual([]); const param_query = bucket.global_parameter_queries[0]; @@ -66,12 +71,15 @@ bucket_definitions: }); test('parse global sync rules with table filter', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT FROM users WHERE users.id = token_parameters.user_id AND users.is_admin data: [] - `); + `, + PARSE_OPTIONS + ); const bucket = rules.bucket_descriptors[0]; expect(bucket.bucket_parameters).toEqual([]); const param_query = bucket.parameter_queries[0]; @@ -86,13 +94,16 @@ bucket_definitions: }); test('parse bucket with parameters', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.user_id, user_parameters.device_id data: - SELECT id, description FROM assets WHERE assets.user_id = bucket.user_id AND assets.device_id = bucket.device_id AND NOT assets.archived - `); + `, + PARSE_OPTIONS + ); const bucket = rules.bucket_descriptors[0]; expect(bucket.bucket_parameters).toEqual(['user_id', 'device_id']); const param_query = bucket.global_parameter_queries[0]; @@ -129,13 +140,16 @@ bucket_definitions: }); test('parse bucket with parameters and OR condition', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.user_id data: - SELECT id, description FROM assets WHERE assets.user_id = bucket.user_id OR assets.owner_id = bucket.user_id - `); + `, + PARSE_OPTIONS + ); const bucket = rules.bucket_descriptors[0]; expect(bucket.bucket_parameters).toEqual(['user_id']); const param_query = bucket.global_parameter_queries[0]; @@ -182,80 +196,104 @@ bucket_definitions: test('parse bucket with parameters and invalid OR condition', () => { expect(() => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.user_id data: - SELECT id, description FROM assets WHERE assets.user_id = bucket.user_id AND (assets.user_id = bucket.foo OR assets.other_id = bucket.bar) - `); + `, + PARSE_OPTIONS + ); }).toThrowError(/must use the same parameters/); }); test('reject unsupported queries', () => { expect( - SqlSyncRules.validate(` + SqlSyncRules.validate( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.user_id LIMIT 1 data: [] - `) + `, + PARSE_OPTIONS + ) ).toMatchObject([{ message: 'LIMIT is not supported' }]); expect( - SqlSyncRules.validate(` + SqlSyncRules.validate( + ` bucket_definitions: mybucket: data: - SELECT DISTINCT id, description FROM assets - `) + `, + PARSE_OPTIONS + ) ).toMatchObject([{ message: 'DISTINCT is not supported' }]); expect( - SqlSyncRules.validate(` + SqlSyncRules.validate( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.user_id OFFSET 10 data: [] - `) + `, + PARSE_OPTIONS + ) ).toMatchObject([{ message: 'LIMIT is not supported' }]); expect(() => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.user_id FOR UPDATE SKIP LOCKED data: [] - `); + `, + PARSE_OPTIONS + ); }).toThrowError(/SKIP is not supported/); expect(() => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.user_id FOR UPDATE data: [] - `); + `, + PARSE_OPTIONS + ); }).toThrowError(/FOR is not supported/); expect(() => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT id, description FROM assets ORDER BY id - `); + `, + PARSE_OPTIONS + ); }).toThrowError(/ORDER BY is not supported/); }); test('transforming things', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT upper(token_parameters.user_id) AS user_id data: - SELECT id, upper(description) AS description_upper FROM assets WHERE upper(assets.user_id) = bucket.user_id AND NOT assets.archived - `); + `, + PARSE_OPTIONS + ); const bucket = rules.bucket_descriptors[0]; expect(bucket.bucket_parameters).toEqual(['user_id']); expect(rules.getStaticBucketIds(normalizeTokenParameters({ user_id: 'user1' }))).toEqual(['mybucket["USER1"]']); @@ -281,13 +319,16 @@ bucket_definitions: test('transforming things with upper-case functions', () => { // Testing that we can use different case for the function names - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT UPPER(token_parameters.user_id) AS user_id data: - SELECT id, UPPER(description) AS description_upper FROM assets WHERE UPPER(assets.user_id) = bucket.user_id AND NOT assets.archived - `); + `, + PARSE_OPTIONS + ); const bucket = rules.bucket_descriptors[0]; expect(bucket.bucket_parameters).toEqual(['user_id']); expect(rules.getStaticBucketIds(normalizeTokenParameters({ user_id: 'user1' }))).toEqual(['mybucket["USER1"]']); @@ -312,12 +353,15 @@ bucket_definitions: }); test('transforming json', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT id, data ->> 'count' AS count, data -> 'bool' AS bool1, data ->> 'bool' AS bool2, 'true' ->> '$' as bool3, json_extract(data, '$.bool') AS bool4 FROM assets - `); + `, + PARSE_OPTIONS + ); expect( rules.evaluateRow({ sourceTable: ASSETS, @@ -342,13 +386,16 @@ bucket_definitions: }); test('IN json', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.region_id data: - SELECT id, description FROM assets WHERE bucket.region_id IN assets.region_ids - `); + `, + PARSE_OPTIONS + ); expect( rules.evaluateRow({ @@ -384,14 +431,17 @@ bucket_definitions: }); test('direct boolean param', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.is_admin data: - SELECT id, description, role, 'admin' as rule FROM assets WHERE bucket.is_admin - SELECT id, description, role, 'normal' as rule FROM assets WHERE (bucket.is_admin OR bucket.is_admin = false) AND assets.role != 'admin' - `); + `, + PARSE_OPTIONS + ); expect( rules.evaluateRow({ sourceTable: ASSETS, record: { id: 'asset1', description: 'test', role: 'admin' } }) @@ -455,12 +505,15 @@ bucket_definitions: }); test('some math', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT id, (5 / 2) AS int, (5 / 2.0) AS float, (CAST(5 AS real) / 2) AS float2 FROM assets - `); + `, + PARSE_OPTIONS + ); expect(rules.evaluateRow({ sourceTable: ASSETS, record: { id: 'asset1' } })).toEqual([ { @@ -479,13 +532,16 @@ bucket_definitions: }); test('bucket with static numeric parameters', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT token_parameters.int1, token_parameters.float1, token_parameters.float2 data: - SELECT id FROM assets WHERE assets.int1 = bucket.int1 AND assets.float1 = bucket.float1 AND assets.float2 = bucket.float2 - `); + `, + PARSE_OPTIONS + ); expect(rules.getStaticBucketIds(normalizeTokenParameters({ int1: 314, float1: 3.14, float2: 314 }))).toEqual([ 'mybucket[314,3.14,314]' ]); @@ -506,24 +562,30 @@ bucket_definitions: }); test('static parameter query with function on token_parameter', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: parameters: SELECT upper(token_parameters.user_id) as upper data: [] - `); + `, + PARSE_OPTIONS + ); expect(rules.errors).toEqual([]); expect(rules.getStaticBucketIds(normalizeTokenParameters({ user_id: 'test' }))).toEqual(['mybucket["TEST"]']); }); test('custom table and id', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT client_id AS id, description FROM assets_123 as assets WHERE assets.archived = false - SELECT other_id AS id, description FROM assets_123 as assets - `); + `, + PARSE_OPTIONS + ); expect( rules.evaluateRow({ @@ -555,12 +617,15 @@ bucket_definitions: }); test('wildcard table', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT client_id AS id, description, _table_suffix as suffix, * FROM "assets_%" as assets WHERE assets.archived = false AND _table_suffix > '100' - `); + `, + PARSE_OPTIONS + ); expect( rules.evaluateRow({ @@ -586,12 +651,15 @@ bucket_definitions: }); test('wildcard without alias', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT *, _table_suffix as suffix, * FROM "%" WHERE archived = false - `); + `, + PARSE_OPTIONS + ); expect( rules.evaluateRow({ @@ -615,7 +683,8 @@ bucket_definitions: }); test('should filter schemas', () => { - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: @@ -624,7 +693,9 @@ bucket_definitions: - SELECT id FROM "default.public"."assets" # yes - SELECT id FROM "other"."assets" # no - SELECT id FROM "other.public"."assets" # no - `); + `, + PARSE_OPTIONS + ); expect( rules.evaluateRow({ @@ -670,7 +741,7 @@ bucket_definitions: parameters: SELECT id FROM assets WHERE other_id = token_parameters.user_id data: [] `, - { schema: BASIC_SCHEMA } + { schema: BASIC_SCHEMA, ...PARSE_OPTIONS } ); expect(rules.errors).toMatchObject([ @@ -689,7 +760,7 @@ bucket_definitions: parameters: SELECT request.parameters() ->> 'project_id' as project_id data: [] `, - { schema: BASIC_SCHEMA } + { schema: BASIC_SCHEMA, ...PARSE_OPTIONS } ); expect(rules.errors).toMatchObject([ @@ -710,7 +781,7 @@ bucket_definitions: parameters: SELECT request.parameters() ->> 'project_id' as project_id data: [] `, - { schema: BASIC_SCHEMA } + { schema: BASIC_SCHEMA, ...PARSE_OPTIONS } ); expect(rules.errors).toEqual([]); @@ -722,7 +793,7 @@ bucket_definitions: tag: DEFAULT_TAG, schemas: [ { - name: DEFAULT_SCHEMA, + name: 'public', tables: [ { name: 'assets', @@ -739,14 +810,17 @@ bucket_definitions: } ]); - const rules = SqlSyncRules.fromYaml(` + const rules = SqlSyncRules.fromYaml( + ` bucket_definitions: mybucket: data: - SELECT * FROM assets as assets1 - SELECT id, name, count FROM assets as assets2 - SELECT id, owner_id as other_id, foo FROM assets as ASSETS2 - `); + `, + PARSE_OPTIONS + ); expect(new DartSchemaGenerator().generate(rules, schema)).toEqual(`Schema([ Table('assets1', [ diff --git a/packages/sync-rules/test/src/util.ts b/packages/sync-rules/test/src/util.ts index 1b4b12117..e6feebf75 100644 --- a/packages/sync-rules/test/src/util.ts +++ b/packages/sync-rules/test/src/util.ts @@ -1,5 +1,4 @@ import { - DEFAULT_SCHEMA, DEFAULT_TAG, RequestJwtPayload, RequestParameters, @@ -9,11 +8,15 @@ import { export class TestSourceTable implements SourceTableInterface { readonly connectionTag = DEFAULT_TAG; - readonly schema = DEFAULT_SCHEMA; + readonly schema = 'public'; constructor(public readonly table: string) {} } +export const PARSE_OPTIONS = { + defaultSchema: 'public' +}; + export const ASSETS = new TestSourceTable('assets'); export const USERS = new TestSourceTable('users'); @@ -22,7 +25,7 @@ export const BASIC_SCHEMA = new StaticSchema([ tag: DEFAULT_TAG, schemas: [ { - name: DEFAULT_SCHEMA, + name: 'public', tables: [ { name: 'assets', From 22b95cefd4ccde4e89567b8ad41d6f9950cd0623 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 14:31:46 +0200 Subject: [PATCH 175/298] Use 'test_schema' instead of 'public' for tests. --- packages/sync-rules/test/src/data_queries.test.ts | 2 +- packages/sync-rules/test/src/sync_rules.test.ts | 8 ++++---- packages/sync-rules/test/src/util.ts | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/sync-rules/test/src/data_queries.test.ts b/packages/sync-rules/test/src/data_queries.test.ts index 00ad2a3ec..a70a63b30 100644 --- a/packages/sync-rules/test/src/data_queries.test.ts +++ b/packages/sync-rules/test/src/data_queries.test.ts @@ -144,7 +144,7 @@ describe('data queries', () => { ); expect(q3.errors).toMatchObject([ { - message: `Table public.nope not found`, + message: `Table test_schema.nope not found`, type: 'warning' } ]); diff --git a/packages/sync-rules/test/src/sync_rules.test.ts b/packages/sync-rules/test/src/sync_rules.test.ts index 089cdbf36..c7e88ad46 100644 --- a/packages/sync-rules/test/src/sync_rules.test.ts +++ b/packages/sync-rules/test/src/sync_rules.test.ts @@ -689,10 +689,10 @@ bucket_definitions: mybucket: data: - SELECT id FROM "assets" # Yes - - SELECT id FROM "public"."assets" # yes - - SELECT id FROM "default.public"."assets" # yes + - SELECT id FROM "test_schema"."assets" # yes + - SELECT id FROM "default.test_schema"."assets" # yes - SELECT id FROM "other"."assets" # no - - SELECT id FROM "other.public"."assets" # no + - SELECT id FROM "other.test_schema"."assets" # no `, PARSE_OPTIONS ); @@ -793,7 +793,7 @@ bucket_definitions: tag: DEFAULT_TAG, schemas: [ { - name: 'public', + name: 'test_schema', tables: [ { name: 'assets', diff --git a/packages/sync-rules/test/src/util.ts b/packages/sync-rules/test/src/util.ts index e6feebf75..e1ed5b804 100644 --- a/packages/sync-rules/test/src/util.ts +++ b/packages/sync-rules/test/src/util.ts @@ -8,13 +8,13 @@ import { export class TestSourceTable implements SourceTableInterface { readonly connectionTag = DEFAULT_TAG; - readonly schema = 'public'; + readonly schema = 'test_schema'; constructor(public readonly table: string) {} } export const PARSE_OPTIONS = { - defaultSchema: 'public' + defaultSchema: 'test_schema' }; export const ASSETS = new TestSourceTable('assets'); @@ -25,7 +25,7 @@ export const BASIC_SCHEMA = new StaticSchema([ tag: DEFAULT_TAG, schemas: [ { - name: 'public', + name: 'test_schema', tables: [ { name: 'assets', From ba4fab41fe687b75935807c15da21284b1ed1c7a Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 14:32:39 +0200 Subject: [PATCH 176/298] Expand comment. --- packages/sync-rules/src/SqlSyncRules.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/sync-rules/src/SqlSyncRules.ts b/packages/sync-rules/src/SqlSyncRules.ts index d93292b49..10c0de3b4 100644 --- a/packages/sync-rules/src/SqlSyncRules.ts +++ b/packages/sync-rules/src/SqlSyncRules.ts @@ -28,6 +28,8 @@ const ACCEPT_POTENTIALLY_DANGEROUS_QUERIES = Symbol('ACCEPT_POTENTIALLY_DANGEROU export interface SyncRulesOptions { schema?: SourceSchema; /** + * The default schema to use when only a table name is specified. + * * 'public' for Postgres, default database for MongoDB/MySQL. */ defaultSchema: string; From cf142712e9baa1a3cf43ec293e67223175c21738 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 14:42:27 +0200 Subject: [PATCH 177/298] Tweaks for ReplicaId. --- .../service-core/src/storage/BucketStorage.ts | 4 +-- .../src/storage/mongo/OperationBatch.ts | 3 +- .../src/storage/mongo/PersistedBatch.ts | 4 +-- .../service-core/src/storage/mongo/models.ts | 10 +++++- .../service-core/src/storage/mongo/util.ts | 31 ++++++++++++------- 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 03a29dd57..f2117f673 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -10,7 +10,7 @@ import { import * as util from '../util/util-index.js'; import { SourceTable } from './SourceTable.js'; import { SourceEntityDescriptor } from './SourceEntity.js'; -import * as bson from 'bson'; +import { ReplicaId } from './storage-index.js'; export interface BucketStorageFactory { /** @@ -359,8 +359,6 @@ export interface SaveBucketData { export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete; -export type ReplicaId = string | bson.UUID | bson.Document | any; - export interface SaveInsert { tag: 'insert'; sourceTable: SourceTable; diff --git a/packages/service-core/src/storage/mongo/OperationBatch.ts b/packages/service-core/src/storage/mongo/OperationBatch.ts index 74b4b651f..4bcc0e64a 100644 --- a/packages/service-core/src/storage/mongo/OperationBatch.ts +++ b/packages/service-core/src/storage/mongo/OperationBatch.ts @@ -1,8 +1,9 @@ import { ToastableSqliteRow } from '@powersync/service-sync-rules'; import * as bson from 'bson'; -import { ReplicaId, SaveOptions } from '../BucketStorage.js'; +import { SaveOptions } from '../BucketStorage.js'; import { isUUID } from './util.js'; +import { ReplicaId } from './models.js'; /** * Maximum number of operations in a batch. diff --git a/packages/service-core/src/storage/mongo/PersistedBatch.ts b/packages/service-core/src/storage/mongo/PersistedBatch.ts index 0e5d2fabc..076c42259 100644 --- a/packages/service-core/src/storage/mongo/PersistedBatch.ts +++ b/packages/service-core/src/storage/mongo/PersistedBatch.ts @@ -13,11 +13,11 @@ import { BucketParameterDocument, CurrentBucket, CurrentDataDocument, - SourceKey + SourceKey, + ReplicaId } from './models.js'; import { serializeLookup } from './util.js'; import { logger } from '@powersync/lib-services-framework'; -import { ReplicaId } from '../BucketStorage.js'; /** * Maximum size of operations we write in a single transaction. diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index 19dbf0fbc..fa52a37da 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -1,6 +1,14 @@ import * as bson from 'bson'; import { SqliteJsonValue } from '@powersync/service-sync-rules'; -import { ReplicaId } from '../BucketStorage.js'; + +/** + * Replica id uniquely identifying a row on the source database. + * + * Can be any value serializable to BSON. + * + * If the value is an entire document, the data serialized to a v5 UUID may be a good choice here. + */ +export type ReplicaId = bson.UUID | bson.Document | any; export interface SourceKey { /** group_id */ diff --git a/packages/service-core/src/storage/mongo/util.ts b/packages/service-core/src/storage/mongo/util.ts index 58a9bc36c..c40533f79 100644 --- a/packages/service-core/src/storage/mongo/util.ts +++ b/packages/service-core/src/storage/mongo/util.ts @@ -1,12 +1,11 @@ import { SqliteJsonValue } from '@powersync/service-sync-rules'; import * as bson from 'bson'; -import * as mongo from 'mongodb'; import * as crypto from 'crypto'; -import { BucketDataDocument } from './models.js'; -import { ID_NAMESPACE, timestampToOpId } from '../../util/utils.js'; -import { OplogEntry } from '../../util/protocol-types.js'; -import { ReplicaId } from '../BucketStorage.js'; +import * as mongo from 'mongodb'; import * as uuid from 'uuid'; +import { OplogEntry } from '../../util/protocol-types.js'; +import { ID_NAMESPACE, timestampToOpId } from '../../util/utils.js'; +import { BucketDataDocument, ReplicaId } from './models.js'; /** * Lookup serialization must be number-agnostic. I.e. normalize numbers, instead of preserving numbers. @@ -100,7 +99,7 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { object_type: row.table, object_id: row.row_id, checksum: Number(row.checksum), - subkey: `${row.source_table}/${replicaIdToSubkey(row.source_key!)}`, + subkey: replicaIdToSubkey(row.source_table!, row.source_key!), data: row.data }; } else { @@ -114,6 +113,9 @@ export function mapOpEntry(row: BucketDataDocument): OplogEntry { } } +/** + * Returns true if two ReplicaId values are the same (serializes to the same BSON value). + */ export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { if (a === b) { return true; @@ -126,23 +128,28 @@ export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { } else if (a != null || b != null) { return false; } else { - return (bson.serialize(a) as Buffer).equals(bson.serialize(b)); + // There are many possible primitive values, this covers them all + return (bson.serialize({ id: a }) as Buffer).equals(bson.serialize({ id: b })); } } -export function replicaIdToSubkey(id: ReplicaId) { +export function replicaIdToSubkey(table: bson.ObjectId, id: ReplicaId): string { if (isUUID(id)) { // Special case for UUID for backwards-compatiblity + `${table.toHexString()}/${id.toHexString()}`; return id.toHexString(); - } else if (typeof id == 'string') { - return id; } else { - const repr = bson.serialize({ id: id }); + // Hashed UUID from the table and id + const repr = bson.serialize({ table, id }); return uuid.v5(repr, ID_NAMESPACE); - return; } } +/** + * True if this is a bson.UUID. + * + * Works even with multiple copies of the bson package. + */ export function isUUID(value: any): value is bson.UUID { if (value == null || typeof value != 'object') { return false; From ca2c97da3e672c2eb2ed2d7afbb31ed83035bfdf Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 15:26:45 +0200 Subject: [PATCH 178/298] Fix subkey calculation & tests. --- .../src/storage/mongo/PersistedBatch.ts | 4 +- .../service-core/src/storage/mongo/util.ts | 3 +- .../test/src/__snapshots__/sync.test.ts.snap | 14 +- .../service-core/test/src/compacting.test.ts | 37 +- .../test/src/data_storage.test.ts | 402 +++++++++++------- packages/service-core/test/src/sync.test.ts | 67 ++- packages/service-core/test/src/util.ts | 50 ++- 7 files changed, 381 insertions(+), 196 deletions(-) diff --git a/packages/service-core/src/storage/mongo/PersistedBatch.ts b/packages/service-core/src/storage/mongo/PersistedBatch.ts index 076c42259..58b0be659 100644 --- a/packages/service-core/src/storage/mongo/PersistedBatch.ts +++ b/packages/service-core/src/storage/mongo/PersistedBatch.ts @@ -16,7 +16,7 @@ import { SourceKey, ReplicaId } from './models.js'; -import { serializeLookup } from './util.js'; +import { replicaIdToSubkey, serializeLookup } from './util.js'; import { logger } from '@powersync/lib-services-framework'; /** @@ -71,7 +71,7 @@ export class PersistedBatch { remaining_buckets.set(key, b); } - const dchecksum = util.hashDelete(`${options.table.id}/${options.sourceKey}`); + const dchecksum = util.hashDelete(replicaIdToSubkey(options.table.id, options.sourceKey)); for (let k of options.evaluated) { const key = currentBucketKey(k); diff --git a/packages/service-core/src/storage/mongo/util.ts b/packages/service-core/src/storage/mongo/util.ts index c40533f79..d6b36a7dc 100644 --- a/packages/service-core/src/storage/mongo/util.ts +++ b/packages/service-core/src/storage/mongo/util.ts @@ -136,8 +136,7 @@ export function replicaIdEquals(a: ReplicaId, b: ReplicaId) { export function replicaIdToSubkey(table: bson.ObjectId, id: ReplicaId): string { if (isUUID(id)) { // Special case for UUID for backwards-compatiblity - `${table.toHexString()}/${id.toHexString()}`; - return id.toHexString(); + return `${table.toHexString()}/${id.toHexString()}`; } else { // Hashed UUID from the table and id const repr = bson.serialize({ table, id }); diff --git a/packages/service-core/test/src/__snapshots__/sync.test.ts.snap b/packages/service-core/test/src/__snapshots__/sync.test.ts.snap index 806abf375..f1e9b6762 100644 --- a/packages/service-core/test/src/__snapshots__/sync.test.ts.snap +++ b/packages/service-core/test/src/__snapshots__/sync.test.ts.snap @@ -61,7 +61,7 @@ exports[`sync - mongodb > compacting data - invalidate checkpoint 2`] = ` "object_type": "test", "op": "PUT", "op_id": "3", - "subkey": "6544e3899293153fa7b38331/117ab485-4b42-58a2-ab32-0053a22c3423", + "subkey": "e5aa2ddc-1328-58fa-a000-0b5ed31eaf1a", }, { "checksum": 3028503153n, @@ -70,7 +70,7 @@ exports[`sync - mongodb > compacting data - invalidate checkpoint 2`] = ` "object_type": "test", "op": "PUT", "op_id": "4", - "subkey": "6544e3899293153fa7b38331/ec27c691-b47a-5d92-927a-9944feb89eee", + "subkey": "13423353-9f27-59b4-baf0-64a5e09f1769", }, ], "has_more": false, @@ -151,7 +151,7 @@ exports[`sync - mongodb > sync global data 1`] = ` "object_type": "test", "op": "PUT", "op_id": "1", - "subkey": "6544e3899293153fa7b38331/117ab485-4b42-58a2-ab32-0053a22c3423", + "subkey": "e5aa2ddc-1328-58fa-a000-0b5ed31eaf1a", }, { "checksum": 3280762209n, @@ -160,7 +160,7 @@ exports[`sync - mongodb > sync global data 1`] = ` "object_type": "test", "op": "PUT", "op_id": "2", - "subkey": "6544e3899293153fa7b38331/ec27c691-b47a-5d92-927a-9944feb89eee", + "subkey": "13423353-9f27-59b4-baf0-64a5e09f1769", }, ], "has_more": false, @@ -207,7 +207,7 @@ exports[`sync - mongodb > sync legacy non-raw data 1`] = ` "object_type": "test", "op": "PUT", "op_id": "1", - "subkey": "6544e3899293153fa7b38331/117ab485-4b42-58a2-ab32-0053a22c3423", + "subkey": "e5aa2ddc-1328-58fa-a000-0b5ed31eaf1a", }, ], "has_more": false, @@ -273,7 +273,7 @@ exports[`sync - mongodb > sync updates to global data 2`] = ` "object_type": "test", "op": "PUT", "op_id": "1", - "subkey": "6544e3899293153fa7b38331/117ab485-4b42-58a2-ab32-0053a22c3423", + "subkey": "e5aa2ddc-1328-58fa-a000-0b5ed31eaf1a", }, ], "has_more": false, @@ -316,7 +316,7 @@ exports[`sync - mongodb > sync updates to global data 3`] = ` "object_type": "test", "op": "PUT", "op_id": "2", - "subkey": "6544e3899293153fa7b38331/ec27c691-b47a-5d92-927a-9944feb89eee", + "subkey": "13423353-9f27-59b4-baf0-64a5e09f1769", }, ], "has_more": false, diff --git a/packages/service-core/test/src/compacting.test.ts b/packages/service-core/test/src/compacting.test.ts index e7cb6d313..5fc50a33a 100644 --- a/packages/service-core/test/src/compacting.test.ts +++ b/packages/service-core/test/src/compacting.test.ts @@ -3,7 +3,9 @@ import { SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { validateCompactedBucket } from './bucket_validation.js'; import { oneFromAsync } from './stream_utils.js'; -import { makeTestTable, MONGO_STORAGE_FACTORY, ZERO_LSN } from './util.js'; +import { BATCH_OPTIONS, makeTestTable, MONGO_STORAGE_FACTORY, rid, testRules, ZERO_LSN } from './util.js'; +import { ParseSyncRulesOptions, PersistedSyncRulesContent, StartBatchOptions } from '@/storage/BucketStorage.js'; +import { getUuidReplicaIdentityBson } from '@/util/util-index.js'; const TEST_TABLE = makeTestTable('test', ['id']); @@ -18,21 +20,22 @@ function compactTests(compactOptions: MongoCompactOptions) { const factory = MONGO_STORAGE_FACTORY; test('compacting (1)', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules(` bucket_definitions: global: data: [select * from test] `); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 't1' - } + }, + afterReplicaId: rid('t1') }); await batch.save({ @@ -40,7 +43,8 @@ bucket_definitions: tag: 'insert', after: { id: 't2' - } + }, + afterReplicaId: rid('t2') }); await batch.save({ @@ -48,7 +52,8 @@ bucket_definitions: tag: 'update', after: { id: 't2' - } + }, + afterReplicaId: rid('t2') }); }); @@ -108,21 +113,22 @@ bucket_definitions: }); test('compacting (2)', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules(` bucket_definitions: global: data: [select * from test] `); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 't1' - } + }, + afterReplicaId: rid('t1') }); await batch.save({ @@ -130,7 +136,8 @@ bucket_definitions: tag: 'insert', after: { id: 't2' - } + }, + afterReplicaId: rid('t2') }); await batch.save({ @@ -138,7 +145,8 @@ bucket_definitions: tag: 'delete', before: { id: 't1' - } + }, + beforeReplicaId: rid('t1') }); await batch.save({ @@ -146,7 +154,8 @@ bucket_definitions: tag: 'update', after: { id: 't2' - } + }, + afterReplicaId: rid('t2') }); }); diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index c2035148f..103ff4b4d 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -1,8 +1,25 @@ -import { BucketDataBatchOptions } from '@/storage/BucketStorage.js'; +import { + BucketDataBatchOptions, + ParseSyncRulesOptions, + PersistedSyncRulesContent, + StartBatchOptions +} from '@/storage/BucketStorage.js'; import { RequestParameters, SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { fromAsync, oneFromAsync } from './stream_utils.js'; -import { getBatchData, getBatchMeta, makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory, ZERO_LSN } from './util.js'; +import { + BATCH_OPTIONS, + getBatchData, + getBatchMeta, + makeTestTable, + MONGO_STORAGE_FACTORY, + PARSE_OPTIONS, + rid, + StorageFactory, + testRules, + ZERO_LSN +} from './util.js'; +import { getUuidReplicaIdentityBson } from '@/util/util-index.js'; const TEST_TABLE = makeTestTable('test', ['id']); @@ -12,7 +29,7 @@ describe('store - mongodb', function () { function defineDataStorageTests(factory: StorageFactory) { test('save and load parameters', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules(` bucket_definitions: mybucket: parameters: @@ -20,9 +37,9 @@ bucket_definitions: data: [] `); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -31,7 +48,8 @@ bucket_definitions: id1: 'user3', id2: 'user4', group_id: 'group2a' - } + }, + afterReplicaId: rid('t2') }); await batch.save({ @@ -42,7 +60,8 @@ bucket_definitions: id1: 'user1', id2: 'user2', group_id: 'group1a' - } + }, + afterReplicaId: rid('t1') }); }); @@ -55,34 +74,38 @@ bucket_definitions: }); test('it should use the latest version', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: mybucket: parameters: - SELECT group_id FROM test WHERE id = token_parameters.user_id data: [] - `); + ` + ); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result1 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 'user1', group_id: 'group1' - } + }, + afterReplicaId: rid('user1') }); }); - const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 'user1', group_id: 'group2' - } + }, + afterReplicaId: rid('user1') }); }); @@ -103,17 +126,19 @@ bucket_definitions: }); test('save and load parameters with different number types', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: mybucket: parameters: - SELECT group_id FROM test WHERE n1 = token_parameters.n1 and f2 = token_parameters.f2 and f3 = token_parameters.f3 data: [] - `); + ` + ); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -123,7 +148,8 @@ bucket_definitions: n1: 314n, f2: 314, f3: 3.14 - } + }, + afterReplicaId: rid('t1') }); }); @@ -144,17 +170,19 @@ bucket_definitions: // This specific case tested here cannot happen with postgres in practice, but we still // test this to ensure correct deserialization. - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: mybucket: parameters: - SELECT group_id FROM test WHERE n1 = token_parameters.n1 data: [] - `); + ` + ); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -162,7 +190,8 @@ bucket_definitions: id: 't1', group_id: 'group1', n1: 1152921504606846976n // 2^60 - } + }, + afterReplicaId: rid('t1') }); await batch.save({ @@ -174,7 +203,8 @@ bucket_definitions: // Simulate a TOAST value, even though it can't happen for values like this // in practice. n1: undefined - } + }, + afterReplicaId: rid('t1') }); }); @@ -187,15 +217,17 @@ bucket_definitions: }); test('removing row', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "%" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -204,14 +236,13 @@ bucket_definitions: after: { id: 'test1', description: 'test1' - } + }, + afterReplicaId: rid('test1') }); await batch.save({ sourceTable, tag: 'delete', - before: { - id: 'test1' - } + beforeReplicaId: rid('test1') }); }); @@ -247,25 +278,29 @@ bucket_definitions: test('save and load parameters with workspaceId', async () => { const WORKSPACE_TABLE = makeTestTable('workspace', ['id']); - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules_content = testRules( + ` bucket_definitions: by_workspace: parameters: - SELECT id as workspace_id FROM workspace WHERE workspace."userId" = token_parameters.user_id data: [] - `); + ` + ); + const sync_rules = sync_rules_content.parsed(PARSE_OPTIONS).sync_rules; - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules_content); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, tag: 'insert', after: { id: 'workspace1', userId: 'u1' - } + }, + afterReplicaId: rid('workspace1') }); }); @@ -293,25 +328,29 @@ bucket_definitions: test('save and load parameters with dynamic global buckets', async () => { const WORKSPACE_TABLE = makeTestTable('workspace'); - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules_content = testRules( + ` bucket_definitions: by_public_workspace: parameters: - SELECT id as workspace_id FROM workspace WHERE workspace.visibility = 'public' data: [] - `); + ` + ); + const sync_rules = sync_rules_content.parsed(PARSE_OPTIONS).sync_rules; - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules_content); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, tag: 'insert', after: { id: 'workspace1', visibility: 'public' - } + }, + afterReplicaId: rid('workspace1') }); await batch.save({ @@ -320,7 +359,8 @@ bucket_definitions: after: { id: 'workspace2', visibility: 'private' - } + }, + afterReplicaId: rid('workspace2') }); await batch.save({ @@ -329,7 +369,8 @@ bucket_definitions: after: { id: 'workspace3', visibility: 'public' - } + }, + afterReplicaId: rid('workspace3') }); }); @@ -359,7 +400,8 @@ bucket_definitions: test('multiple parameter queries', async () => { const WORKSPACE_TABLE = makeTestTable('workspace'); - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules_content = testRules( + ` bucket_definitions: by_workspace: parameters: @@ -368,18 +410,21 @@ bucket_definitions: - SELECT id as workspace_id FROM workspace WHERE workspace.user_id = token_parameters.user_id data: [] - `); + ` + ); + const sync_rules = sync_rules_content.parsed(PARSE_OPTIONS).sync_rules; - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules_content); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: WORKSPACE_TABLE, tag: 'insert', after: { id: 'workspace1', visibility: 'public' - } + }, + afterReplicaId: rid('workspace1') }); await batch.save({ @@ -388,7 +433,8 @@ bucket_definitions: after: { id: 'workspace2', visibility: 'private' - } + }, + afterReplicaId: rid('workspace2') }); await batch.save({ @@ -398,7 +444,8 @@ bucket_definitions: id: 'workspace3', user_id: 'u1', visibility: 'private' - } + }, + afterReplicaId: rid('workspace3') }); await batch.save({ @@ -408,7 +455,8 @@ bucket_definitions: id: 'workspace4', user_id: 'u2', visibility: 'private' - } + }, + afterReplicaId: rid('workspace4') }); }); @@ -445,16 +493,18 @@ bucket_definitions: }); test('changing client ids', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT client_id as id, description FROM "%" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); const sourceTable = TEST_TABLE; - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable, tag: 'insert', @@ -462,7 +512,8 @@ bucket_definitions: id: 'test1', client_id: 'client1a', description: 'test1a' - } + }, + afterReplicaId: rid('test1') }); await batch.save({ sourceTable, @@ -471,7 +522,8 @@ bucket_definitions: id: 'test1', client_id: 'client1b', description: 'test1b' - } + }, + afterReplicaId: rid('test1') }); await batch.save({ @@ -481,7 +533,8 @@ bucket_definitions: id: 'test2', client_id: 'client2', description: 'test2' - } + }, + afterReplicaId: rid('test2') }); }); const checkpoint = result!.flushed_op; @@ -502,15 +555,17 @@ bucket_definitions: }); test('re-apply delete', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "%" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -519,31 +574,28 @@ bucket_definitions: after: { id: 'test1', description: 'test1' - } + }, + afterReplicaId: rid('test1') }); }); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ sourceTable, tag: 'delete', - before: { - id: 'test1' - } + beforeReplicaId: rid('test1') }); }); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ sourceTable, tag: 'delete', - before: { - id: 'test1' - } + beforeReplicaId: rid('test1') }); }); @@ -577,15 +629,17 @@ bucket_definitions: }); test('re-apply update + delete', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "%" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -594,11 +648,12 @@ bucket_definitions: after: { id: 'test1', description: 'test1' - } + }, + afterReplicaId: rid('test1') }); }); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -607,7 +662,8 @@ bucket_definitions: after: { id: 'test1', description: undefined - } + }, + afterReplicaId: rid('test1') }); await batch.save({ @@ -616,19 +672,18 @@ bucket_definitions: after: { id: 'test1', description: undefined - } + }, + afterReplicaId: rid('test1') }); await batch.save({ sourceTable, tag: 'delete', - before: { - id: 'test1' - } + beforeReplicaId: rid('test1') }); }); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -637,7 +692,8 @@ bucket_definitions: after: { id: 'test1', description: undefined - } + }, + afterReplicaId: rid('test1') }); await batch.save({ @@ -646,15 +702,14 @@ bucket_definitions: after: { id: 'test1', description: undefined - } + }, + afterReplicaId: rid('test1') }); await batch.save({ sourceTable, tag: 'delete', - before: { - id: 'test1' - } + beforeReplicaId: rid('test1') }); }); @@ -691,17 +746,19 @@ bucket_definitions: }); test('truncate parameters', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: mybucket: parameters: - SELECT group_id FROM test WHERE id1 = token_parameters.user_id OR id2 = token_parameters.user_id data: [] - `); + ` + ); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -710,7 +767,8 @@ bucket_definitions: id1: 'user3', id2: 'user4', group_id: 'group2a' - } + }, + afterReplicaId: rid('t2') }); await batch.truncate([TEST_TABLE]); @@ -731,16 +789,18 @@ bucket_definitions: // 1. Not getting the correct "current_data" state for each operation. // 2. Output order not being correct. - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "test" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); // Pre-setup - const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result1 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; await batch.save({ @@ -749,7 +809,8 @@ bucket_definitions: after: { id: 'test1', description: 'test1a' - } + }, + afterReplicaId: rid('test1') }); await batch.save({ @@ -758,14 +819,15 @@ bucket_definitions: after: { id: 'test2', description: 'test2a' - } + }, + afterReplicaId: rid('test2') }); }); const checkpoint1 = result1?.flushed_op ?? '0'; // Test batch - const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; // b await batch.save({ @@ -774,7 +836,8 @@ bucket_definitions: after: { id: 'test1', description: 'test1b' - } + }, + afterReplicaId: rid('test1') }); await batch.save({ @@ -783,10 +846,12 @@ bucket_definitions: before: { id: 'test1' }, + beforeReplicaId: rid('test1'), after: { id: 'test2', description: 'test2b' - } + }, + afterReplicaId: rid('test2') }); await batch.save({ @@ -795,10 +860,13 @@ bucket_definitions: before: { id: 'test2' }, + beforeReplicaId: rid('test2'), after: { id: 'test3', description: 'test3b' - } + }, + + afterReplicaId: rid('test3') }); // c @@ -808,7 +876,8 @@ bucket_definitions: after: { id: 'test2', description: 'test2c' - } + }, + afterReplicaId: rid('test2') }); // d @@ -818,7 +887,8 @@ bucket_definitions: after: { id: 'test4', description: 'test4d' - } + }, + afterReplicaId: rid('test4') }); await batch.save({ @@ -827,10 +897,12 @@ bucket_definitions: before: { id: 'test4' }, + beforeReplicaId: rid('test4'), after: { id: 'test5', description: 'test5d' - } + }, + afterReplicaId: rid('test5') }); }); @@ -865,31 +937,40 @@ bucket_definitions: }); test('changed data with replica identity full', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "test" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + function rid2(id: string, description: string) { + return getUuidReplicaIdentityBson({ id, description }, [ + { name: 'id', type: 'VARCHAR', typeId: 25 }, + { name: 'description', type: 'VARCHAR', typeId: 25 } + ]); + } + const storage = (await factory()).getInstance(sync_rules); const sourceTable = makeTestTable('test', ['id', 'description']); // Pre-setup - const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result1 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable, tag: 'insert', after: { id: 'test1', description: 'test1a' - } + }, + afterReplicaId: rid2('test1', 'test1a') }); }); const checkpoint1 = result1?.flushed_op ?? '0'; - const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { // Unchanged, but has a before id await batch.save({ sourceTable, @@ -898,14 +979,16 @@ bucket_definitions: id: 'test1', description: 'test1a' }, + beforeReplicaId: rid2('test1', 'test1a'), after: { id: 'test1', description: 'test1b' - } + }, + afterReplicaId: rid2('test1', 'test1b') }); }); - const result3 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result3 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { // Delete await batch.save({ sourceTable, @@ -914,6 +997,7 @@ bucket_definitions: id: 'test1', description: 'test1b' }, + beforeReplicaId: rid2('test1', 'test1b'), after: undefined }); }); @@ -957,31 +1041,41 @@ bucket_definitions: }); test('unchanged data with replica identity full', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "test" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + function rid2(id: string, description: string) { + return getUuidReplicaIdentityBson({ id, description }, [ + { name: 'id', type: 'VARCHAR', typeId: 25 }, + { name: 'description', type: 'VARCHAR', typeId: 25 } + ]); + } + + const storage = (await factory()).getInstance(sync_rules); const sourceTable = makeTestTable('test', ['id', 'description']); // Pre-setup - const result1 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result1 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable, tag: 'insert', after: { id: 'test1', description: 'test1a' - } + }, + afterReplicaId: rid2('test1', 'test1a') }); }); const checkpoint1 = result1?.flushed_op ?? '0'; - const result2 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { // Unchanged, but has a before id await batch.save({ sourceTable, @@ -990,14 +1084,16 @@ bucket_definitions: id: 'test1', description: 'test1a' }, + beforeReplicaId: rid2('test1', 'test1a'), after: { id: 'test1', description: 'test1a' - } + }, + afterReplicaId: rid2('test1', 'test1a') }); }); - const result3 = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result3 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { // Delete await batch.save({ sourceTable, @@ -1006,6 +1102,7 @@ bucket_definitions: id: 'test1', description: 'test1a' }, + beforeReplicaId: rid2('test1', 'test1a'), after: undefined }); }); @@ -1046,15 +1143,17 @@ bucket_definitions: // but large enough in size to be split over multiple returned batches. // The specific batch splits is an implementation detail of the storage driver, // and the test will have to updated when other implementations are added. - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "%" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; const largeDescription = '0123456789'.repeat(12_000_00); @@ -1065,7 +1164,8 @@ bucket_definitions: after: { id: 'test1', description: 'test1' - } + }, + afterReplicaId: rid('test1') }); await batch.save({ @@ -1074,7 +1174,8 @@ bucket_definitions: after: { id: 'large1', description: largeDescription - } + }, + afterReplicaId: rid('large1') }); // Large enough to split the returned batch @@ -1084,7 +1185,8 @@ bucket_definitions: after: { id: 'large2', description: largeDescription - } + }, + afterReplicaId: rid('large2') }); await batch.save({ @@ -1093,7 +1195,8 @@ bucket_definitions: after: { id: 'test3', description: 'test3' - } + }, + afterReplicaId: rid('test3') }); }); @@ -1138,15 +1241,17 @@ bucket_definitions: // Test syncing a batch of data that is small in count, // but large enough in size to be split over multiple returned chunks. // Similar to the above test, but splits over 1MB chunks. - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "%" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; const largeDescription = '0123456789'.repeat(2_000_00); @@ -1157,7 +1262,8 @@ bucket_definitions: after: { id: 'test1', description: 'test1' - } + }, + afterReplicaId: rid('test1') }); await batch.save({ @@ -1166,7 +1272,8 @@ bucket_definitions: after: { id: 'large1', description: largeDescription - } + }, + afterReplicaId: rid('large1') }); // Large enough to split the returned batch @@ -1176,7 +1283,8 @@ bucket_definitions: after: { id: 'large2', description: largeDescription - } + }, + afterReplicaId: rid('large2') }); await batch.save({ @@ -1185,7 +1293,8 @@ bucket_definitions: after: { id: 'test3', description: 'test3' - } + }, + afterReplicaId: rid('test3') }); }); @@ -1227,15 +1336,17 @@ bucket_definitions: test('long batch', async () => { // Test syncing a batch of data that is limited by count. - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules( + ` bucket_definitions: global: data: - SELECT id, description FROM "%" -`); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); +` + ); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { const sourceTable = TEST_TABLE; for (let i = 1; i <= 6; i++) { @@ -1245,7 +1356,8 @@ bucket_definitions: after: { id: `test${i}`, description: `test${i}` - } + }, + afterReplicaId: `test${i}` }); } }); diff --git a/packages/service-core/test/src/sync.test.ts b/packages/service-core/test/src/sync.test.ts index c5e702633..f1c4c7272 100644 --- a/packages/service-core/test/src/sync.test.ts +++ b/packages/service-core/test/src/sync.test.ts @@ -5,7 +5,15 @@ import { JSONBig } from '@powersync/service-jsonbig'; import { RequestParameters } from '@powersync/service-sync-rules'; import * as timers from 'timers/promises'; import { describe, expect, test } from 'vitest'; -import { makeTestTable, MONGO_STORAGE_FACTORY, StorageFactory, ZERO_LSN } from './util.js'; +import { + BATCH_OPTIONS, + makeTestTable, + MONGO_STORAGE_FACTORY, + PARSE_OPTIONS, + StorageFactory, + ZERO_LSN +} from './util.js'; +import { ParseSyncRulesOptions, StartBatchOptions } from '@/storage/BucketStorage.js'; describe('sync - mongodb', function () { defineTests(MONGO_STORAGE_FACTORY); @@ -30,18 +38,19 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 't1', description: 'Test 1' - } + }, + afterReplicaId: 't1' }); await batch.save({ @@ -50,7 +59,8 @@ function defineTests(factory: StorageFactory) { after: { id: 't2', description: 'Test 2' - } + }, + afterReplicaId: 't2' }); await batch.commit('0/1'); @@ -63,6 +73,7 @@ function defineTests(factory: StorageFactory) { include_checksum: true, raw_data: true }, + parseOptions: PARSE_OPTIONS, tracker, syncParams: new RequestParameters({ sub: '' }, {}), token: { exp: Date.now() / 1000 + 10 } as any @@ -79,11 +90,11 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); - const result = await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', @@ -91,7 +102,8 @@ function defineTests(factory: StorageFactory) { id: 't1', description: 'Test\n"string"', large_num: 12345678901234567890n - } + }, + afterReplicaId: 't1' }); await batch.commit('0/1'); @@ -104,6 +116,7 @@ function defineTests(factory: StorageFactory) { include_checksum: true, raw_data: false }, + parseOptions: PARSE_OPTIONS, tracker, syncParams: new RequestParameters({ sub: '' }, {}), token: { exp: Date.now() / 1000 + 10 } as any @@ -122,7 +135,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); @@ -133,6 +146,7 @@ function defineTests(factory: StorageFactory) { include_checksum: true, raw_data: true }, + parseOptions: PARSE_OPTIONS, tracker, syncParams: new RequestParameters({ sub: '' }, {}), token: { exp: 0 } as any @@ -149,7 +163,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); @@ -160,6 +174,7 @@ function defineTests(factory: StorageFactory) { include_checksum: true, raw_data: true }, + parseOptions: PARSE_OPTIONS, tracker, syncParams: new RequestParameters({ sub: '' }, {}), token: { exp: Date.now() / 1000 + 10 } as any @@ -168,14 +183,15 @@ function defineTests(factory: StorageFactory) { expect(await getCheckpointLines(iter)).toMatchSnapshot(); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 't1', description: 'Test 1' - } + }, + afterReplicaId: 't1' }); await batch.commit('0/1'); @@ -183,14 +199,15 @@ function defineTests(factory: StorageFactory) { expect(await getCheckpointLines(iter)).toMatchSnapshot(); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 't2', description: 'Test 2' - } + }, + afterReplicaId: 't2' }); await batch.commit('0/2'); @@ -208,7 +225,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); @@ -221,6 +238,7 @@ function defineTests(factory: StorageFactory) { include_checksum: true, raw_data: true }, + parseOptions: PARSE_OPTIONS, tracker, syncParams: new RequestParameters({ sub: '' }, {}), token: { exp: exp } as any @@ -246,18 +264,19 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.setSnapshotDone(ZERO_LSN); await storage.autoActivate(); - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 't1', description: 'Test 1' - } + }, + afterReplicaId: 't1' }); await batch.save({ @@ -266,7 +285,8 @@ function defineTests(factory: StorageFactory) { after: { id: 't2', description: 'Test 2' - } + }, + afterReplicaId: 't2' }); await batch.commit('0/1'); @@ -279,6 +299,7 @@ function defineTests(factory: StorageFactory) { include_checksum: true, raw_data: true }, + parseOptions: PARSE_OPTIONS, tracker, syncParams: new RequestParameters({ sub: '' }, {}), token: { exp: Date.now() / 1000 + 10 } as any @@ -298,14 +319,15 @@ function defineTests(factory: StorageFactory) { // Now we save additional data AND compact before continuing. // This invalidates the checkpoint we've received above. - await storage.startBatch({ zeroLSN: ZERO_LSN }, async (batch) => { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'update', after: { id: 't1', description: 'Test 1b' - } + }, + afterReplicaId: 't1' }); await batch.save({ @@ -314,7 +336,8 @@ function defineTests(factory: StorageFactory) { after: { id: 't2', description: 'Test 2b' - } + }, + afterReplicaId: 't2' }); await batch.commit('0/2'); diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 4d11c3d5b..5acdd2f2e 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -1,13 +1,21 @@ import { Metrics } from '@/metrics/Metrics.js'; -import { BucketStorageFactory, SyncBucketDataBatch } from '@/storage/BucketStorage.js'; +import { + BucketStorageFactory, + ParseSyncRulesOptions, + PersistedSyncRulesContent, + StartBatchOptions, + SyncBucketDataBatch +} from '@/storage/BucketStorage.js'; import { MongoBucketStorage } from '@/storage/MongoBucketStorage.js'; import { SourceTable } from '@/storage/SourceTable.js'; import { PowerSyncMongo } from '@/storage/mongo/db.js'; import { SyncBucketData } from '@/util/protocol-types.js'; -import { hashData } from '@/util/utils.js'; +import { getUuidReplicaIdentityBson, hashData } from '@/util/utils.js'; import * as bson from 'bson'; import * as mongo from 'mongodb'; import { env } from './env.js'; +import { SqlSyncRules } from '@powersync/service-sync-rules'; +import { ReplicaId } from '@/storage/storage-index.js'; // The metrics need to be initialised before they can be used await Metrics.initialise({ @@ -27,6 +35,33 @@ export const MONGO_STORAGE_FACTORY: StorageFactory = async () => { export const ZERO_LSN = '0/0'; +export const PARSE_OPTIONS: ParseSyncRulesOptions = { + defaultSchema: 'public' +}; + +export const BATCH_OPTIONS: StartBatchOptions = { + ...PARSE_OPTIONS, + zeroLSN: ZERO_LSN +}; + +export function testRules(content: string): PersistedSyncRulesContent { + return { + id: 1, + sync_rules_content: content, + slot_name: 'test', + parsed(options) { + return { + id: 1, + sync_rules: SqlSyncRules.fromYaml(content, PARSE_OPTIONS), + slot_name: 'test' + }; + }, + lock() { + throw new Error('Not implemented'); + } + }; +} + export async function connectMongo() { // Short timeout for tests, to fail fast when the server is not available. // Slightly longer timeouts for CI, to avoid arbitrary test failures @@ -45,9 +80,9 @@ export function makeTestTable(name: string, columns?: string[] | undefined) { id, SourceTable.DEFAULT_TAG, relId, - SourceTable.DEFAULT_SCHEMA, + 'public', name, - (columns ?? ['id']).map((column) => ({ name: column, type: 'VARCHAR', typeOid: 25 })), + (columns ?? ['id']).map((column) => ({ name: column, type: 'VARCHAR', typeId: 25 })), true ); } @@ -93,3 +128,10 @@ function getFirst(batch: SyncBucketData[] | SyncBucketDataBatch[] | SyncBucketDa return first as SyncBucketData; } } + +/** + * Replica id in the old Postgres format, for backwards-compatible tests. + */ +export function rid(id: string): bson.UUID { + return getUuidReplicaIdentityBson({ id: id }, [{ name: 'id', type: 'VARCHAR', typeId: 25 }]); +} From 3ceae96c3521335e3e8a777f3caf836023284d92 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 15:32:36 +0200 Subject: [PATCH 179/298] Fix more tests. --- modules/module-postgres/test/src/slow_tests.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/module-postgres/test/src/slow_tests.test.ts b/modules/module-postgres/test/src/slow_tests.test.ts index aea33acf9..0b3ae38fc 100644 --- a/modules/module-postgres/test/src/slow_tests.test.ts +++ b/modules/module-postgres/test/src/slow_tests.test.ts @@ -83,7 +83,7 @@ bucket_definitions: - SELECT * FROM "test_data" `; const syncRules = await f.updateSyncRules({ content: syncRuleContent }); - const storage = f.getInstance(syncRules.parsed()); + const storage = f.getInstance(syncRules); abortController = new AbortController(); const options: WalStreamOptions = { abort_signal: abortController.signal, @@ -235,7 +235,7 @@ bucket_definitions: - SELECT id, description FROM "test_data" `; const syncRules = await f.updateSyncRules({ content: syncRuleContent }); - const storage = f.getInstance(syncRules.parsed()); + const storage = f.getInstance(syncRules); // 1. Setup some base data that will be replicated in initial replication await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); From e36f306336d5708d253188147ff0fa3bfdc3103f Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 16:07:29 +0200 Subject: [PATCH 180/298] Fix merge conflicts. --- packages/service-core/src/storage/mongo/PersistedBatch.ts | 1 - packages/service-core/src/storage/mongo/models.ts | 1 - pnpm-lock.yaml | 8 ++++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/service-core/src/storage/mongo/PersistedBatch.ts b/packages/service-core/src/storage/mongo/PersistedBatch.ts index 2d7654687..58b0be659 100644 --- a/packages/service-core/src/storage/mongo/PersistedBatch.ts +++ b/packages/service-core/src/storage/mongo/PersistedBatch.ts @@ -18,7 +18,6 @@ import { } from './models.js'; import { replicaIdToSubkey, serializeLookup } from './util.js'; import { logger } from '@powersync/lib-services-framework'; -import { ReplicaId } from '../BucketStorage.js'; /** * Maximum size of operations we write in a single transaction. diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index aeaffbebd..fa52a37da 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -1,6 +1,5 @@ import * as bson from 'bson'; import { SqliteJsonValue } from '@powersync/service-sync-rules'; -import { ReplicaId } from '../BucketStorage.js'; /** * Replica id uniquely identifying a row on the source database. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 738908a92..9a1f108ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,7 +113,7 @@ importers: version: link:../../packages/types mongodb: specifier: ^6.7.0 - version: 6.8.0(socks@2.8.3) + version: 6.7.0(socks@2.8.3) ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -132,7 +132,7 @@ importers: version: 5.2.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.2.2)(vite@5.3.3(@types/node@18.11.11)) + version: 4.3.2(typescript@5.2.2)(vite@5.2.11(@types/node@18.11.11)) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -5267,7 +5267,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.0 '@prisma/instrumentation': 5.15.0 '@sentry/core': 8.9.2 - '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) + '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/semantic-conventions@1.25.0) '@sentry/types': 8.9.2 '@sentry/utils': 8.9.2 optionalDependencies: @@ -5275,7 +5275,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': + '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/semantic-conventions@1.25.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) From 3facc2f3522523f298a1fa839f96a5637d9567b3 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 16:10:21 +0200 Subject: [PATCH 181/298] Fix Dockerfile. --- service/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/Dockerfile b/service/Dockerfile index 369abcc59..715931b11 100644 --- a/service/Dockerfile +++ b/service/Dockerfile @@ -18,6 +18,7 @@ COPY packages/types/package.json packages/types/tsconfig.json packages/types/ COPY libs/lib-services/package.json libs/lib-services/tsconfig.json libs/lib-services/ COPY modules/module-postgres/package.json modules/module-postgres/tsconfig.json modules/module-postgres/ +COPY modules/module-mongodb/package.json modules/module-mongodb/tsconfig.json modules/module-mongodb/ RUN pnpm install --frozen-lockfile @@ -34,6 +35,7 @@ COPY packages/types/src packages/types/src/ COPY libs/lib-services/src libs/lib-services/src/ COPY modules/module-postgres/src modules/module-postgres/src/ +COPY modules/module-mongodb/src modules/module-mongodb/src/ RUN pnpm build:production && \ rm -rf node_modules **/node_modules && \ From 505e728d142d48780dff0becc65b0061365e4ca7 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 16 Sep 2024 18:03:32 +0200 Subject: [PATCH 182/298] Fix and test mongo data type conversion. --- .../src/replication/ChangeStream.ts | 2 +- .../src/replication/MongoRelation.ts | 21 +- modules/module-mongodb/test/src/env.ts | 7 + .../test/src/mongo_test.test.ts | 235 ++++++++++++++++++ modules/module-mongodb/test/src/setup.ts | 7 + modules/module-mongodb/test/src/util.ts | 52 ++++ 6 files changed, 322 insertions(+), 2 deletions(-) create mode 100644 modules/module-mongodb/test/src/env.ts create mode 100644 modules/module-mongodb/test/src/mongo_test.test.ts create mode 100644 modules/module-mongodb/test/src/setup.ts create mode 100644 modules/module-mongodb/test/src/util.ts diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 150cb9970..5bc08ba63 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -182,7 +182,7 @@ export class ChangeStream { static *getQueryData(results: Iterable): Generator { for (let row of results) { - yield toSyncRulesRow(row); + yield constructAfterRecord(row); } } diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index d64e531f2..d3c48ea2b 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -56,6 +56,12 @@ export function toMongoSyncRulesValue(data: any): SqliteValue { return data.toHexString(); } else if (data instanceof mongo.UUID) { return data.toHexString(); + } else if (data instanceof Date) { + return data.toISOString().replace('T', ' '); + } else if (data instanceof mongo.Binary) { + return new Uint8Array(data.buffer); + } else if (data instanceof mongo.Long) { + return data.toBigInt(); } else if (Array.isArray(data)) { // We may be able to avoid some parse + stringify cycles here for JsonSqliteContainer. return JSONBig.stringify(data.map((element) => filterJsonData(element))); @@ -77,22 +83,35 @@ export function toMongoSyncRulesValue(data: any): SqliteValue { const DEPTH_LIMIT = 20; function filterJsonData(data: any, depth = 0): any { + const autoBigNum = true; if (depth > DEPTH_LIMIT) { // This is primarily to prevent infinite recursion throw new Error(`json nested object depth exceeds the limit of ${DEPTH_LIMIT}`); } if (data == null) { return data; // null or undefined - } else if (typeof data == 'string' || typeof data == 'number') { + } else if (typeof data == 'string') { return data; + } else if (typeof data == 'number') { + if (autoBigNum && Number.isInteger(data)) { + return BigInt(data); + } else { + return data; + } } else if (typeof data == 'boolean') { return data ? 1n : 0n; } else if (typeof data == 'bigint') { return data; + } else if (data instanceof Date) { + return data.toISOString().replace('T', ' '); } else if (data instanceof mongo.ObjectId) { return data.toHexString(); } else if (data instanceof mongo.UUID) { return data.toHexString(); + } else if (data instanceof mongo.Binary) { + return undefined; + } else if (data instanceof mongo.Long) { + return data.toBigInt(); } else if (Array.isArray(data)) { return data.map((element) => filterJsonData(element, depth + 1)); } else if (ArrayBuffer.isView(data)) { diff --git a/modules/module-mongodb/test/src/env.ts b/modules/module-mongodb/test/src/env.ts new file mode 100644 index 000000000..7d72df3a9 --- /dev/null +++ b/modules/module-mongodb/test/src/env.ts @@ -0,0 +1,7 @@ +import { utils } from '@powersync/lib-services-framework'; + +export const env = utils.collectEnvironmentVariables({ + MONGO_TEST_DATA_URL: utils.type.string.default('mongodb://localhost:27017/powersync_test'), + CI: utils.type.boolean.default('false'), + SLOW_TESTS: utils.type.boolean.default('false') +}); diff --git a/modules/module-mongodb/test/src/mongo_test.test.ts b/modules/module-mongodb/test/src/mongo_test.test.ts new file mode 100644 index 000000000..e4126c484 --- /dev/null +++ b/modules/module-mongodb/test/src/mongo_test.test.ts @@ -0,0 +1,235 @@ +import { ChangeStream } from '@module/replication/ChangeStream.js'; +import * as mongo from 'mongodb'; +import { describe, expect, test } from 'vitest'; +import { clearTestDb, connectMongoData } from './util.js'; + +describe('mongo data types', () => { + async function setupTable(db: mongo.Db) { + await clearTestDb(db); + } + + async function insert(collection: mongo.Collection) { + await collection.insertMany([ + { + _id: 1 as any, + null: null, + text: 'text', + uuid: new mongo.UUID('baeb2514-4c57-436d-b3cc-c1256211656d'), + bool: true, + bytea: Buffer.from('test'), + int2: 1000, + int4: 1000000, + int8: 9007199254740993n, + float: 3.14 + }, + { _id: 2 as any, nested: { test: 'thing' } }, + { _id: 3 as any, date: new Date('2023-03-06 15:47+02') }, + { + _id: 4 as any, + timestamp: mongo.Timestamp.fromBits(123, 456), + objectId: mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb') + } + ]); + } + + async function insertNested(collection: mongo.Collection) { + await collection.insertMany([ + { + _id: 1 as any, + null: [null], + text: ['text'], + uuid: [new mongo.UUID('baeb2514-4c57-436d-b3cc-c1256211656d')], + bool: [true], + bytea: [Buffer.from('test')], + int2: [1000], + int4: [1000000], + int8: [9007199254740993n], + float: [3.14] + }, + { _id: 2 as any, nested: [{ test: 'thing' }] }, + { _id: 3 as any, date: [new Date('2023-03-06 15:47+02')] }, + { + _id: 10 as any, + timestamp: [mongo.Timestamp.fromBits(123, 456)], + objectId: [mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')] + } + ]); + } + + function checkResults(transformed: Record[]) { + expect(transformed[0]).toMatchObject({ + _id: 1n, + text: 'text', + uuid: 'baeb2514-4c57-436d-b3cc-c1256211656d', + bool: 1n, + bytea: new Uint8Array([116, 101, 115, 116]), + int2: 1000n, + int4: 1000000n, + int8: 9007199254740993n, + float: 3.14, + null: null + }); + expect(transformed[1]).toMatchObject({ + _id: 2n, + nested: '{"test":"thing"}' + }); + + expect(transformed[2]).toMatchObject({ + _id: 3n, + date: '2023-03-06 13:47:00.000Z' + }); + + expect(transformed[3]).toMatchObject({ + _id: 4n, + objectId: '66e834cc91d805df11fa0ecb', + timestamp: 1958505087099n + }); + } + + function checkResultsNested(transformed: Record[]) { + expect(transformed[0]).toMatchObject({ + _id: 1n, + text: `["text"]`, + uuid: '["baeb2514-4c57-436d-b3cc-c1256211656d"]', + bool: '[1]', + bytea: '[null]', + int2: '[1000]', + int4: '[1000000]', + int8: `[9007199254740993]`, + float: '[3.14]', + null: '[null]' + }); + + // Note: Depending on to what extent we use the original postgres value, the whitespace may change, and order may change. + // We do expect that decimals and big numbers are preserved. + expect(transformed[1]).toMatchObject({ + _id: 2n, + nested: '[{"test":"thing"}]' + }); + + expect(transformed[2]).toMatchObject({ + _id: 3n, + date: '["2023-03-06 13:47:00.000Z"]' + }); + + expect(transformed[3]).toMatchObject({ + _id: 10n, + objectId: '["66e834cc91d805df11fa0ecb"]', + timestamp: '[1958505087099]' + }); + } + + test('test direct queries', async () => { + const { db, client } = await connectMongoData(); + const collection = db.collection('test_data'); + try { + await setupTable(db); + + await insert(collection); + + const transformed = [...ChangeStream.getQueryData(await db.collection('test_data').find().toArray())]; + + checkResults(transformed); + } finally { + await client.close(); + } + }); + + test('test direct queries - arrays', async () => { + const { db, client } = await connectMongoData(); + const collection = db.collection('test_data_arrays'); + try { + await setupTable(db); + + await insertNested(collection); + + const transformed = [...ChangeStream.getQueryData(await db.collection('test_data_arrays').find().toArray())]; + + checkResultsNested(transformed); + } finally { + await client.close(); + } + }); + + // test('test replication', async () => { + // const db = await connectPgPool(); + // try { + // await setupTable(db); + + // const slotName = 'test_slot'; + + // await db.query({ + // statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', + // params: [{ type: 'varchar', value: slotName }] + // }); + + // await db.query({ + // statement: `SELECT slot_name, lsn FROM pg_catalog.pg_create_logical_replication_slot($1, 'pgoutput')`, + // params: [{ type: 'varchar', value: slotName }] + // }); + + // await insert(db); + + // const pg: pgwire.PgConnection = await pgwire.pgconnect({ replication: 'database' }, TEST_URI); + // const replicationStream = await pg.logicalReplication({ + // slot: slotName, + // options: { + // proto_version: '1', + // publication_names: 'powersync' + // } + // }); + + // const transformed = await getReplicationTx(replicationStream); + // await pg.end(); + + // checkResults(transformed); + // } finally { + // await db.end(); + // } + // }); + + // test('test replication - arrays', async () => { + // const db = await connectPgPool(); + // try { + // await setupTable(db); + + // const slotName = 'test_slot'; + + // await db.query({ + // statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', + // params: [{ type: 'varchar', value: slotName }] + // }); + + // await db.query({ + // statement: `SELECT slot_name, lsn FROM pg_catalog.pg_create_logical_replication_slot($1, 'pgoutput')`, + // params: [{ type: 'varchar', value: slotName }] + // }); + + // await insertArrays(db); + + // const pg: pgwire.PgConnection = await pgwire.pgconnect({ replication: 'database' }, TEST_URI); + // const replicationStream = await pg.logicalReplication({ + // slot: slotName, + // options: { + // proto_version: '1', + // publication_names: 'powersync' + // } + // }); + + // const transformed = await getReplicationTx(replicationStream); + // await pg.end(); + + // checkResultArrays(transformed); + // } finally { + // await db.end(); + // } + // }); + + test.skip('schema', async function () { + // const db = await connectPgWire(); + // await setupTable(db); + // TODO need a test for adapter + // const schema = await api.getConnectionsSchema(db); + // expect(schema).toMatchSnapshot(); + }); +}); diff --git a/modules/module-mongodb/test/src/setup.ts b/modules/module-mongodb/test/src/setup.ts new file mode 100644 index 000000000..b924cf736 --- /dev/null +++ b/modules/module-mongodb/test/src/setup.ts @@ -0,0 +1,7 @@ +import { container } from '@powersync/lib-services-framework'; +import { beforeAll } from 'vitest'; + +beforeAll(() => { + // Executes for every test file + container.registerDefaults(); +}); diff --git a/modules/module-mongodb/test/src/util.ts b/modules/module-mongodb/test/src/util.ts new file mode 100644 index 000000000..a101f77a5 --- /dev/null +++ b/modules/module-mongodb/test/src/util.ts @@ -0,0 +1,52 @@ +import * as types from '@module/types/types.js'; +import { BucketStorageFactory, Metrics, MongoBucketStorage, OpId } from '@powersync/service-core'; + +import { env } from './env.js'; +import { logger } from '@powersync/lib-services-framework'; +import { connectMongo } from '@core-tests/util.js'; +import * as mongo from 'mongodb'; + +// The metrics need to be initialized before they can be used +await Metrics.initialise({ + disable_telemetry_sharing: true, + powersync_instance_id: 'test', + internal_metrics_endpoint: 'unused.for.tests.com' +}); +Metrics.getInstance().resetCounters(); + +export const TEST_URI = env.MONGO_TEST_DATA_URL; + +export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ + type: 'mongodb', + uri: TEST_URI +}); + +export type StorageFactory = () => Promise; + +export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { + const db = await connectMongo(); + + // None of the PG tests insert data into this collection, so it was never created + if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) { + await db.db.createCollection('bucket_parameters'); + } + + await db.clear(); + + return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); +}; + +export async function clearTestDb(db: mongo.Db) { + await db.dropDatabase(); +} + +export async function connectMongoData() { + const client = new mongo.MongoClient(env.MONGO_TEST_DATA_URL, { + connectTimeoutMS: env.CI ? 15_000 : 5_000, + socketTimeoutMS: env.CI ? 15_000 : 5_000, + serverSelectionTimeoutMS: env.CI ? 15_000 : 2_500, + useBigInt64: true + }); + const dbname = new URL(env.MONGO_TEST_DATA_URL).pathname.substring(1); + return { client, db: client.db(dbname) }; +} From ae89a0d45da8443fc49d644c8ab15f21447162cb Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 08:40:59 +0200 Subject: [PATCH 183/298] More mongo format tests. --- .../test/src/mongo_test.test.ts | 144 ++++++++---------- 1 file changed, 64 insertions(+), 80 deletions(-) diff --git a/modules/module-mongodb/test/src/mongo_test.test.ts b/modules/module-mongodb/test/src/mongo_test.test.ts index e4126c484..1b36dab90 100644 --- a/modules/module-mongodb/test/src/mongo_test.test.ts +++ b/modules/module-mongodb/test/src/mongo_test.test.ts @@ -2,6 +2,8 @@ import { ChangeStream } from '@module/replication/ChangeStream.js'; import * as mongo from 'mongodb'; import { describe, expect, test } from 'vitest'; import { clearTestDb, connectMongoData } from './util.js'; +import { SqliteRow } from '@powersync/service-sync-rules'; +import { constructAfterRecord } from '@module/replication/MongoRelation.js'; describe('mongo data types', () => { async function setupTable(db: mongo.Db) { @@ -151,85 +153,67 @@ describe('mongo data types', () => { } }); - // test('test replication', async () => { - // const db = await connectPgPool(); - // try { - // await setupTable(db); - - // const slotName = 'test_slot'; - - // await db.query({ - // statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', - // params: [{ type: 'varchar', value: slotName }] - // }); - - // await db.query({ - // statement: `SELECT slot_name, lsn FROM pg_catalog.pg_create_logical_replication_slot($1, 'pgoutput')`, - // params: [{ type: 'varchar', value: slotName }] - // }); - - // await insert(db); - - // const pg: pgwire.PgConnection = await pgwire.pgconnect({ replication: 'database' }, TEST_URI); - // const replicationStream = await pg.logicalReplication({ - // slot: slotName, - // options: { - // proto_version: '1', - // publication_names: 'powersync' - // } - // }); - - // const transformed = await getReplicationTx(replicationStream); - // await pg.end(); - - // checkResults(transformed); - // } finally { - // await db.end(); - // } - // }); - - // test('test replication - arrays', async () => { - // const db = await connectPgPool(); - // try { - // await setupTable(db); - - // const slotName = 'test_slot'; - - // await db.query({ - // statement: 'SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE slot_name = $1', - // params: [{ type: 'varchar', value: slotName }] - // }); - - // await db.query({ - // statement: `SELECT slot_name, lsn FROM pg_catalog.pg_create_logical_replication_slot($1, 'pgoutput')`, - // params: [{ type: 'varchar', value: slotName }] - // }); - - // await insertArrays(db); - - // const pg: pgwire.PgConnection = await pgwire.pgconnect({ replication: 'database' }, TEST_URI); - // const replicationStream = await pg.logicalReplication({ - // slot: slotName, - // options: { - // proto_version: '1', - // publication_names: 'powersync' - // } - // }); - - // const transformed = await getReplicationTx(replicationStream); - // await pg.end(); - - // checkResultArrays(transformed); - // } finally { - // await db.end(); - // } - // }); - - test.skip('schema', async function () { - // const db = await connectPgWire(); - // await setupTable(db); - // TODO need a test for adapter - // const schema = await api.getConnectionsSchema(db); - // expect(schema).toMatchSnapshot(); + test('test replication', async () => { + // With MongoDB, replication uses the exact same document format + // as normal queries. We test it anyway. + const { db, client } = await connectMongoData(); + const collection = db.collection('test_data'); + try { + await setupTable(db); + + const stream = db.watch([], { + useBigInt64: true, + maxAwaitTimeMS: 50, + fullDocument: 'updateLookup' + }); + + await stream.tryNext(); + + await insert(collection); + + const transformed = await getReplicationTx(stream, 4); + + checkResults(transformed); + } finally { + await client.close(); + } + }); + + test('test replication - arrays', async () => { + const { db, client } = await connectMongoData(); + const collection = db.collection('test_data'); + try { + await setupTable(db); + + const stream = db.watch([], { + useBigInt64: true, + maxAwaitTimeMS: 50, + fullDocument: 'updateLookup' + }); + + await stream.tryNext(); + + await insertNested(collection); + + const transformed = await getReplicationTx(stream, 4); + + checkResultsNested(transformed); + } finally { + await client.close(); + } }); }); + +/** + * Return all the inserts from the first transaction in the replication stream. + */ +async function getReplicationTx(replicationStream: mongo.ChangeStream, count: number) { + let transformed: SqliteRow[] = []; + for await (const doc of replicationStream) { + transformed.push(constructAfterRecord((doc as any).fullDocument)); + if (transformed.length == count) { + break; + } + } + return transformed; +} From a2857bf82d9b4df1d48b72bc1cda5ee174c8badd Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 11:59:58 +0200 Subject: [PATCH 184/298] Fix initial snapshot; initial change-stream tests. --- .../src/api/MongoRouteAPIAdapter.ts | 6 +- .../src/replication/ChangeStream.ts | 11 +- .../src/replication/MongoRelation.ts | 23 ++ .../test/src/change_stream.test.ts | 301 ++++++++++++++++++ .../test/src/change_stream_utils.ts | 145 +++++++++ .../service-core/src/storage/BucketStorage.ts | 11 +- .../src/storage/mongo/MongoBucketBatch.ts | 27 +- 7 files changed, 502 insertions(+), 22 deletions(-) create mode 100644 modules/module-mongodb/test/src/change_stream.test.ts create mode 100644 modules/module-mongodb/test/src/change_stream_utils.ts diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 3d862db5e..3389b2eb1 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -5,9 +5,11 @@ import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import * as types from '../types/types.js'; import { MongoManager } from '../replication/MongoManager.js'; +import { createCheckpoint, getMongoLsn } from '../replication/MongoRelation.js'; export class MongoRouteAPIAdapter implements api.RouteAPI { protected client: mongo.MongoClient; + private db: mongo.Db; connectionTag: string; defaultSchema: string; @@ -15,6 +17,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { constructor(protected config: types.ResolvedConnectionConfig) { const manager = new MongoManager(config); this.client = manager.client; + this.db = manager.db; this.defaultSchema = manager.db.databaseName; this.connectionTag = config.tag ?? sync_rules.DEFAULT_TAG; } @@ -72,8 +75,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { } async getReplicationHead(): Promise { - // TODO: implement - return ''; + return createCheckpoint(this.db); } async getConnectionSchema(): Promise { diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 5bc08ba63..03a267afd 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -7,7 +7,7 @@ import { constructAfterRecord, getMongoLsn, getMongoRelation, mongoLsnToTimestam export const ZERO_LSN = '0000000000000000'; -export interface WalStreamOptions { +export interface ChangeStreamOptions { connections: MongoManager; storage: storage.SyncRulesBucketStorage; abort_signal: AbortSignal; @@ -39,7 +39,7 @@ export class ChangeStream { private relation_cache = new Map(); - constructor(options: WalStreamOptions) { + constructor(options: ChangeStreamOptions) { this.storage = options.storage; this.group_id = options.storage.group_id; this.connections = options.connections; @@ -147,7 +147,7 @@ export class ChangeStream { if (time != null) { const lsn = getMongoLsn(time.clusterTime); logger.info(`Snapshot commit at ${time.clusterTime.inspect()} / ${lsn}`); - await batch.commit(lsn); + await batch.commit(lsn, { forceCommit: true }); } else { logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); } @@ -161,7 +161,7 @@ export class ChangeStream { private getSourceNamespaceFilters() { const sourceTables = this.sync_rules.getSourceTables(); - let filters: any[] = []; + let filters: any[] = [{ db: this.defaultDb.databaseName, collection: '_powersync_checkpoints' }]; for (let tablePattern of sourceTables) { if (tablePattern.connectionTag != this.connections.connectionTag) { continue; @@ -337,9 +337,6 @@ export class ChangeStream { // TODO: Use changeStreamSplitLargeEvent - const nsFilter = this.getSourceNamespaceFilters(); - nsFilter.$in.push({ ns: nsFilter }); - const pipeline: mongo.Document[] = [ { $match: { diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index d3c48ea2b..8a83f5803 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -129,3 +129,26 @@ function filterJsonData(data: any, depth = 0): any { return undefined; } } + +export async function createCheckpoint(db: mongo.Db): Promise { + const pingResult = await db.command({ ping: 1 }); + + const time: mongo.Timestamp = pingResult.$clusterTime.clusterTime; + + const result = await db.collection('_powersync_checkpoints').findOneAndUpdate( + { + _id: 'checkpoint' as any + }, + { + $inc: { i: 1 } + }, + { + upsert: true, + returnDocument: 'after' + } + ); + + // TODO: Use the above when we support custom write checkpoints + + return getMongoLsn(time); +} diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts new file mode 100644 index 000000000..c0f144a1b --- /dev/null +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -0,0 +1,301 @@ +import { putOp, removeOp } from '@core-tests/stream_utils.js'; +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; +import { BucketStorageFactory, Metrics } from '@powersync/service-core'; +import * as crypto from 'crypto'; +import { describe, expect, test } from 'vitest'; +import { walStreamTest } from './change_stream_utils.js'; + +type StorageFactory = () => Promise; + +const BASIC_SYNC_RULES = ` +bucket_definitions: + global: + data: + - SELECT id, description FROM "test_data" +`; + +describe( + 'wal stream - mongodb', + function () { + defineWalStreamTests(MONGO_STORAGE_FACTORY); + }, + { timeout: 20_000 } +); + +function defineWalStreamTests(factory: StorageFactory) { + test.only( + 'replicating basic values', + walStreamTest(factory, async (context) => { + const { db } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_data"`); + + await context.replicateSnapshot(); + + context.startStreaming(); + + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); + const test_id = result.insertedId; + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id.toHexString(), description: 'test1', num: 1152921504606846976n }) + ]); + }) + ); + + test( + 'replicating case sensitive table', + walStreamTest(factory, async (context) => { + const { pool } = context; + await context.updateSyncRules(` + bucket_definitions: + global: + data: + - SELECT id, description FROM "test_DATA" + `); + + await pool.query(`DROP TABLE IF EXISTS "test_DATA"`); + await pool.query(`CREATE TABLE "test_DATA"(id uuid primary key default uuid_generate_v4(), description text)`); + + await context.replicateSnapshot(); + + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + context.startStreaming(); + + const [{ test_id }] = pgwireRows( + await pool.query(`INSERT INTO "test_DATA"(description) VALUES('test1') returning id as test_id`) + ); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([putOp('test_DATA', { id: test_id, description: 'test1' })]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + expect(endRowCount - startRowCount).toEqual(1); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); + + test( + 'replicating TOAST values', + walStreamTest(factory, async (context) => { + const { pool } = context; + await context.updateSyncRules(` + bucket_definitions: + global: + data: + - SELECT id, name, description FROM "test_data" + `); + + await pool.query(`DROP TABLE IF EXISTS test_data`); + await pool.query( + `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), name text, description text)` + ); + + await context.replicateSnapshot(); + context.startStreaming(); + + // Must be > 8kb after compression + const largeDescription = crypto.randomBytes(20_000).toString('hex'); + const [{ test_id }] = pgwireRows( + await pool.query({ + statement: `INSERT INTO test_data(name, description) VALUES('test1', $1) returning id as test_id`, + params: [{ type: 'varchar', value: largeDescription }] + }) + ); + + await pool.query(`UPDATE test_data SET name = 'test2' WHERE id = '${test_id}'`); + + const data = await context.getBucketData('global[]'); + expect(data.slice(0, 1)).toMatchObject([ + putOp('test_data', { id: test_id, name: 'test1', description: largeDescription }) + ]); + expect(data.slice(1)).toMatchObject([ + putOp('test_data', { id: test_id, name: 'test2', description: largeDescription }) + ]); + }) + ); + + test( + 'replicating TRUNCATE', + walStreamTest(factory, async (context) => { + const { pool } = context; + const syncRuleContent = ` +bucket_definitions: + global: + data: + - SELECT id, description FROM "test_data" + by_test_data: + parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id + data: [] +`; + await context.updateSyncRules(syncRuleContent); + await pool.query(`DROP TABLE IF EXISTS test_data`); + await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); + + await context.replicateSnapshot(); + context.startStreaming(); + + const [{ test_id }] = pgwireRows( + await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) + ); + await pool.query(`TRUNCATE test_data`); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id, description: 'test1' }), + removeOp('test_data', test_id) + ]); + }) + ); + + test( + 'replicating changing primary key', + walStreamTest(factory, async (context) => { + const { pool } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); + await pool.query(`DROP TABLE IF EXISTS test_data`); + await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); + + await context.replicateSnapshot(); + context.startStreaming(); + + const [{ test_id }] = pgwireRows( + await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) + ); + + const [{ test_id: test_id2 }] = pgwireRows( + await pool.query( + `UPDATE test_data SET id = uuid_generate_v4(), description = 'test2a' WHERE id = '${test_id}' returning id as test_id` + ) + ); + + // This update may fail replicating with: + // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} + await pool.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${test_id2}'`); + + // Re-use old id again + await pool.query(`INSERT INTO test_data(id, description) VALUES('${test_id}', 'test1b')`); + await pool.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${test_id}'`); + + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([ + // Initial insert + putOp('test_data', { id: test_id, description: 'test1' }), + // Update id, then description + removeOp('test_data', test_id), + putOp('test_data', { id: test_id2, description: 'test2a' }), + putOp('test_data', { id: test_id2, description: 'test2b' }), + // Re-use old id + putOp('test_data', { id: test_id, description: 'test1b' }), + putOp('test_data', { id: test_id, description: 'test1c' }) + ]); + }) + ); + + test( + 'initial sync', + walStreamTest(factory, async (context) => { + const { pool } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); + + await pool.query(`DROP TABLE IF EXISTS test_data`); + await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); + + const [{ test_id }] = pgwireRows( + await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) + ); + + await context.replicateSnapshot(); + context.startStreaming(); + + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([putOp('test_data', { id: test_id, description: 'test1' })]); + }) + ); + + test( + 'record too large', + walStreamTest(factory, async (context) => { + await context.updateSyncRules(`bucket_definitions: + global: + data: + - SELECT id, description, other FROM "test_data"`); + const { pool } = context; + + await pool.query(`CREATE TABLE test_data(id text primary key, description text, other text)`); + + await context.replicateSnapshot(); + + // 4MB + const largeDescription = crypto.randomBytes(2_000_000).toString('hex'); + // 18MB + const tooLargeDescription = crypto.randomBytes(9_000_000).toString('hex'); + + await pool.query({ + statement: `INSERT INTO test_data(id, description, other) VALUES('t1', $1, 'foo')`, + params: [{ type: 'varchar', value: tooLargeDescription }] + }); + await pool.query({ + statement: `UPDATE test_data SET description = $1 WHERE id = 't1'`, + params: [{ type: 'varchar', value: largeDescription }] + }); + + context.startStreaming(); + + const data = await context.getBucketData('global[]'); + expect(data.length).toEqual(1); + const row = JSON.parse(data[0].data as string); + delete row.description; + expect(row).toEqual({ id: 't1', other: 'foo' }); + delete data[0].data; + expect(data[0]).toMatchObject({ object_id: 't1', object_type: 'test_data', op: 'PUT', op_id: '1' }); + }) + ); + + test( + 'table not in sync rules', + walStreamTest(factory, async (context) => { + const { pool } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); + + await pool.query(`CREATE TABLE test_donotsync(id uuid primary key default uuid_generate_v4(), description text)`); + + await context.replicateSnapshot(); + + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + context.startStreaming(); + + const [{ test_id }] = pgwireRows( + await pool.query(`INSERT INTO test_donotsync(description) VALUES('test1') returning id as test_id`) + ); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + // There was a transaction, but we should not replicate any actual data + expect(endRowCount - startRowCount).toEqual(0); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); +} diff --git a/modules/module-mongodb/test/src/change_stream_utils.ts b/modules/module-mongodb/test/src/change_stream_utils.ts new file mode 100644 index 000000000..fedfb536c --- /dev/null +++ b/modules/module-mongodb/test/src/change_stream_utils.ts @@ -0,0 +1,145 @@ +import { BucketStorageFactory, OpId, SyncRulesBucketStorage } from '@powersync/service-core'; + +import { TEST_CONNECTION_OPTIONS, clearTestDb } from './util.js'; +import { fromAsync } from '@core-tests/stream_utils.js'; +import { MongoManager } from '@module/replication/MongoManager.js'; +import { ChangeStream, ChangeStreamOptions } from '@module/replication/ChangeStream.js'; +import * as mongo from 'mongodb'; +import { createCheckpoint } from '@module/replication/MongoRelation.js'; + +/** + * Tests operating on the wal stream need to configure the stream and manage asynchronous + * replication, which gets a little tricky. + * + * This wraps a test in a function that configures all the context, and tears it down afterwards. + */ +export function walStreamTest( + factory: () => Promise, + test: (context: ChangeStreamTestContext) => Promise +): () => Promise { + return async () => { + const f = await factory(); + const connectionManager = new MongoManager(TEST_CONNECTION_OPTIONS); + + await clearTestDb(connectionManager.db); + const context = new ChangeStreamTestContext(f, connectionManager); + try { + await test(context); + } finally { + await context.dispose(); + } + }; +} + +export class ChangeStreamTestContext { + private _walStream?: ChangeStream; + private abortController = new AbortController(); + private streamPromise?: Promise; + public storage?: SyncRulesBucketStorage; + + constructor(public factory: BucketStorageFactory, public connectionManager: MongoManager) {} + + async dispose() { + this.abortController.abort(); + await this.streamPromise?.catch((e) => e); + await this.connectionManager.destroy(); + } + + get client() { + return this.connectionManager.client; + } + + get db() { + return this.connectionManager.db; + } + + get connectionTag() { + return this.connectionManager.connectionTag; + } + + async updateSyncRules(content: string) { + const syncRules = await this.factory.updateSyncRules({ content: content }); + this.storage = this.factory.getInstance(syncRules); + return this.storage!; + } + + get walStream() { + if (this.storage == null) { + throw new Error('updateSyncRules() first'); + } + if (this._walStream) { + return this._walStream; + } + const options: ChangeStreamOptions = { + storage: this.storage, + connections: this.connectionManager, + abort_signal: this.abortController.signal + }; + this._walStream = new ChangeStream(options); + return this._walStream!; + } + + async replicateSnapshot() { + await this.walStream.initReplication(); + await this.storage!.autoActivate(); + } + + startStreaming() { + this.streamPromise = this.walStream.streamChanges(); + } + + async getCheckpoint(options?: { timeout?: number }) { + let checkpoint = await Promise.race([ + getClientCheckpoint(this.db, this.factory, { timeout: options?.timeout ?? 15_000 }), + this.streamPromise + ]); + if (typeof checkpoint == undefined) { + // This indicates an issue with the test setup - streamingPromise completed instead + // of getClientCheckpoint() + throw new Error('Test failure - streamingPromise completed'); + } + return checkpoint as string; + } + + async getBucketsDataBatch(buckets: Record, options?: { timeout?: number }) { + let checkpoint = await this.getCheckpoint(options); + const map = new Map(Object.entries(buckets)); + return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map)); + } + + async getBucketData(bucket: string, start?: string, options?: { timeout?: number }) { + start ??= '0'; + let checkpoint = await this.getCheckpoint(options); + const map = new Map([[bucket, start]]); + const batch = this.storage!.getBucketDataBatch(checkpoint, map); + const batches = await fromAsync(batch); + return batches[0]?.batch.data ?? []; + } +} + +export async function getClientCheckpoint( + db: mongo.Db, + bucketStorage: BucketStorageFactory, + options?: { timeout?: number } +): Promise { + const start = Date.now(); + const lsn = await createCheckpoint(db); + // This old API needs a persisted checkpoint id. + // Since we don't use LSNs anymore, the only way to get that is to wait. + + const timeout = options?.timeout ?? 5_00; + + while (Date.now() - start < timeout) { + const cp = await bucketStorage.getActiveCheckpoint(); + if (!cp.hasSyncRules()) { + throw new Error('No sync rules available'); + } + if (cp.lsn && cp.lsn >= lsn) { + return cp.checkpoint; + } + + await new Promise((resolve) => setTimeout(resolve, 30)); + } + + throw new Error('Timeout while waiting for checkpoint'); +} diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 373be8f33..aface2594 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -328,7 +328,7 @@ export interface BucketStorageBatch { * * Only call this after a transaction. */ - commit(lsn: string): Promise; + commit(lsn: string, options?: CommitOptions): Promise; /** * Advance the checkpoint LSN position, without any associated op. @@ -347,6 +347,15 @@ export interface BucketStorageBatch { markSnapshotDone(tables: SourceTable[], no_checkpoint_before_lsn: string): Promise; } +export interface CommitOptions { + /** + * Usually, a commit only takes effect if there are operations to commit. + * + * Setting this to true forces the commit to take place. + */ + forceCommit?: boolean; +} + export interface SaveParameterData { sourceTable: SourceTable; /** UUID */ diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index d82759029..1d42a6494 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -4,10 +4,10 @@ import * as mongo from 'mongodb'; import { container, errors, logger } from '@powersync/lib-services-framework'; import * as util from '../../util/util-index.js'; -import { BucketStorageBatch, FlushedResult, mergeToast, SaveOptions } from '../BucketStorage.js'; +import { BucketStorageBatch, CommitOptions, FlushedResult, mergeToast, SaveOptions } from '../BucketStorage.js'; import { SourceTable } from '../SourceTable.js'; import { PowerSyncMongo } from './db.js'; -import { CurrentBucket, CurrentDataDocument, SourceKey } from './models.js'; +import { CurrentBucket, CurrentDataDocument, SourceKey, SyncRuleDocument } from './models.js'; import { MongoIdSequence } from './MongoIdSequence.js'; import { cacheKey, OperationBatch, RecordOperation } from './OperationBatch.js'; import { PersistedBatch } from './PersistedBatch.js'; @@ -536,7 +536,7 @@ export class MongoBucketBatch implements BucketStorageBatch { await this.session.endSession(); } - async commit(lsn: string): Promise { + async commit(lsn: string, options?: CommitOptions): Promise { await this.flush(); if (this.last_checkpoint_lsn != null && lsn < this.last_checkpoint_lsn) { @@ -550,21 +550,24 @@ export class MongoBucketBatch implements BucketStorageBatch { return false; } - if (this.persisted_op != null) { + if (this.persisted_op != null || options?.forceCommit) { const now = new Date(); + let setValues: mongo.MatchKeysAndValues = { + last_checkpoint_lsn: lsn, + last_checkpoint_ts: now, + last_keepalive_ts: now, + snapshot_done: true, + last_fatal_error: null + }; + if (this.persisted_op != null) { + (setValues as any).last_checkpoint = this.persisted_op; + } await this.db.sync_rules.updateOne( { _id: this.group_id }, { - $set: { - last_checkpoint: this.persisted_op, - last_checkpoint_lsn: lsn, - last_checkpoint_ts: now, - last_keepalive_ts: now, - snapshot_done: true, - last_fatal_error: null - } + $set: setValues }, { session: this.session } ); From 6c2645df6ec50affe58e6a2e24735b00a3b6c6bf Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 17 Sep 2024 12:14:27 +0200 Subject: [PATCH 185/298] Fixed missing ErrorRateLimiter for WalStreamReplicationJob --- .../src/replication/WalStreamReplicationJob.ts | 13 ------------- .../src/replication/WalStreamReplicator.ts | 3 ++- .../src/replication/AbstractReplicationJob.ts | 2 +- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/modules/module-postgres/src/replication/WalStreamReplicationJob.ts b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts index 00603d48d..8d82840d6 100644 --- a/modules/module-postgres/src/replication/WalStreamReplicationJob.ts +++ b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts @@ -4,7 +4,6 @@ import { PgManager } from './PgManager.js'; import { replication } from '@powersync/service-core'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; -import { cleanUpReplicationSlot } from './replication-utils.js'; export interface WalStreamReplicationJobOptions extends replication.AbstractReplicationJobOptions { connectionFactory: ConnectionManagerFactory; @@ -24,18 +23,6 @@ export class WalStreamReplicationJob extends replication.AbstractReplicationJob }); } - async cleanUp(): Promise { - const connectionManager = this.connectionFactory.create({ - idleTimeout: 30_000, - maxSize: 1 - }); - try { - await cleanUpReplicationSlot(this.slotName, connectionManager.pool); - } finally { - await connectionManager.end(); - } - } - /** * Postgres on RDS writes performs a WAL checkpoint every 5 minutes by default, which creates a new 64MB file. * diff --git a/modules/module-postgres/src/replication/WalStreamReplicator.ts b/modules/module-postgres/src/replication/WalStreamReplicator.ts index 61848b377..fb068ba14 100644 --- a/modules/module-postgres/src/replication/WalStreamReplicator.ts +++ b/modules/module-postgres/src/replication/WalStreamReplicator.ts @@ -20,7 +20,8 @@ export class WalStreamReplicator extends replication.AbstractReplicator Date: Tue, 17 Sep 2024 12:18:51 +0200 Subject: [PATCH 186/298] Record keepalive events. --- .../src/replication/ChangeStream.ts | 10 +- .../src/replication/MongoRelation.ts | 1 - .../test/src/change_stream.test.ts | 133 +++++------------- .../service-core/src/storage/BucketStorage.ts | 11 +- .../src/storage/mongo/MongoBucketBatch.ts | 25 ++-- 5 files changed, 51 insertions(+), 129 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 03a267afd..0cfbd8a6e 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -147,7 +147,8 @@ export class ChangeStream { if (time != null) { const lsn = getMongoLsn(time.clusterTime); logger.info(`Snapshot commit at ${time.clusterTime.inspect()} / ${lsn}`); - await batch.commit(lsn, { forceCommit: true }); + await batch.commit(lsn); + await batch.keepalive(lsn); } else { logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); } @@ -161,7 +162,7 @@ export class ChangeStream { private getSourceNamespaceFilters() { const sourceTables = this.sync_rules.getSourceTables(); - let filters: any[] = [{ db: this.defaultDb.databaseName, collection: '_powersync_checkpoints' }]; + let filters: any[] = [{ db: this.defaultDb.databaseName, coll: '_powersync_checkpoints' }]; for (let tablePattern of sourceTables) { if (tablePattern.connectionTag != this.connections.connectionTag) { continue; @@ -396,7 +397,10 @@ export class ChangeStream { // console.log('event', changeDocument); - if ( + if (changeDocument.operationType == 'insert' && changeDocument.ns.coll == '_powersync_checkpoints') { + const lsn = getMongoLsn(changeDocument.clusterTime!); + await batch.keepalive(lsn); + } else if ( changeDocument.operationType == 'insert' || changeDocument.operationType == 'update' || changeDocument.operationType == 'delete' diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index 8a83f5803..6b129d9da 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -134,7 +134,6 @@ export async function createCheckpoint(db: mongo.Db): Promise { const pingResult = await db.command({ ping: 1 }); const time: mongo.Timestamp = pingResult.$clusterTime.clusterTime; - const result = await db.collection('_powersync_checkpoints').findOneAndUpdate( { _id: 'checkpoint' as any diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index c0f144a1b..9578ae506 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -11,7 +11,7 @@ const BASIC_SYNC_RULES = ` bucket_definitions: global: data: - - SELECT id, description FROM "test_data" + - SELECT _id as id, description FROM "test_data" `; describe( @@ -23,7 +23,7 @@ describe( ); function defineWalStreamTests(factory: StorageFactory) { - test.only( + test( 'replicating basic values', walStreamTest(factory, async (context) => { const { db } = context; @@ -52,105 +52,83 @@ bucket_definitions: test( 'replicating case sensitive table', walStreamTest(factory, async (context) => { - const { pool } = context; + const { db } = context; await context.updateSyncRules(` bucket_definitions: global: data: - - SELECT id, description FROM "test_DATA" + - SELECT _id as id, description FROM "test_DATA" `); - await pool.query(`DROP TABLE IF EXISTS "test_DATA"`); - await pool.query(`CREATE TABLE "test_DATA"(id uuid primary key default uuid_generate_v4(), description text)`); - await context.replicateSnapshot(); - const startRowCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const startTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - context.startStreaming(); - const [{ test_id }] = pgwireRows( - await pool.query(`INSERT INTO "test_DATA"(description) VALUES('test1') returning id as test_id`) - ); + const collection = db.collection('test_DATA'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); const data = await context.getBucketData('global[]'); expect(data).toMatchObject([putOp('test_DATA', { id: test_id, description: 'test1' })]); - const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const endTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - expect(endRowCount - startRowCount).toEqual(1); - expect(endTxCount - startTxCount).toEqual(1); }) ); test( - 'replicating TOAST values', + 'replicating large values', walStreamTest(factory, async (context) => { - const { pool } = context; + const { db } = context; await context.updateSyncRules(` bucket_definitions: global: data: - - SELECT id, name, description FROM "test_data" + - SELECT _id as id, name, description FROM "test_data" `); - await pool.query(`DROP TABLE IF EXISTS test_data`); - await pool.query( - `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), name text, description text)` - ); - await context.replicateSnapshot(); context.startStreaming(); - // Must be > 8kb after compression const largeDescription = crypto.randomBytes(20_000).toString('hex'); - const [{ test_id }] = pgwireRows( - await pool.query({ - statement: `INSERT INTO test_data(name, description) VALUES('test1', $1) returning id as test_id`, - params: [{ type: 'varchar', value: largeDescription }] - }) - ); - await pool.query(`UPDATE test_data SET name = 'test2' WHERE id = '${test_id}'`); + const collection = db.collection('test_data'); + const result = await collection.insertOne({ name: 'test1', description: largeDescription }); + const test_id = result.insertedId; + + await collection.updateOne({ _id: test_id }, { $set: { name: 'test2' } }); const data = await context.getBucketData('global[]'); expect(data.slice(0, 1)).toMatchObject([ - putOp('test_data', { id: test_id, name: 'test1', description: largeDescription }) + putOp('test_data', { id: test_id.toHexString(), name: 'test1', description: largeDescription }) ]); expect(data.slice(1)).toMatchObject([ - putOp('test_data', { id: test_id, name: 'test2', description: largeDescription }) + putOp('test_data', { id: test_id.toHexString(), name: 'test2', description: largeDescription }) ]); }) ); - test( - 'replicating TRUNCATE', + // Not implemented yet + test.skip( + 'replicating dropCollection', walStreamTest(factory, async (context) => { - const { pool } = context; + const { db } = context; const syncRuleContent = ` bucket_definitions: global: data: - - SELECT id, description FROM "test_data" + - SELECT _id as id, description FROM "test_data" by_test_data: - parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id + parameters: SELECT _id as id FROM test_data WHERE id = token_parameters.user_id data: [] `; await context.updateSyncRules(syncRuleContent); - await pool.query(`DROP TABLE IF EXISTS test_data`); - await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); - await context.replicateSnapshot(); context.startStreaming(); - const [{ test_id }] = pgwireRows( - await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) - ); - await pool.query(`TRUNCATE test_data`); + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); + + await collection.drop(); const data = await context.getBucketData('global[]'); @@ -161,62 +139,15 @@ bucket_definitions: }) ); - test( - 'replicating changing primary key', - walStreamTest(factory, async (context) => { - const { pool } = context; - await context.updateSyncRules(BASIC_SYNC_RULES); - await pool.query(`DROP TABLE IF EXISTS test_data`); - await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); - - await context.replicateSnapshot(); - context.startStreaming(); - - const [{ test_id }] = pgwireRows( - await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) - ); - - const [{ test_id: test_id2 }] = pgwireRows( - await pool.query( - `UPDATE test_data SET id = uuid_generate_v4(), description = 'test2a' WHERE id = '${test_id}' returning id as test_id` - ) - ); - - // This update may fail replicating with: - // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} - await pool.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${test_id2}'`); - - // Re-use old id again - await pool.query(`INSERT INTO test_data(id, description) VALUES('${test_id}', 'test1b')`); - await pool.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${test_id}'`); - - const data = await context.getBucketData('global[]'); - expect(data).toMatchObject([ - // Initial insert - putOp('test_data', { id: test_id, description: 'test1' }), - // Update id, then description - removeOp('test_data', test_id), - putOp('test_data', { id: test_id2, description: 'test2a' }), - putOp('test_data', { id: test_id2, description: 'test2b' }), - // Re-use old id - putOp('test_data', { id: test_id, description: 'test1b' }), - putOp('test_data', { id: test_id, description: 'test1c' }) - ]); - }) - ); - test( 'initial sync', walStreamTest(factory, async (context) => { - const { pool } = context; + const { db } = context; await context.updateSyncRules(BASIC_SYNC_RULES); - await pool.query(`DROP TABLE IF EXISTS test_data`); - await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); - - const [{ test_id }] = pgwireRows( - await pool.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) - ); + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); await context.replicateSnapshot(); context.startStreaming(); diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index aface2594..373be8f33 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -328,7 +328,7 @@ export interface BucketStorageBatch { * * Only call this after a transaction. */ - commit(lsn: string, options?: CommitOptions): Promise; + commit(lsn: string): Promise; /** * Advance the checkpoint LSN position, without any associated op. @@ -347,15 +347,6 @@ export interface BucketStorageBatch { markSnapshotDone(tables: SourceTable[], no_checkpoint_before_lsn: string): Promise; } -export interface CommitOptions { - /** - * Usually, a commit only takes effect if there are operations to commit. - * - * Setting this to true forces the commit to take place. - */ - forceCommit?: boolean; -} - export interface SaveParameterData { sourceTable: SourceTable; /** UUID */ diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index 1d42a6494..4ac36a385 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -4,7 +4,7 @@ import * as mongo from 'mongodb'; import { container, errors, logger } from '@powersync/lib-services-framework'; import * as util from '../../util/util-index.js'; -import { BucketStorageBatch, CommitOptions, FlushedResult, mergeToast, SaveOptions } from '../BucketStorage.js'; +import { BucketStorageBatch, FlushedResult, mergeToast, SaveOptions } from '../BucketStorage.js'; import { SourceTable } from '../SourceTable.js'; import { PowerSyncMongo } from './db.js'; import { CurrentBucket, CurrentDataDocument, SourceKey, SyncRuleDocument } from './models.js'; @@ -536,7 +536,7 @@ export class MongoBucketBatch implements BucketStorageBatch { await this.session.endSession(); } - async commit(lsn: string, options?: CommitOptions): Promise { + async commit(lsn: string): Promise { await this.flush(); if (this.last_checkpoint_lsn != null && lsn < this.last_checkpoint_lsn) { @@ -550,24 +550,21 @@ export class MongoBucketBatch implements BucketStorageBatch { return false; } - if (this.persisted_op != null || options?.forceCommit) { + if (this.persisted_op != null) { const now = new Date(); - let setValues: mongo.MatchKeysAndValues = { - last_checkpoint_lsn: lsn, - last_checkpoint_ts: now, - last_keepalive_ts: now, - snapshot_done: true, - last_fatal_error: null - }; - if (this.persisted_op != null) { - (setValues as any).last_checkpoint = this.persisted_op; - } await this.db.sync_rules.updateOne( { _id: this.group_id }, { - $set: setValues + $set: { + last_checkpoint: this.persisted_op, + last_checkpoint_lsn: lsn, + last_checkpoint_ts: now, + last_keepalive_ts: now, + snapshot_done: true, + last_fatal_error: null + } }, { session: this.session } ); From aac810188e931264814eafd99bb046d5bfec9000 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 12:36:51 +0200 Subject: [PATCH 187/298] Another fix; basic replication tests passing. --- .../src/replication/ChangeStream.ts | 2 +- .../test/src/change_stream.test.ts | 65 +++++++------------ 2 files changed, 25 insertions(+), 42 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 0cfbd8a6e..0c46c0c00 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -147,7 +147,7 @@ export class ChangeStream { if (time != null) { const lsn = getMongoLsn(time.clusterTime); logger.info(`Snapshot commit at ${time.clusterTime.inspect()} / ${lsn}`); - await batch.commit(lsn); + // keepalive() does an auto-commit if there is data await batch.keepalive(lsn); } else { logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 9578ae506..83e713470 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -1,6 +1,6 @@ import { putOp, removeOp } from '@core-tests/stream_utils.js'; import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; -import { BucketStorageFactory, Metrics } from '@powersync/service-core'; +import { BucketStorageFactory } from '@powersync/service-core'; import * as crypto from 'crypto'; import { describe, expect, test } from 'vitest'; import { walStreamTest } from './change_stream_utils.js'; @@ -15,7 +15,7 @@ bucket_definitions: `; describe( - 'wal stream - mongodb', + 'change stream - mongodb', function () { defineWalStreamTests(MONGO_STORAGE_FACTORY); }, @@ -157,76 +157,59 @@ bucket_definitions: }) ); - test( - 'record too large', + // Not correctly implemented yet + test.skip( + 'large record', walStreamTest(factory, async (context) => { await context.updateSyncRules(`bucket_definitions: global: data: - - SELECT id, description, other FROM "test_data"`); - const { pool } = context; - - await pool.query(`CREATE TABLE test_data(id text primary key, description text, other text)`); + - SELECT _id as id, description, other FROM "test_data"`); + const { db } = context; await context.replicateSnapshot(); - // 4MB - const largeDescription = crypto.randomBytes(2_000_000).toString('hex'); - // 18MB - const tooLargeDescription = crypto.randomBytes(9_000_000).toString('hex'); + // 16MB + const largeDescription = crypto.randomBytes(8_000_000 - 100).toString('hex'); - await pool.query({ - statement: `INSERT INTO test_data(id, description, other) VALUES('t1', $1, 'foo')`, - params: [{ type: 'varchar', value: tooLargeDescription }] - }); - await pool.query({ - statement: `UPDATE test_data SET description = $1 WHERE id = 't1'`, - params: [{ type: 'varchar', value: largeDescription }] - }); + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: largeDescription }); + const test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { name: 't2' } }); context.startStreaming(); const data = await context.getBucketData('global[]'); - expect(data.length).toEqual(1); + expect(data.length).toEqual(2); const row = JSON.parse(data[0].data as string); delete row.description; - expect(row).toEqual({ id: 't1', other: 'foo' }); + expect(row).toEqual({ id: test_id.toHexString() }); delete data[0].data; - expect(data[0]).toMatchObject({ object_id: 't1', object_type: 'test_data', op: 'PUT', op_id: '1' }); + expect(data[0]).toMatchObject({ + object_id: test_id.toHexString(), + object_type: 'test_data', + op: 'PUT', + op_id: '1' + }); }) ); test( 'table not in sync rules', walStreamTest(factory, async (context) => { - const { pool } = context; + const { db } = context; await context.updateSyncRules(BASIC_SYNC_RULES); - await pool.query(`CREATE TABLE test_donotsync(id uuid primary key default uuid_generate_v4(), description text)`); - await context.replicateSnapshot(); - const startRowCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const startTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - context.startStreaming(); - const [{ test_id }] = pgwireRows( - await pool.query(`INSERT INTO test_donotsync(description) VALUES('test1') returning id as test_id`) - ); + const collection = db.collection('test_donotsync'); + const result = await collection.insertOne({ description: 'test' }); const data = await context.getBucketData('global[]'); expect(data).toMatchObject([]); - const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const endTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - - // There was a transaction, but we should not replicate any actual data - expect(endRowCount - startRowCount).toEqual(0); - expect(endTxCount - startTxCount).toEqual(1); }) ); } From 78659d5be24531f19d29b6f71d4a864124b537a4 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 17 Sep 2024 13:08:14 +0200 Subject: [PATCH 188/298] Added more logging for compact action --- packages/service-core/src/entry/commands/compact-action.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index 6016644e5..d737b2232 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -26,9 +26,12 @@ export function registerCompactAction(program: Command) { wrapConfigCommand(compactCommand); return compactCommand.description('Compact storage').action(async (options) => { + logger.info('Compacting storage...'); const runnerConfig = extractRunnerOptions(options); const configuration = await utils.loadConfig(runnerConfig); + logger.info('Successfully loaded configuration...'); const { storage: storageConfig } = configuration; + logger.info('Connecting to storage...'); const psdb = storage.createPowerSyncMongo(storageConfig); const client = psdb.client; await client.connect(); @@ -40,8 +43,9 @@ export function registerCompactAction(program: Command) { return; } const p = bucketStorage.getInstance(active); + logger.info('Performing compaction...'); await p.compact({ memoryLimitMB: COMPACT_MEMORY_LIMIT_MB }); - logger.info('done'); + logger.info('Successfully compacted storage.'); } catch (e) { logger.error(`Failed to compact: ${e.toString()}`); process.exit(1); From 6a3b1e41669ac3680a8a181be5b06ab2724de4ff Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 13:33:53 +0200 Subject: [PATCH 189/298] Handle collection drop and rename events. --- .../src/replication/ChangeStream.ts | 74 ++++++++++++++----- .../test/src/change_stream.test.ts | 35 ++++++++- modules/module-mongodb/test/src/env.ts | 2 +- 3 files changed, 90 insertions(+), 21 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 0c46c0c00..10efb1942 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -80,18 +80,33 @@ export class ChangeStream { const name = tablePattern.name; - const table = await this.handleRelation( - batch, - { - name, - schema, - objectId: name, - replicationColumns: [{ name: '_id' }] - } as SourceEntityDescriptor, - false - ); + // Check if the collection exists + const collections = await this.client + .db(schema) + .listCollections( + { + name: name + }, + { nameOnly: true } + ) + .toArray(); + + if (collections[0]?.name == name) { + const table = await this.handleRelation( + batch, + { + name, + schema, + objectId: name, + replicationColumns: [{ name: '_id' }] + } as SourceEntityDescriptor, + // This is done as part of the initial setup - snapshot is handled elsewhere + { snapshot: false } + ); + + result.push(table); + } - result.push(table); return result; } @@ -126,6 +141,8 @@ export class ChangeStream { const sourceTables = this.sync_rules.getSourceTables(); await this.client.connect(); + const ping = await this.defaultDb.command({ ping: 1 }); + const startTime = ping.$clusterTime.clusterTime as mongo.Timestamp; const session = await this.client.startSession({ snapshot: true }); @@ -142,11 +159,12 @@ export class ChangeStream { await touch(); } } - const time = session.clusterTime; - if (time != null) { - const lsn = getMongoLsn(time.clusterTime); - logger.info(`Snapshot commit at ${time.clusterTime.inspect()} / ${lsn}`); + const snapshotTime = session.clusterTime?.clusterTime ?? startTime; + + if (snapshotTime != null) { + const lsn = getMongoLsn(snapshotTime); + logger.info(`Snapshot commit at ${snapshotTime.inspect()} / ${lsn}`); // keepalive() does an auto-commit if there is data await batch.keepalive(lsn); } else { @@ -228,7 +246,12 @@ export class ChangeStream { await batch.flush(); } - async handleRelation(batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, snapshot: boolean) { + async handleRelation( + batch: storage.BucketStorageBatch, + descriptor: SourceEntityDescriptor, + options: { snapshot: boolean } + ) { + const snapshot = options.snapshot; if (!descriptor.objectId && typeof descriptor.objectId != 'string') { throw new Error('objectId expected'); } @@ -249,7 +272,6 @@ export class ChangeStream { // 2. Snapshot is not already done, AND: // 3. The table is used in sync rules. const shouldSnapshot = snapshot && !result.table.snapshotComplete && result.table.syncAny; - if (shouldSnapshot) { // Truncate this table, in case a previous snapshot was interrupted. await batch.truncate([result.table]); @@ -406,13 +428,29 @@ export class ChangeStream { changeDocument.operationType == 'delete' ) { const rel = getMongoRelation(changeDocument.ns); - const table = await this.handleRelation(batch, rel, true); + // Don't snapshot tables here - we get all the data as insert events + const table = await this.handleRelation(batch, rel, { snapshot: false }); if (table.syncAny) { await this.writeChange(batch, table, changeDocument); if (changeDocument.clusterTime) { lastEventTimestamp = changeDocument.clusterTime; } } + } else if (changeDocument.operationType == 'drop') { + const rel = getMongoRelation(changeDocument.ns); + const table = await this.handleRelation(batch, rel, { snapshot: false }); + if (table.syncAny) { + await batch.drop([table]); + } + } else if (changeDocument.operationType == 'rename') { + const relFrom = getMongoRelation(changeDocument.ns); + const relTo = getMongoRelation(changeDocument.to); + const tableFrom = await this.handleRelation(batch, relFrom, { snapshot: false }); + if (tableFrom.syncAny) { + await batch.drop([tableFrom]); + } + // Here we do need to snapshot the new table + await this.handleRelation(batch, relTo, { snapshot: true }); } } }); diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 83e713470..618a0856c 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -106,8 +106,7 @@ bucket_definitions: }) ); - // Not implemented yet - test.skip( + test( 'replicating dropCollection', walStreamTest(factory, async (context) => { const { db } = context; @@ -139,6 +138,38 @@ bucket_definitions: }) ); + test( + 'replicating renameCollection', + walStreamTest(factory, async (context) => { + const { db } = context; + const syncRuleContent = ` +bucket_definitions: + global: + data: + - SELECT _id as id, description FROM "test_data1" + - SELECT _id as id, description FROM "test_data2" +`; + await context.updateSyncRules(syncRuleContent); + await context.replicateSnapshot(); + context.startStreaming(); + + console.log('insert1', db.databaseName); + const collection = db.collection('test_data1'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); + + await collection.rename('test_data2'); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data1', { id: test_id, description: 'test1' }), + removeOp('test_data1', test_id), + putOp('test_data2', { id: test_id, description: 'test1' }) + ]); + }) + ); + test( 'initial sync', walStreamTest(factory, async (context) => { diff --git a/modules/module-mongodb/test/src/env.ts b/modules/module-mongodb/test/src/env.ts index 7d72df3a9..e460c80b3 100644 --- a/modules/module-mongodb/test/src/env.ts +++ b/modules/module-mongodb/test/src/env.ts @@ -1,7 +1,7 @@ import { utils } from '@powersync/lib-services-framework'; export const env = utils.collectEnvironmentVariables({ - MONGO_TEST_DATA_URL: utils.type.string.default('mongodb://localhost:27017/powersync_test'), + MONGO_TEST_DATA_URL: utils.type.string.default('mongodb://localhost:27017/powersync_test_data'), CI: utils.type.boolean.default('false'), SLOW_TESTS: utils.type.boolean.default('false') }); From 9f1de16225bf5c02a2cfa21ee3eb88d067452c1e Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 13:40:30 +0200 Subject: [PATCH 190/298] Handle replace event. --- .../module-mongodb/src/replication/ChangeStream.ts | 3 ++- .../module-mongodb/test/src/change_stream.test.ts | 14 ++++++++++++-- .../module-mongodb/test/src/change_stream_utils.ts | 6 ++++-- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 10efb1942..73f32bcb8 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -307,7 +307,7 @@ export class ChangeStream { after: baseRecord, afterReplicaId: change.documentKey._id }); - } else if (change.operationType == 'update') { + } else if (change.operationType == 'update' || change.operationType == 'replace') { const after = constructAfterRecord(change.fullDocument ?? {}); return await batch.save({ tag: 'update', @@ -425,6 +425,7 @@ export class ChangeStream { } else if ( changeDocument.operationType == 'insert' || changeDocument.operationType == 'update' || + changeDocument.operationType == 'replace' || changeDocument.operationType == 'delete' ) { const rel = getMongoRelation(changeDocument.ns); diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 618a0856c..889ddda8b 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -33,18 +33,28 @@ bucket_definitions: data: - SELECT _id as id, description, num FROM "test_data"`); + db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: true } + }); + const collection = db.collection('test_data'); + await context.replicateSnapshot(); context.startStreaming(); - const collection = db.collection('test_data'); const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); const test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); + await collection.replaceOne({ _id: test_id }, { description: 'test3' }); + await collection.deleteOne({ _id: test_id }); const data = await context.getBucketData('global[]'); expect(data).toMatchObject([ - putOp('test_data', { id: test_id.toHexString(), description: 'test1', num: 1152921504606846976n }) + putOp('test_data', { id: test_id.toHexString(), description: 'test1', num: 1152921504606846976n }), + putOp('test_data', { id: test_id.toHexString(), description: 'test2', num: 1152921504606846976n }), + putOp('test_data', { id: test_id.toHexString(), description: 'test3' }), + removeOp('test_data', test_id.toHexString()) ]); }) ); diff --git a/modules/module-mongodb/test/src/change_stream_utils.ts b/modules/module-mongodb/test/src/change_stream_utils.ts index fedfb536c..73f71f292 100644 --- a/modules/module-mongodb/test/src/change_stream_utils.ts +++ b/modules/module-mongodb/test/src/change_stream_utils.ts @@ -1,4 +1,4 @@ -import { BucketStorageFactory, OpId, SyncRulesBucketStorage } from '@powersync/service-core'; +import { ActiveCheckpoint, BucketStorageFactory, OpId, SyncRulesBucketStorage } from '@powersync/service-core'; import { TEST_CONNECTION_OPTIONS, clearTestDb } from './util.js'; import { fromAsync } from '@core-tests/stream_utils.js'; @@ -128,9 +128,11 @@ export async function getClientCheckpoint( // Since we don't use LSNs anymore, the only way to get that is to wait. const timeout = options?.timeout ?? 5_00; + let lastCp: ActiveCheckpoint | null = null; while (Date.now() - start < timeout) { const cp = await bucketStorage.getActiveCheckpoint(); + lastCp = cp; if (!cp.hasSyncRules()) { throw new Error('No sync rules available'); } @@ -141,5 +143,5 @@ export async function getClientCheckpoint( await new Promise((resolve) => setTimeout(resolve, 30)); } - throw new Error('Timeout while waiting for checkpoint'); + throw new Error(`Timeout while waiting for checkpoint. Last checkpoint: ${lastCp?.lsn}`); } From 64bb4f48de312a887424f9f33c511a9ca17be297 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 13:46:37 +0200 Subject: [PATCH 191/298] Handle missing fullDocument correctly. --- .../src/replication/ChangeStream.ts | 11 ++++- .../test/src/change_stream.test.ts | 47 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 73f32bcb8..a92842617 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -308,7 +308,16 @@ export class ChangeStream { afterReplicaId: change.documentKey._id }); } else if (change.operationType == 'update' || change.operationType == 'replace') { - const after = constructAfterRecord(change.fullDocument ?? {}); + if (change.fullDocument == null) { + // Treat as delete + return await batch.save({ + tag: 'delete', + sourceTable: table, + before: undefined, + beforeReplicaId: change.documentKey._id + }); + } + const after = constructAfterRecord(change.fullDocument!); return await batch.save({ tag: 'update', sourceTable: table, diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 889ddda8b..1abefe69b 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -4,7 +4,7 @@ import { BucketStorageFactory } from '@powersync/service-core'; import * as crypto from 'crypto'; import { describe, expect, test } from 'vitest'; import { walStreamTest } from './change_stream_utils.js'; - +import * as mongo from 'mongodb'; type StorageFactory = () => Promise; const BASIC_SYNC_RULES = ` @@ -59,6 +59,51 @@ bucket_definitions: }) ); + test( + 'no fullDocument available', + walStreamTest(factory, async (context) => { + const { db, client } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_data"`); + + db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: false } + }); + const collection = db.collection('test_data'); + + await context.replicateSnapshot(); + + context.startStreaming(); + + const session = client.startSession(); + let test_id: mongo.ObjectId | undefined; + try { + await session.withTransaction(async () => { + const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }, { session }); + test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }, { session }); + await collection.replaceOne({ _id: test_id }, { description: 'test3' }, { session }); + await collection.deleteOne({ _id: test_id }, { session }); + }); + } finally { + await session.endSession(); + } + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id!.toHexString(), description: 'test1', num: 1152921504606846976n }), + // fullDocument is not available at the point this is replicated, resulting in it treated as a remove + removeOp('test_data', test_id!.toHexString()), + putOp('test_data', { id: test_id!.toHexString(), description: 'test3' }), + removeOp('test_data', test_id!.toHexString()) + ]); + }) + ); + test( 'replicating case sensitive table', walStreamTest(factory, async (context) => { From 834aa18998f90aa5456d0547d1f864ec91898a04 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 14:21:15 +0200 Subject: [PATCH 192/298] Improve keepalive stability. --- .../src/api/MongoRouteAPIAdapter.ts | 2 +- .../src/replication/ChangeStream.ts | 21 ++++++--- .../src/replication/MongoRelation.ts | 43 ++++++++++--------- .../test/src/change_stream.test.ts | 5 +++ .../test/src/change_stream_utils.ts | 9 ++-- 5 files changed, 50 insertions(+), 30 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 3389b2eb1..e3a3eea72 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -75,7 +75,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { } async getReplicationHead(): Promise { - return createCheckpoint(this.db); + return createCheckpoint(this.client, this.db); } async getConnectionSchema(): Promise { diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index a92842617..d3552dc96 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -3,7 +3,13 @@ import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-cor import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; import { MongoManager } from './MongoManager.js'; -import { constructAfterRecord, getMongoLsn, getMongoRelation, mongoLsnToTimestamp } from './MongoRelation.js'; +import { + constructAfterRecord, + createCheckpoint, + getMongoLsn, + getMongoRelation, + mongoLsnToTimestamp +} from './MongoRelation.js'; export const ZERO_LSN = '0000000000000000'; @@ -276,10 +282,10 @@ export class ChangeStream { // Truncate this table, in case a previous snapshot was interrupted. await batch.truncate([result.table]); - let lsn: string = ZERO_LSN; - // TODO: Transaction / consistency await this.snapshotTable(batch, result.table); - const [table] = await batch.markSnapshotDone([result.table], lsn); + const no_checkpoint_before_lsn = await createCheckpoint(this.client, this.defaultDb); + + const [table] = await batch.markSnapshotDone([result.table], no_checkpoint_before_lsn); return table; } @@ -428,7 +434,12 @@ export class ChangeStream { // console.log('event', changeDocument); - if (changeDocument.operationType == 'insert' && changeDocument.ns.coll == '_powersync_checkpoints') { + if ( + (changeDocument.operationType == 'insert' || + changeDocument.operationType == 'update' || + changeDocument.operationType == 'replace') && + changeDocument.ns.coll == '_powersync_checkpoints' + ) { const lsn = getMongoLsn(changeDocument.clusterTime!); await batch.keepalive(lsn); } else if ( diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index 6b129d9da..267674895 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -130,24 +130,27 @@ function filterJsonData(data: any, depth = 0): any { } } -export async function createCheckpoint(db: mongo.Db): Promise { - const pingResult = await db.command({ ping: 1 }); - - const time: mongo.Timestamp = pingResult.$clusterTime.clusterTime; - const result = await db.collection('_powersync_checkpoints').findOneAndUpdate( - { - _id: 'checkpoint' as any - }, - { - $inc: { i: 1 } - }, - { - upsert: true, - returnDocument: 'after' - } - ); - - // TODO: Use the above when we support custom write checkpoints - - return getMongoLsn(time); +export async function createCheckpoint(client: mongo.MongoClient, db: mongo.Db): Promise { + const session = client.startSession(); + try { + const result = await db.collection('_powersync_checkpoints').findOneAndUpdate( + { + _id: 'checkpoint' as any + }, + { + $inc: { i: 1 } + }, + { + upsert: true, + returnDocument: 'after', + session + } + ); + const time = session.operationTime!; + // console.log('marked checkpoint at', time, getMongoLsn(time)); + // TODO: Use the above when we support custom write checkpoints + return getMongoLsn(time); + } finally { + await session.endSession(); + } } diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 1abefe69b..f950e4f35 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -5,6 +5,8 @@ import * as crypto from 'crypto'; import { describe, expect, test } from 'vitest'; import { walStreamTest } from './change_stream_utils.js'; import * as mongo from 'mongodb'; +import { setTimeout } from 'node:timers/promises'; + type StorageFactory = () => Promise; const BASIC_SYNC_RULES = ` @@ -44,8 +46,11 @@ bucket_definitions: const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); const test_id = result.insertedId; + await setTimeout(10); await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); + await setTimeout(10); await collection.replaceOne({ _id: test_id }, { description: 'test3' }); + await setTimeout(10); await collection.deleteOne({ _id: test_id }); const data = await context.getBucketData('global[]'); diff --git a/modules/module-mongodb/test/src/change_stream_utils.ts b/modules/module-mongodb/test/src/change_stream_utils.ts index 73f71f292..02a70e6b4 100644 --- a/modules/module-mongodb/test/src/change_stream_utils.ts +++ b/modules/module-mongodb/test/src/change_stream_utils.ts @@ -90,7 +90,7 @@ export class ChangeStreamTestContext { async getCheckpoint(options?: { timeout?: number }) { let checkpoint = await Promise.race([ - getClientCheckpoint(this.db, this.factory, { timeout: options?.timeout ?? 15_000 }), + getClientCheckpoint(this.client, this.db, this.factory, { timeout: options?.timeout ?? 15_000 }), this.streamPromise ]); if (typeof checkpoint == undefined) { @@ -118,16 +118,17 @@ export class ChangeStreamTestContext { } export async function getClientCheckpoint( + client: mongo.MongoClient, db: mongo.Db, bucketStorage: BucketStorageFactory, options?: { timeout?: number } ): Promise { const start = Date.now(); - const lsn = await createCheckpoint(db); + const lsn = await createCheckpoint(client, db); // This old API needs a persisted checkpoint id. // Since we don't use LSNs anymore, the only way to get that is to wait. - const timeout = options?.timeout ?? 5_00; + const timeout = options?.timeout ?? 50_000; let lastCp: ActiveCheckpoint | null = null; while (Date.now() - start < timeout) { @@ -143,5 +144,5 @@ export async function getClientCheckpoint( await new Promise((resolve) => setTimeout(resolve, 30)); } - throw new Error(`Timeout while waiting for checkpoint. Last checkpoint: ${lastCp?.lsn}`); + throw new Error(`Timeout while waiting for checkpoint ${lsn}. Last checkpoint: ${lastCp?.lsn}`); } From b8fb0125188abe5709f76496224a9126961c41c7 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 17 Sep 2024 14:31:51 +0200 Subject: [PATCH 193/298] Added start up and shutdown logging to engines. Consolidated shutdown functions of engines. --- .../service-core/src/replication/ReplicationEngine.ts | 8 +++++++- packages/service-core/src/routes/RouterEngine.ts | 10 +++++++--- packages/service-core/src/storage/StorageEngine.ts | 6 ++++++ service/src/runners/server.ts | 2 +- service/src/runners/stream-worker.ts | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/service-core/src/replication/ReplicationEngine.ts b/packages/service-core/src/replication/ReplicationEngine.ts index 258c0da1a..bf57570b3 100644 --- a/packages/service-core/src/replication/ReplicationEngine.ts +++ b/packages/service-core/src/replication/ReplicationEngine.ts @@ -21,17 +21,23 @@ export class ReplicationEngine { * Start replication on all managed Replicators */ public start(): void { + logger.info('Starting Replication Engine...'); for (const replicator of this.replicators.values()) { + logger.info(`Starting Replicator: ${replicator.id}`); replicator.start(); } + logger.info('Successfully started Replication Engine.'); } /** * Stop replication on all managed Replicators */ - public async stop(): Promise { + public async shutDown(): Promise { + logger.info('Shutting down Replication Engine...'); for (const replicator of this.replicators.values()) { + logger.info(`Stopping Replicator: ${replicator.id}`); await replicator.stop(); } + logger.info('Successfully shut down Replication Engine.'); } } diff --git a/packages/service-core/src/routes/RouterEngine.ts b/packages/service-core/src/routes/RouterEngine.ts index ebab56f44..28b35173f 100644 --- a/packages/service-core/src/routes/RouterEngine.ts +++ b/packages/service-core/src/routes/RouterEngine.ts @@ -72,31 +72,35 @@ export class RouterEngine { * Starts the router given the configuration provided */ async start(setup: RouterSetup) { + logger.info('Starting Router Engine...'); const { onShutdown } = await setup(this.routes); this.cleanupHandler = onShutdown; + logger.info('Successfully started Router Engine.'); } /** * Runs all stop handlers then final cleanup. */ - async shutdown() { + async shutDown() { + logger.info('Shutting down Router Engine...'); // Close open streams, so that they don't block the server from closing. // Note: This does not work well when streaming requests are queued. In that case, the server still doesn't // close in the 30-second timeout. this.closed = true; - logger.info(`Closing ${this.stopHandlers.size} streams`); + logger.info(`Closing ${this.stopHandlers.size} streams.`); for (let handler of this.stopHandlers) { handler(); } - logger.info(`Running close cleanup`); + logger.info(`Running cleanup.`); // Typically closes the server await this.cleanupHandler?.(); // Close the api handlers await this.api?.shutdown(); + logger.info('Successfully shut down Router Engine.'); } /** diff --git a/packages/service-core/src/storage/StorageEngine.ts b/packages/service-core/src/storage/StorageEngine.ts index 966851a26..bc4d23db4 100644 --- a/packages/service-core/src/storage/StorageEngine.ts +++ b/packages/service-core/src/storage/StorageEngine.ts @@ -1,6 +1,7 @@ import { ResolvedPowerSyncConfig } from '../util/util-index.js'; import { BucketStorageFactory } from './BucketStorage.js'; import { BucketStorageProvider, ActiveStorage } from './StorageProvider.js'; +import { logger } from '@powersync/lib-services-framework'; export type StorageEngineOptions = { configuration: ResolvedPowerSyncConfig; @@ -34,16 +35,21 @@ export class StorageEngine { } public async start(): Promise { + logger.info('Starting Storage Engine...'); const { configuration } = this.options; this.currentActiveStorage = await this.storageProviders.get(configuration.storage.type)!.getStorage({ resolvedConfig: configuration }); + logger.info(`Successfully activated storage: ${configuration.storage.type}.`); + logger.info('Successfully started Storage Engine.'); } /** * Shutdown the storage engine, safely shutting down any activated storage providers. */ public async shutDown(): Promise { + logger.info('Shutting down Storage Engine...'); await this.currentActiveStorage?.shutDown(); + logger.info('Successfully shut down Storage Engine.'); } } diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index 3a4d7b50e..c91c507be 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -54,7 +54,7 @@ export function registerServerServices(serviceContext: core.system.ServiceContex }; }); }, - stop: (routerEngine) => routerEngine!.shutdown() + stop: (routerEngine) => routerEngine!.shutDown() }); } diff --git a/service/src/runners/stream-worker.ts b/service/src/runners/stream-worker.ts index 0f58c6ede..b3c69d2a8 100644 --- a/service/src/runners/stream-worker.ts +++ b/service/src/runners/stream-worker.ts @@ -12,7 +12,7 @@ export const registerReplicationServices = (serviceContext: core.system.ServiceC serviceContext.register(core.replication.ReplicationEngine, replication); serviceContext.lifeCycleEngine.withLifecycle(replication, { start: (replication) => replication.start(), - stop: (replication) => replication.stop() + stop: (replication) => replication.shutDown() }); }; From a31b727be4c99fc17efc3f7a24c0d354042f719e Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 16:29:44 +0200 Subject: [PATCH 194/298] Use checkpoints for standard replication. --- .../src/replication/ChangeStream.ts | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index d3552dc96..fc092f4ca 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -1,5 +1,5 @@ import { container, logger } from '@powersync/lib-services-framework'; -import { Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; +import { Metrics, SourceEntityDescriptor, SourceTable, storage } from '@powersync/service-core'; import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; import { MongoManager } from './MongoManager.js'; @@ -172,6 +172,7 @@ export class ChangeStream { const lsn = getMongoLsn(snapshotTime); logger.info(`Snapshot commit at ${snapshotTime.inspect()} / ${lsn}`); // keepalive() does an auto-commit if there is data + await batch.flush(); await batch.keepalive(lsn); } else { logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); @@ -252,6 +253,17 @@ export class ChangeStream { await batch.flush(); } + private async getRelation( + batch: storage.BucketStorageBatch, + descriptor: SourceEntityDescriptor + ): Promise { + const existing = this.relation_cache.get(descriptor.objectId); + if (existing != null) { + return existing; + } + return this.handleRelation(batch, descriptor, { snapshot: false }); + } + async handleRelation( batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, @@ -388,28 +400,17 @@ export class ChangeStream { showExpandedEvents: true, useBigInt64: true, maxAwaitTimeMS: 200, - fullDocument: 'updateLookup' // FIXME: figure this one out + fullDocument: 'updateLookup' }); this.abort_signal.addEventListener('abort', () => { stream.close(); }); - let lastEventTimestamp: mongo.Timestamp | null = null; + let waitForCheckpointLsn: string | null = null; while (true) { const changeDocument = await stream.tryNext(); if (changeDocument == null) { - // We don't get events for transaction commit. - // So if no events are available in the stream within maxAwaitTimeMS, - // we assume the transaction is complete. - // This is not foolproof - we may end up with a commit in the middle - // of a transaction. - if (lastEventTimestamp != null) { - const lsn = getMongoLsn(lastEventTimestamp); - await batch.commit(lsn); - lastEventTimestamp = null; - } - continue; } await touch(); @@ -422,16 +423,6 @@ export class ChangeStream { continue; } - if ( - lastEventTimestamp != null && - changeDocument.clusterTime != null && - changeDocument.clusterTime.neq(lastEventTimestamp) - ) { - const lsn = getMongoLsn(lastEventTimestamp); - await batch.commit(lsn); - lastEventTimestamp = null; - } - // console.log('event', changeDocument); if ( @@ -441,6 +432,10 @@ export class ChangeStream { changeDocument.ns.coll == '_powersync_checkpoints' ) { const lsn = getMongoLsn(changeDocument.clusterTime!); + if (waitForCheckpointLsn != null && lsn >= waitForCheckpointLsn) { + waitForCheckpointLsn = null; + } + await batch.flush(); await batch.keepalive(lsn); } else if ( changeDocument.operationType == 'insert' || @@ -448,27 +443,28 @@ export class ChangeStream { changeDocument.operationType == 'replace' || changeDocument.operationType == 'delete' ) { + if (waitForCheckpointLsn == null) { + waitForCheckpointLsn = await createCheckpoint(this.client, this.defaultDb); + } const rel = getMongoRelation(changeDocument.ns); - // Don't snapshot tables here - we get all the data as insert events - const table = await this.handleRelation(batch, rel, { snapshot: false }); + const table = await this.getRelation(batch, rel); if (table.syncAny) { await this.writeChange(batch, table, changeDocument); - if (changeDocument.clusterTime) { - lastEventTimestamp = changeDocument.clusterTime; - } } } else if (changeDocument.operationType == 'drop') { const rel = getMongoRelation(changeDocument.ns); - const table = await this.handleRelation(batch, rel, { snapshot: false }); + const table = await this.getRelation(batch, rel); if (table.syncAny) { await batch.drop([table]); + this.relation_cache.delete(table.objectId); } } else if (changeDocument.operationType == 'rename') { const relFrom = getMongoRelation(changeDocument.ns); const relTo = getMongoRelation(changeDocument.to); - const tableFrom = await this.handleRelation(batch, relFrom, { snapshot: false }); + const tableFrom = await this.getRelation(batch, relFrom); if (tableFrom.syncAny) { await batch.drop([tableFrom]); + this.relation_cache.delete(tableFrom.objectId); } // Here we do need to snapshot the new table await this.handleRelation(batch, relTo, { snapshot: true }); From 594acc45ef9149e403fe1da5e9592449577d00f0 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 16:42:00 +0200 Subject: [PATCH 195/298] Support wildcard table patterns. --- .../src/replication/ChangeStream.ts | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index fc092f4ca..f993ec171 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -78,32 +78,32 @@ export class ChangeStream { return []; } + let nameFilter: RegExp | string; if (tablePattern.isWildcard) { - // TODO: Implement - throw new Error('Wildcard collections not supported yet'); + nameFilter = new RegExp('^' + escapeRegExp(tablePattern.tablePrefix)); + } else { + nameFilter = tablePattern.name; } let result: storage.SourceTable[] = []; - const name = tablePattern.name; - // Check if the collection exists const collections = await this.client .db(schema) .listCollections( { - name: name + name: nameFilter }, { nameOnly: true } ) .toArray(); - if (collections[0]?.name == name) { + for (let collection of collections) { const table = await this.handleRelation( batch, { - name, + name: collection.name, schema, - objectId: name, + objectId: collection.name, replicationColumns: [{ name: '_id' }] } as SourceEntityDescriptor, // This is done as part of the initial setup - snapshot is handled elsewhere @@ -187,23 +187,26 @@ export class ChangeStream { private getSourceNamespaceFilters() { const sourceTables = this.sync_rules.getSourceTables(); - let filters: any[] = [{ db: this.defaultDb.databaseName, coll: '_powersync_checkpoints' }]; + let $inFilters: any[] = [{ db: this.defaultDb.databaseName, coll: '_powersync_checkpoints' }]; + let $refilters: any[] = []; for (let tablePattern of sourceTables) { if (tablePattern.connectionTag != this.connections.connectionTag) { continue; } if (tablePattern.isWildcard) { - // TODO: Implement - throw new Error('wildcard collections not supported yet'); + $refilters.push({ db: tablePattern.schema, coll: new RegExp('^' + escapeRegExp(tablePattern.tablePrefix)) }); + } else { + $inFilters.push({ + db: tablePattern.schema, + coll: tablePattern.name + }); } - - filters.push({ - db: tablePattern.schema, - coll: tablePattern.name - }); } - return { $in: filters }; + if ($refilters.length > 0) { + return { $or: [{ ns: { $in: $inFilters } }, ...$refilters] }; + } + return { ns: { $in: $inFilters } }; } static *getQueryData(results: Iterable): Generator { @@ -389,9 +392,7 @@ export class ChangeStream { const pipeline: mongo.Document[] = [ { - $match: { - ns: this.getSourceNamespaceFilters() - } + $match: this.getSourceNamespaceFilters() } ]; @@ -480,3 +481,8 @@ async function touch() { // or reduce PING_INTERVAL here. return container.probes.touch(); } + +function escapeRegExp(string: string) { + // https://stackoverflow.com/a/3561711/214837 + return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); +} From ef65356009b1ddb5c49b80c95fc3f14d8d2ac901 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 17 Sep 2024 16:43:34 +0200 Subject: [PATCH 196/298] Fix merge issue. --- .../module-mongodb/src/replication/ChangeStreamReplicator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts index d4c1314dd..84b2c7f68 100644 --- a/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts @@ -1,6 +1,7 @@ import { storage, replication } from '@powersync/service-core'; import { ChangeStreamReplicationJob } from './ChangeStreamReplicationJob.js'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; +import { MongoErrorRateLimiter } from './MongoErrorRateLimiter.js'; export interface WalStreamReplicatorOptions extends replication.AbstractReplicatorOptions { connectionFactory: ConnectionManagerFactory; @@ -19,7 +20,8 @@ export class ChangeStreamReplicator extends replication.AbstractReplicator Date: Wed, 18 Sep 2024 10:09:12 +0200 Subject: [PATCH 197/298] Added changeset --- .changeset/tender-vans-impress.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .changeset/tender-vans-impress.md diff --git a/.changeset/tender-vans-impress.md b/.changeset/tender-vans-impress.md new file mode 100644 index 000000000..0da1c4848 --- /dev/null +++ b/.changeset/tender-vans-impress.md @@ -0,0 +1,16 @@ +--- +'@powersync/service-core': minor +'@powersync/service-sync-rules': minor +'@powersync/lib-services-framework': minor +'@powersync/service-jpgwire': minor +'@powersync/service-types': minor +'@powersync/service-image': minor +'@powersync/service-module-postgres': patch +--- + +- Introduced modules to the powersync service architecture + - Core functionality has been moved to "engine" classes. Modules can register additional functionality with these engines. + - The sync API functionality used by the routes has been abstracted to an interface. API routes are now managed by the RouterEngine. + - Replication is managed by the ReplicationEngine and new replication data sources can be registered to the engine by modules. +- Refactored existing Postgres replication as a module. +- Removed Postgres specific code from the core service packages. From 74dcead6e4d9dc201a4a872b43e5ea43a378743f Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 18 Sep 2024 10:47:07 +0200 Subject: [PATCH 198/298] Made Postgres Module package public Removed mongodb test dependency in Postgres module --- modules/module-postgres/package.json | 3 +-- modules/module-postgres/test/src/slow_tests.test.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index a6de29dad..eb4efe257 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -3,7 +3,7 @@ "repository": "https://github.com/powersync-ja/powersync-service", "types": "dist/index.d.ts", "publishConfig": { - "access": "restricted" + "access": "public" }, "version": "0.0.1", "main": "dist/index.js", @@ -35,7 +35,6 @@ "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87", - "mongodb": "^6.7.0", "jose": "^4.15.1", "ts-codec": "^1.2.2", "uuid": "^9.0.1", diff --git a/modules/module-postgres/test/src/slow_tests.test.ts b/modules/module-postgres/test/src/slow_tests.test.ts index 0b3ae38fc..ad7c6753d 100644 --- a/modules/module-postgres/test/src/slow_tests.test.ts +++ b/modules/module-postgres/test/src/slow_tests.test.ts @@ -1,5 +1,4 @@ import * as bson from 'bson'; -import * as mongo from 'mongodb'; import { afterEach, describe, expect, test } from 'vitest'; import { WalStream, WalStreamOptions } from '../../src/replication/WalStream.js'; import { env } from './env.js'; @@ -194,7 +193,7 @@ bucket_definitions: // Check that all inserts have been deleted again const docs = await f.db.current_data.find().toArray(); const transformed = docs.map((doc) => { - return bson.deserialize((doc.data as mongo.Binary).buffer) as SqliteRow; + return bson.deserialize(doc.data.buffer) as SqliteRow; }); expect(transformed).toEqual([]); From 1f4ded87fe20e9842bbdb2c01f6dd3ca117e195b Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 18 Sep 2024 10:51:52 +0200 Subject: [PATCH 199/298] Updated lockfile --- pnpm-lock.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7174dd5be..95a9a6723 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -117,9 +117,6 @@ importers: jose: specifier: ^4.15.1 version: 4.15.5 - mongodb: - specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 @@ -5221,7 +5218,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.0 '@prisma/instrumentation': 5.15.0 '@sentry/core': 8.9.2 - '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) + '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) '@sentry/types': 8.9.2 '@sentry/utils': 8.9.2 optionalDependencies: @@ -5229,7 +5226,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': + '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) From 952cc1e68d4621aa3ed38eebe24c852d6a93e63c Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 18 Sep 2024 12:11:30 +0200 Subject: [PATCH 200/298] Update typescript, vitest, prettier. --- libs/lib-services/package.json | 2 +- modules/module-postgres/package.json | 4 +- package.json | 10 +- packages/rsocket-router/package.json | 4 +- packages/service-core/package.json | 4 +- packages/sync-rules/package.json | 4 +- pnpm-lock.yaml | 1920 ++++---------------------- service/package.json | 4 +- test-client/package.json | 2 +- 9 files changed, 316 insertions(+), 1638 deletions(-) diff --git a/libs/lib-services/package.json b/libs/lib-services/package.json index 3797df49f..add5fc587 100644 --- a/libs/lib-services/package.json +++ b/libs/lib-services/package.json @@ -33,6 +33,6 @@ "devDependencies": { "@types/lodash": "^4.17.5", "@types/uuid": "^9.0.4", - "vitest": "^0.34.6" + "vitest": "^2.1.1" } } diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index eb4efe257..10fa404c8 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -42,8 +42,8 @@ }, "devDependencies": { "@types/uuid": "^9.0.4", - "typescript": "^5.2.2", - "vitest": "^0.34.6", + "typescript": "^5.6.2", + "vitest": "^2.1.1", "vite-tsconfig-paths": "^4.3.2" } } diff --git a/package.json b/package.json index 54bae275f..0f8fe506f 100644 --- a/package.json +++ b/package.json @@ -21,20 +21,20 @@ "test": "pnpm run -r test" }, "devDependencies": { - "@changesets/cli": "^2.27.3", - "@types/node": "18.11.11", + "@changesets/cli": "^2.27.8", + "@types/node": "^22.5.5", "async": "^3.2.4", "bson": "^6.6.0", "concurrently": "^8.2.2", "inquirer": "^9.2.7", - "npm-check-updates": "^16.10.15", - "prettier": "^2.8.8", + "npm-check-updates": "^17.1.2", + "prettier": "^3.3.3", "rsocket-core": "1.0.0-alpha.3", "rsocket-websocket-client": "1.0.0-alpha.3", "semver": "^7.5.4", "tsc-watch": "^6.2.0", "ts-node-dev": "^2.0.0", - "typescript": "~5.2.2", + "typescript": "^5.6.2", "ws": "^8.2.3" } } diff --git a/packages/rsocket-router/package.json b/packages/rsocket-router/package.json index 43114d2c9..97747a9f5 100644 --- a/packages/rsocket-router/package.json +++ b/packages/rsocket-router/package.json @@ -29,7 +29,7 @@ "@types/ws": "~8.2.0", "bson": "^6.6.0", "rsocket-websocket-client": "1.0.0-alpha.3", - "typescript": "~5.2.2", - "vitest": "^0.34.6" + "typescript": "^5.6.2", + "vitest": "^2.1.1" } } diff --git a/packages/service-core/package.json b/packages/service-core/package.json index 58f691586..2e6f1d58a 100644 --- a/packages/service-core/package.json +++ b/packages/service-core/package.json @@ -50,8 +50,8 @@ "@types/uuid": "^9.0.4", "fastify": "4.23.2", "fastify-plugin": "^4.5.1", - "typescript": "^5.2.2", + "typescript": "^5.6.2", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^0.34.6" + "vitest": "^2.1.1" } } diff --git a/packages/sync-rules/package.json b/packages/sync-rules/package.json index 474b091cd..de6e1f4de 100644 --- a/packages/sync-rules/package.json +++ b/packages/sync-rules/package.json @@ -26,7 +26,7 @@ "yaml": "^2.3.1" }, "devDependencies": { - "@types/node": "18.19.50", - "vitest": "^2.0.5" + "@types/node": "^22.5.5", + "vitest": "^2.1.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95a9a6723..01125d62f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,11 +9,11 @@ importers: .: devDependencies: '@changesets/cli': - specifier: ^2.27.3 - version: 2.27.3 + specifier: ^2.27.8 + version: 2.27.8 '@types/node': - specifier: 18.11.11 - version: 18.11.11 + specifier: ^22.5.5 + version: 22.5.5 async: specifier: ^3.2.4 version: 3.2.5 @@ -27,11 +27,11 @@ importers: specifier: ^9.2.7 version: 9.2.22 npm-check-updates: - specifier: ^16.10.15 - version: 16.14.20 + specifier: ^17.1.2 + version: 17.1.2 prettier: - specifier: ^2.8.8 - version: 2.8.8 + specifier: ^3.3.3 + version: 3.3.3 rsocket-core: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 @@ -43,13 +43,13 @@ importers: version: 7.6.2 ts-node-dev: specifier: ^2.0.0 - version: 2.0.0(@types/node@18.11.11)(typescript@5.2.2) + version: 2.0.0(@types/node@22.5.5)(typescript@5.6.2) tsc-watch: specifier: ^6.2.0 - version: 6.2.0(typescript@5.2.2) + version: 6.2.0(typescript@5.6.2) typescript: - specifier: ~5.2.2 - version: 5.2.2 + specifier: ^5.6.2 + version: 5.6.2 ws: specifier: ^8.2.3 version: 8.2.3 @@ -91,8 +91,51 @@ importers: specifier: ^9.0.4 version: 9.0.8 vitest: - specifier: ^0.34.6 - version: 0.34.6 + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) + + modules/module-mongodb: + dependencies: + '@powersync/lib-services-framework': + specifier: workspace:* + version: link:../../libs/lib-services + '@powersync/service-core': + specifier: workspace:* + version: link:../../packages/service-core + '@powersync/service-jsonbig': + specifier: workspace:* + version: link:../../packages/jsonbig + '@powersync/service-sync-rules': + specifier: workspace:* + version: link:../../packages/sync-rules + '@powersync/service-types': + specifier: workspace:* + version: link:../../packages/types + mongodb: + specifier: ^6.7.0 + version: 6.7.0(socks@2.8.3) + ts-codec: + specifier: ^1.2.2 + version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + devDependencies: + '@types/uuid': + specifier: ^9.0.4 + version: 9.0.8 + typescript: + specifier: ^5.6.2 + version: 5.6.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) + vitest: + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) modules/module-postgres: dependencies: @@ -134,14 +177,14 @@ importers: specifier: ^9.0.4 version: 9.0.8 typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.6.2 + version: 5.6.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.2.2)(vite@5.2.11(@types/node@18.11.11)) + version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) vitest: - specifier: ^0.34.6 - version: 0.34.6 + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) packages/jpgwire: dependencies: @@ -198,11 +241,11 @@ importers: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 typescript: - specifier: ~5.2.2 - version: 5.2.2 + specifier: ^5.6.2 + version: 5.6.2 vitest: - specifier: ^0.34.6 - version: 0.34.6 + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) packages/service-core: dependencies: @@ -304,14 +347,14 @@ importers: specifier: ^4.5.1 version: 4.5.1 typescript: - specifier: ^5.2.2 - version: 5.4.5 + specifier: ^5.6.2 + version: 5.6.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@18.11.11)) + version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) vitest: - specifier: ^0.34.6 - version: 0.34.6 + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) packages/sync-rules: dependencies: @@ -332,11 +375,11 @@ importers: version: 2.4.2 devDependencies: '@types/node': - specifier: 18.19.50 - version: 18.19.50 + specifier: ^22.5.5 + version: 22.5.5 vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@18.19.50) + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) packages/types: dependencies: @@ -454,13 +497,13 @@ importers: version: 16.14.20 ts-node: specifier: ^10.9.1 - version: 10.9.2(@types/node@18.19.50)(typescript@5.2.2) + version: 10.9.2(@types/node@22.5.5)(typescript@5.6.2) typescript: - specifier: ~5.2.2 - version: 5.2.2 + specifier: ^5.6.2 + version: 5.6.2 vitest: - specifier: ^0.34.6 - version: 0.34.6 + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) test-client: dependencies: @@ -481,15 +524,11 @@ importers: specifier: 18.11.11 version: 18.11.11 typescript: - specifier: ^5.2.2 - version: 5.2.2 + specifier: ^5.6.2 + version: 5.6.2 packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/code-frame@7.24.6': resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==} engines: {node: '>=6.9.0'} @@ -506,48 +545,51 @@ packages: resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==} engines: {node: '>=6.9.0'} - '@changesets/apply-release-plan@7.0.1': - resolution: {integrity: sha512-aPdSq/R++HOyfEeBGjEe6LNG8gs0KMSyRETD/J2092OkNq8mOioAxyKjMbvVUdzgr/HTawzMOz7lfw339KnsCA==} + '@changesets/apply-release-plan@7.0.5': + resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} - '@changesets/assemble-release-plan@6.0.0': - resolution: {integrity: sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==} + '@changesets/assemble-release-plan@6.0.4': + resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} '@changesets/changelog-git@0.2.0': resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} - '@changesets/cli@2.27.3': - resolution: {integrity: sha512-ve/VpWApILlSs8cr0okNx5C2LKRawI9XZgvfmf58S8sar2nhx5DPJREFXYZBahs0FeTfvH0rdVl+nGe8QF45Ig==} + '@changesets/cli@2.27.8': + resolution: {integrity: sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==} hasBin: true - '@changesets/config@3.0.0': - resolution: {integrity: sha512-o/rwLNnAo/+j9Yvw9mkBQOZySDYyOr/q+wptRLcAVGlU6djOeP9v1nlalbL9MFsobuBVQbZCTp+dIzdq+CLQUA==} + '@changesets/config@3.0.3': + resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - '@changesets/get-dependents-graph@2.0.0': - resolution: {integrity: sha512-cafUXponivK4vBgZ3yLu944mTvam06XEn2IZGjjKc0antpenkYANXiiE6GExV/yKdsCnE8dXVZ25yGqLYZmScA==} + '@changesets/get-dependents-graph@2.1.2': + resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} - '@changesets/get-release-plan@4.0.0': - resolution: {integrity: sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w==} + '@changesets/get-release-plan@4.0.4': + resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - '@changesets/git@3.0.0': - resolution: {integrity: sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w==} + '@changesets/git@3.0.1': + resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} - '@changesets/logger@0.1.0': - resolution: {integrity: sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g==} + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} '@changesets/parse@0.4.0': resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} - '@changesets/pre@2.0.0': - resolution: {integrity: sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw==} + '@changesets/pre@2.0.1': + resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} - '@changesets/read@0.6.0': - resolution: {integrity: sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw==} + '@changesets/read@0.6.1': + resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} + + '@changesets/should-skip-package@0.1.1': + resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} '@changesets/types@4.1.0': resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} @@ -555,8 +597,8 @@ packages: '@changesets/types@6.0.0': resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - '@changesets/write@0.3.1': - resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} + '@changesets/write@0.3.2': + resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} @@ -741,27 +783,15 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -1222,9 +1252,6 @@ packages: resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sindresorhus/is@5.6.0': resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} engines: {node: '>=14.16'} @@ -1269,12 +1296,6 @@ packages: '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/chai-subset@1.3.5': - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} - - '@types/chai@4.3.16': - resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} - '@types/connect@3.4.36': resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} @@ -1323,9 +1344,6 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/minimist@1.2.5': - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - '@types/mysql@2.15.22': resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} @@ -1341,11 +1359,8 @@ packages: '@types/node@18.11.11': resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} - '@types/node@18.19.50': - resolution: {integrity: sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/node@22.5.5': + resolution: {integrity: sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==} '@types/pg-pool@2.0.4': resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} @@ -1395,38 +1410,35 @@ packages: '@types/ws@8.2.3': resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} - '@vitest/expect@0.34.6': - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} - - '@vitest/expect@2.0.5': - resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} - - '@vitest/pretty-format@2.0.5': - resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} - - '@vitest/runner@0.34.6': - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/expect@2.1.1': + resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} - '@vitest/runner@2.0.5': - resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} - - '@vitest/snapshot@0.34.6': - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/mocker@2.1.1': + resolution: {integrity: sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==} + peerDependencies: + '@vitest/spy': 2.1.1 + msw: ^2.3.5 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true - '@vitest/snapshot@2.0.5': - resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} + '@vitest/pretty-format@2.1.1': + resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} - '@vitest/spy@0.34.6': - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/runner@2.1.1': + resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} - '@vitest/spy@2.0.5': - resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + '@vitest/snapshot@2.1.1': + resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} - '@vitest/utils@0.34.6': - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/spy@2.1.1': + resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} - '@vitest/utils@2.0.5': - resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} + '@vitest/utils@2.1.1': + resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1515,10 +1527,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -1544,29 +1552,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} - array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - - arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1581,10 +1570,6 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - avvio@8.3.2: resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} @@ -1625,9 +1610,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - breakword@1.0.6: - resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} - bson@6.7.0: resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==} engines: {node: '>=16.20.1'} @@ -1665,22 +1647,10 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} - camelcase-keys@6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - camelcase@7.0.1: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - chai@4.4.1: - resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} - engines: {node: '>=4'} - chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} engines: {node: '>=12'} @@ -1700,9 +1670,6 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1746,9 +1713,6 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -1805,9 +1769,6 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -1847,35 +1808,10 @@ packages: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} - csv-generate@3.4.3: - resolution: {integrity: sha512-w/T+rqR0vwvHqWs/1ZyMDWtHHSJaN06klRqJXBEpDJaM/+dZkso0OKh1VcuuYvK3XM53KysVNq8Ko/epCK8wOw==} - - csv-parse@4.16.3: - resolution: {integrity: sha512-cO1I/zmz4w2dcKHVvpCr7JVRu8/FymG5OEpmvsZYlccYolPBLoVGKUHgNoc4ZGkFeFlWGEDmMyBM+TTqRdW/wg==} - - csv-stringify@5.6.5: - resolution: {integrity: sha512-PjiQ659aQ+fUTQqSrd1XEDnOr52jh30RBurfzkscaE2tPaFsDH5wOAHJiw8XAHphRknCwMUE9KRayc4K/NbO8A==} - - csv@5.5.3: - resolution: {integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==} - engines: {node: '>= 0.1.90'} - data-uri-to-buffer@4.0.1: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} - date-fns@2.30.0: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} @@ -1901,22 +1837,10 @@ packages: supports-color: optional: true - decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - deep-eql@4.1.3: - resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} - engines: {node: '>=6'} - deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1936,10 +1860,6 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -1947,10 +1867,6 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -2002,13 +1918,6 @@ packages: err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} - es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} engines: {node: '>= 0.4'} @@ -2017,21 +1926,6 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} @@ -2068,10 +1962,6 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - exponential-backoff@3.1.1: resolution: {integrity: sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==} @@ -2143,15 +2033,9 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-yarn-workspace-root2@1.2.16: - resolution: {integrity: sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==} - fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -2202,13 +2086,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -2233,14 +2110,6 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2263,10 +2132,6 @@ packages: resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==} engines: {node: '>=10'} - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2287,16 +2152,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - - hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} - - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -2316,10 +2171,6 @@ packages: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} @@ -2331,9 +2182,6 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - hosted-git-info@5.2.1: resolution: {integrity: sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -2360,10 +2208,6 @@ packages: human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} @@ -2435,10 +2279,6 @@ packages: resolution: {integrity: sha512-SqLLa/Oe5rZUagTR9z+Zd6izyatHglbmbvVofo1KzuVB54YHleWzeHNLoR7FOICGOeQSqeLh1cordb3MzhGcEw==} engines: {node: '>=18'} - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - ip-address@9.0.5: resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} engines: {node: '>= 12'} @@ -2451,31 +2291,13 @@ packages: resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - is-ci@3.0.1: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true @@ -2483,14 +2305,6 @@ packages: is-core-module@2.13.1: resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} - - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2514,18 +2328,10 @@ packages: is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - is-npm@6.0.0: resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -2538,42 +2344,14 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} - is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - is-typedarray@1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -2581,9 +2359,6 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -2598,9 +2373,6 @@ packages: isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2634,9 +2406,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@3.0.2: resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -2672,10 +2441,6 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} @@ -2694,17 +2459,6 @@ packages: light-my-request@5.13.0: resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - load-yaml-file@0.2.0: - resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} - engines: {node: '>=6'} - - local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2733,9 +2487,6 @@ packages: lossless-json@2.0.11: resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - loupe@3.1.1: resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} @@ -2754,8 +2505,8 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} @@ -2768,27 +2519,12 @@ packages: resolution: {integrity: sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - - map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - map-stream@0.1.0: resolution: {integrity: sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==} memory-pager@1.5.0: resolution: {integrity: sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==} - meow@6.1.1: - resolution: {integrity: sha512-3YffViIt2QWgTy6Pale5QpopX/IvU3LPL03jOTqp6pGj3VjesdO/U8CuHMKpnQr4shCNCM5fd5XFFvIIl6JBHg==} - engines: {node: '>=8'} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2801,10 +2537,6 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -2813,10 +2545,6 @@ packages: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2828,10 +2556,6 @@ packages: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} - minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -2878,18 +2602,11 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} - mixme@0.5.10: - resolution: {integrity: sha512-5H76ANWinB1H3twpJ6JY8uvAtpmFvHNArpilJAjXRKXSDDLPIMoZArw5SH0q9z+lLs8IrMw7Q2VWpWimFKFT1Q==} - engines: {node: '>= 8.0.0'} - mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - mlly@1.7.0: - resolution: {integrity: sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==} - mnemonist@0.39.5: resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} @@ -2929,6 +2646,10 @@ packages: moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -2981,9 +2702,6 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} hasBin: true - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - normalize-package-data@5.0.0: resolution: {integrity: sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3005,6 +2723,11 @@ packages: engines: {node: '>=14.14'} hasBin: true + npm-check-updates@17.1.2: + resolution: {integrity: sha512-k3osAbCNXIXqC7QAuF2uRHsKtTUS50KhOW1VAojRHlLdZRh/5EYfduvnVPGDWsbQXFakbSrSbWDdV8qIvDSUtA==} + engines: {node: ^18.18.0 || >=20.0.0, npm: '>=8.12.1'} + hasBin: true + npm-install-checks@6.3.0: resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3029,10 +2752,6 @@ packages: resolution: {integrity: sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - npmlog@6.0.2: resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} @@ -3042,17 +2761,6 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - obliterator@2.0.4: resolution: {integrity: sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==} @@ -3070,10 +2778,6 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - opentelemetry-instrumentation-fetch-node@1.2.0: resolution: {integrity: sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA==} engines: {node: '>18.0.0'} @@ -3105,10 +2809,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -3133,6 +2833,9 @@ packages: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} + package-manager-detector@0.2.0: + resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} + pacote@15.2.0: resolution: {integrity: sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3143,10 +2846,6 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3159,10 +2858,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -3177,9 +2872,6 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -3209,6 +2901,9 @@ packages: picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -3227,17 +2922,6 @@ packages: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - - pkg-types@1.1.1: - resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} - - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} @@ -3258,18 +2942,15 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - preferred-pm@3.1.3: - resolution: {integrity: sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w==} - engines: {node: '>=10'} - prettier@2.8.8: resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} engines: {node: '>=10.13.0'} hasBin: true - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true proc-log@3.0.0: resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} @@ -3340,10 +3021,6 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - quick-lru@4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - quick-lru@5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -3362,9 +3039,6 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - read-package-json-fast@3.0.2: resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3374,14 +3048,6 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} deprecated: This package is no longer supported. Please use @npmcli/package-json instead. - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} @@ -3408,17 +3074,9 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - registry-auth-token@5.0.2: resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} engines: {node: '>=14'} @@ -3443,9 +3101,6 @@ packages: resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} engines: {node: '>=8.6.0'} - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - resolve-alpn@1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} @@ -3520,20 +3175,12 @@ packages: rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} - safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - safe-regex2@2.0.0: resolution: {integrity: sha512-PaUSFsUaNNuKwkBijoAPHAK6/eM6VirvyPWlZ7BAQy4D+hCvh4B6lIG+nPdhbFfIbP+gTGBcrdsOaUs0F+ZBOQ==} @@ -3554,10 +3201,6 @@ packages: semver-utils@1.1.4: resolution: {integrity: sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==} - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} @@ -3573,10 +3216,6 @@ packages: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -3599,10 +3238,6 @@ packages: shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -3636,11 +3271,6 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - smartwrap@2.0.2: - resolution: {integrity: sha512-vCsKNQxb7PnCNd2wY1WClWifAc2lwqsG8OaswpJkVJsvMGcnEntdTCDajZCkk93Ay1U3t/9puJmb525Rg5MZBA==} - engines: {node: '>=6'} - hasBin: true - socks-proxy-agent@7.0.0: resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==} engines: {node: '>= 10'} @@ -3721,9 +3351,6 @@ packages: stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} - stream-transform@2.1.3: - resolution: {integrity: sha512-9GHUiM5hMiCi6Y03jD2ARC1ettBXkQBoQAe7nJsPknnI0ow10aXjTnew8QtYQmLjzn974BnmWEAJgCY6ZP1DeQ==} - string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -3736,17 +3363,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - string_decoder@0.10.31: resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} @@ -3768,14 +3384,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -3784,9 +3392,6 @@ packages: resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} engines: {node: '>=14.16'} - strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -3823,12 +3428,11 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} tinypool@1.0.1: resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} @@ -3838,10 +3442,6 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} - tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - tinyspy@3.0.0: resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} engines: {node: '>=14.0.0'} @@ -3870,10 +3470,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - trim-newlines@3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - triple-beam@1.4.1: resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==} engines: {node: '>= 14.0.0'} @@ -3929,35 +3525,14 @@ packages: tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} - tty-table@4.2.3: - resolution: {integrity: sha512-Fs15mu0vGzCrj8fmJNP7Ynxt5J7praPXqFN0leZeZBXJwkMxv9cb2D454k1ltrtUSJbZ4yH4e0CynsHLxmUfFA==} - engines: {node: '>=8.0.0'} - hasBin: true - tuf-js@1.1.7: resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - type-fest@1.4.0: resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} engines: {node: '>=10'} @@ -3966,46 +3541,19 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} - typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} - typescript@5.2.2: - resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} - engines: {node: '>=14.17'} - hasBin: true - - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} engines: {node: '>=14.17'} hasBin: true - ufo@1.5.3: - resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} - - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} unique-filename@2.0.1: resolution: {integrity: sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==} @@ -4063,13 +3611,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite-node@0.34.6: - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true - - vite-node@2.0.5: - resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} + vite-node@2.1.1: + resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -4109,46 +3652,15 @@ packages: terser: optional: true - vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true - - vitest@2.0.5: - resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} + vitest@2.1.1: + resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.5 - '@vitest/ui': 2.0.5 + '@vitest/browser': 2.1.1 + '@vitest/ui': 2.1.1 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -4180,20 +3692,6 @@ packages: resolution: {integrity: sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==} engines: {node: '>=16'} - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which-pm@2.0.0: - resolution: {integrity: sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==} - engines: {node: '>=8.15'} - - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -4208,11 +3706,6 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true - why-is-node-running@2.2.2: - resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} - engines: {node: '>=8'} - hasBin: true - why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -4283,9 +3776,6 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4306,10 +3796,6 @@ packages: engines: {node: '>= 14'} hasBin: true - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -4318,10 +3804,6 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} @@ -4338,20 +3820,11 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.0.0: - resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} - engines: {node: '>=12.20'} - zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} snapshots: - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - '@babel/code-frame@7.24.6': dependencies: '@babel/highlight': 7.24.6 @@ -4370,12 +3843,12 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 - '@changesets/apply-release-plan@7.0.1': + '@changesets/apply-release-plan@7.0.5': dependencies: - '@babel/runtime': 7.24.6 - '@changesets/config': 3.0.0 + '@changesets/config': 3.0.3 '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.0 + '@changesets/git': 3.0.1 + '@changesets/should-skip-package': 0.1.1 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 detect-indent: 6.1.0 @@ -4386,11 +3859,11 @@ snapshots: resolve-from: 5.0.0 semver: 7.6.2 - '@changesets/assemble-release-plan@6.0.0': + '@changesets/assemble-release-plan@6.0.4': dependencies: - '@babel/runtime': 7.24.6 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/should-skip-package': 0.1.1 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 semver: 7.6.2 @@ -4399,46 +3872,44 @@ snapshots: dependencies: '@changesets/types': 6.0.0 - '@changesets/cli@2.27.3': + '@changesets/cli@2.27.8': dependencies: - '@babel/runtime': 7.24.6 - '@changesets/apply-release-plan': 7.0.1 - '@changesets/assemble-release-plan': 6.0.0 + '@changesets/apply-release-plan': 7.0.5 + '@changesets/assemble-release-plan': 6.0.4 '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.0 + '@changesets/config': 3.0.3 '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/get-release-plan': 4.0.0 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/get-release-plan': 4.0.4 + '@changesets/git': 3.0.1 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.1 + '@changesets/should-skip-package': 0.1.1 '@changesets/types': 6.0.0 - '@changesets/write': 0.3.1 + '@changesets/write': 0.3.2 '@manypkg/get-packages': 1.1.3 '@types/semver': 7.5.8 ansi-colors: 4.1.3 - chalk: 2.4.2 ci-info: 3.9.0 enquirer: 2.4.1 external-editor: 3.1.0 fs-extra: 7.0.1 - human-id: 1.0.2 - meow: 6.1.1 + mri: 1.2.0 outdent: 0.5.0 p-limit: 2.3.0 - preferred-pm: 3.1.3 + package-manager-detector: 0.2.0 + picocolors: 1.1.0 resolve-from: 5.0.0 semver: 7.6.2 spawndamnit: 2.0.0 term-size: 2.2.1 - tty-table: 4.2.3 - '@changesets/config@3.0.0': + '@changesets/config@3.0.3': dependencies: '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.0.0 - '@changesets/logger': 0.1.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/logger': 0.1.1 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 @@ -4448,71 +3919,69 @@ snapshots: dependencies: extendable-error: 0.1.7 - '@changesets/get-dependents-graph@2.0.0': + '@changesets/get-dependents-graph@2.1.2': dependencies: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - chalk: 2.4.2 - fs-extra: 7.0.1 + picocolors: 1.1.0 semver: 7.6.2 - '@changesets/get-release-plan@4.0.0': + '@changesets/get-release-plan@4.0.4': dependencies: - '@babel/runtime': 7.24.6 - '@changesets/assemble-release-plan': 6.0.0 - '@changesets/config': 3.0.0 - '@changesets/pre': 2.0.0 - '@changesets/read': 0.6.0 + '@changesets/assemble-release-plan': 6.0.4 + '@changesets/config': 3.0.3 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.1 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 '@changesets/get-version-range-type@0.4.0': {} - '@changesets/git@3.0.0': + '@changesets/git@3.0.1': dependencies: - '@babel/runtime': 7.24.6 '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 micromatch: 4.0.7 spawndamnit: 2.0.0 - '@changesets/logger@0.1.0': + '@changesets/logger@0.1.1': dependencies: - chalk: 2.4.2 + picocolors: 1.1.0 '@changesets/parse@0.4.0': dependencies: '@changesets/types': 6.0.0 js-yaml: 3.14.1 - '@changesets/pre@2.0.0': + '@changesets/pre@2.0.1': dependencies: - '@babel/runtime': 7.24.6 '@changesets/errors': 0.2.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - '@changesets/read@0.6.0': + '@changesets/read@0.6.1': dependencies: - '@babel/runtime': 7.24.6 - '@changesets/git': 3.0.0 - '@changesets/logger': 0.1.0 + '@changesets/git': 3.0.1 + '@changesets/logger': 0.1.1 '@changesets/parse': 0.4.0 '@changesets/types': 6.0.0 - chalk: 2.4.2 fs-extra: 7.0.1 p-filter: 2.1.0 + picocolors: 1.1.0 + + '@changesets/should-skip-package@0.1.1': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 '@changesets/types@4.1.0': {} '@changesets/types@6.0.0': {} - '@changesets/write@0.3.1': + '@changesets/write@0.3.2': dependencies: - '@babel/runtime': 7.24.6 '@changesets/types': 6.0.0 fs-extra: 7.0.1 human-id: 1.0.2 @@ -4638,26 +4107,11 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jridgewell/gen-mapping@0.3.5': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.25 - '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} - '@jridgewell/sourcemap-codec@1.4.15': {} - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.9': dependencies: @@ -5218,7 +4672,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.0 '@prisma/instrumentation': 5.15.0 '@sentry/core': 8.9.2 - '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) + '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) '@sentry/types': 8.9.2 '@sentry/utils': 8.9.2 optionalDependencies: @@ -5226,7 +4680,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': + '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) @@ -5264,8 +4718,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@sinclair/typebox@0.27.8': {} - '@sindresorhus/is@5.6.0': {} '@syncpoint/wkx@0.5.2': @@ -5295,28 +4747,22 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/async@3.2.24': {} '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 18.19.50 - - '@types/chai-subset@1.3.5': - dependencies: - '@types/chai': 4.3.16 - - '@types/chai@4.3.16': {} + '@types/node': 22.5.5 '@types/connect@3.4.36': dependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/connect@3.4.38': dependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/content-disposition@0.5.8': {} @@ -5325,13 +4771,13 @@ snapshots: '@types/connect': 3.4.38 '@types/express': 4.17.21 '@types/keygrip': 1.0.6 - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/estree@1.0.5': {} '@types/express-serve-static-core@4.19.1': dependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/qs': 6.9.15 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -5364,7 +4810,7 @@ snapshots: '@types/http-errors': 2.0.4 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/koa__router@12.0.3': dependencies: @@ -5374,11 +4820,9 @@ snapshots: '@types/mime@1.3.5': {} - '@types/minimist@1.2.5': {} - '@types/mysql@2.15.22': dependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/node@12.20.55': {} @@ -5388,11 +4832,9 @@ snapshots: '@types/node@18.11.11': {} - '@types/node@18.19.50': + '@types/node@22.5.5': dependencies: - undici-types: 5.26.5 - - '@types/normalize-package-data@2.4.4': {} + undici-types: 6.19.8 '@types/pg-pool@2.0.4': dependencies: @@ -5400,7 +4842,7 @@ snapshots: '@types/pg@8.6.1': dependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 pg-protocol: 1.6.1 pg-types: 2.2.0 @@ -5415,12 +4857,12 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 18.19.50 + '@types/node': 22.5.5 '@types/send': 0.17.4 '@types/shimmer@1.0.5': {} @@ -5441,66 +4883,45 @@ snapshots: '@types/ws@8.2.3': dependencies: - '@types/node': 18.11.11 - - '@vitest/expect@0.34.6': - dependencies: - '@vitest/spy': 0.34.6 - '@vitest/utils': 0.34.6 - chai: 4.4.1 + '@types/node': 22.5.5 - '@vitest/expect@2.0.5': + '@vitest/expect@2.1.1': dependencies: - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/pretty-format@2.0.5': - dependencies: - tinyrainbow: 1.2.0 - - '@vitest/runner@0.34.6': + '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.2.11(@types/node@22.5.5))': dependencies: - '@vitest/utils': 0.34.6 - p-limit: 4.0.0 - pathe: 1.1.2 + '@vitest/spy': 2.1.1 + estree-walker: 3.0.3 + magic-string: 0.30.11 + optionalDependencies: + vite: 5.2.11(@types/node@22.5.5) - '@vitest/runner@2.0.5': + '@vitest/pretty-format@2.1.1': dependencies: - '@vitest/utils': 2.0.5 - pathe: 1.1.2 + tinyrainbow: 1.2.0 - '@vitest/snapshot@0.34.6': + '@vitest/runner@2.1.1': dependencies: - magic-string: 0.30.10 + '@vitest/utils': 2.1.1 pathe: 1.1.2 - pretty-format: 29.7.0 - '@vitest/snapshot@2.0.5': + '@vitest/snapshot@2.1.1': dependencies: - '@vitest/pretty-format': 2.0.5 - magic-string: 0.30.10 + '@vitest/pretty-format': 2.1.1 + magic-string: 0.30.11 pathe: 1.1.2 - '@vitest/spy@0.34.6': - dependencies: - tinyspy: 2.2.1 - - '@vitest/spy@2.0.5': + '@vitest/spy@2.1.1': dependencies: tinyspy: 3.0.0 - '@vitest/utils@0.34.6': - dependencies: - diff-sequences: 29.6.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - - '@vitest/utils@2.0.5': + '@vitest/utils@2.1.1': dependencies: - '@vitest/pretty-format': 2.0.5 - estree-walker: 3.0.3 + '@vitest/pretty-format': 2.1.1 loupe: 3.1.1 tinyrainbow: 1.2.0 @@ -5577,8 +4998,6 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} anymatch@3.1.3: @@ -5601,35 +5020,8 @@ snapshots: argparse@2.0.1: {} - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - array-union@2.1.0: {} - array.prototype.flat@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 - - arrify@1.0.1: {} - - assertion-error@1.1.0: {} - assertion-error@2.0.1: {} async-mutex@0.5.0: @@ -5640,10 +5032,6 @@ snapshots: atomic-sleep@1.0.0: {} - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 - avvio@8.3.2: dependencies: '@fastify/error': 3.4.1 @@ -5698,10 +5086,6 @@ snapshots: dependencies: fill-range: 7.1.1 - breakword@1.0.6: - dependencies: - wcwidth: 1.0.1 - bson@6.7.0: {} buffer-from@1.1.2: {} @@ -5776,26 +5160,8 @@ snapshots: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - camelcase-keys@6.2.2: - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - - camelcase@5.3.1: {} - camelcase@7.0.1: {} - chai@4.4.1: - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.3 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.0.8 - chai@5.1.1: dependencies: assertion-error: 2.0.1 @@ -5819,10 +5185,6 @@ snapshots: chardet@0.7.0: {} - check-error@1.0.3: - dependencies: - get-func-name: 2.0.2 - check-error@2.1.1: {} chokidar@3.6.0: @@ -5861,12 +5223,6 @@ snapshots: cli-width@4.1.0: {} - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -5930,8 +5286,6 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 - confbox@0.1.7: {} - config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -5984,39 +5338,8 @@ snapshots: dependencies: type-fest: 1.4.0 - csv-generate@3.4.3: {} - - csv-parse@4.16.3: {} - - csv-stringify@5.6.5: {} - - csv@5.5.3: - dependencies: - csv-generate: 3.4.3 - csv-parse: 4.16.3 - csv-stringify: 5.6.5 - stream-transform: 2.1.3 - data-uri-to-buffer@4.0.1: {} - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - date-fns@2.30.0: dependencies: '@babel/runtime': 7.24.6 @@ -6033,21 +5356,10 @@ snapshots: dependencies: ms: 2.1.3 - decamelize-keys@1.1.1: - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - - decamelize@1.2.0: {} - decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 - deep-eql@4.1.3: - dependencies: - type-detect: 4.0.8 - deep-eql@5.0.2: {} deep-extend@0.6.0: {} @@ -6064,18 +5376,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.0.1 - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - delegates@1.0.0: {} detect-indent@6.1.0: {} - diff-sequences@29.6.3: {} - diff@4.0.2: {} dir-glob@3.0.1: @@ -6118,85 +5422,12 @@ snapshots: err-code@2.0.3: {} - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - es-abstract@1.23.3: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 es-errors@1.3.0: {} - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - esbuild@0.20.2: optionalDependencies: '@esbuild/aix-ppc64': 0.20.2 @@ -6249,18 +5480,6 @@ snapshots: events@3.3.0: {} - execa@8.0.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - exponential-backoff@3.1.1: {} extendable-error@0.1.7: {} @@ -6357,17 +5576,8 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-yarn-workspace-root2@1.2.16: - dependencies: - micromatch: 4.0.7 - pkg-dir: 4.2.0 - fn.name@1.1.0: {} - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - foreground-child@3.1.1: dependencies: cross-spawn: 7.0.3 @@ -6412,15 +5622,6 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - functions-have-names: 1.2.3 - - functions-have-names@1.2.3: {} - gauge@4.0.4: dependencies: aproba: 2.0.0 @@ -6448,14 +5649,6 @@ snapshots: get-stream@6.0.1: {} - get-stream@8.0.1: {} - - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -6489,11 +5682,6 @@ snapshots: dependencies: ini: 2.0.0 - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.0.1 - globby@11.1.0: dependencies: array-union: 2.1.0 @@ -6527,12 +5715,6 @@ snapshots: graceful-fs@4.2.11: {} - grapheme-splitter@1.0.4: {} - - hard-rejection@2.1.0: {} - - has-bigints@1.0.2: {} - has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -6545,10 +5727,6 @@ snapshots: has-symbols@1.0.3: {} - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 - has-unicode@2.0.1: {} has-yarn@3.0.0: {} @@ -6557,8 +5735,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hosted-git-info@2.8.9: {} - hosted-git-info@5.2.1: dependencies: lru-cache: 7.18.3 @@ -6591,8 +5767,6 @@ snapshots: human-id@1.0.2: {} - human-signals@5.0.0: {} - humanize-ms@1.2.1: dependencies: ms: 2.1.3 @@ -6677,12 +5851,6 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 6.2.0 - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.0.6 - ip-address@9.0.5: dependencies: jsbn: 1.1.0 @@ -6692,30 +5860,12 @@ snapshots: ipaddr.js@2.2.0: {} - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-arrayish@0.2.1: {} - is-arrayish@0.3.2: {} - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.1.2: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - is-ci@3.0.1: dependencies: ci-info: 3.9.0 @@ -6724,15 +5874,7 @@ snapshots: dependencies: hasown: 2.0.2 - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.2 - - is-extglob@2.1.1: {} + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -6749,59 +5891,24 @@ snapshots: is-lambda@1.0.1: {} - is-negative-zero@2.0.3: {} - is-npm@6.0.0: {} - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - is-number@7.0.0: {} is-obj@2.0.0: {} is-path-inside@3.0.3: {} - is-plain-obj@1.1.0: {} - - is-regex@1.1.4: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 - is-stream@2.0.1: {} - is-stream@3.0.0: {} - - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 - is-symbol@1.0.4: - dependencies: - has-symbols: 1.0.3 - - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - is-typedarray@1.0.0: {} is-unicode-supported@0.1.0: {} - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.7 - is-windows@1.0.2: {} is-yarn-global@0.4.1: {} @@ -6810,8 +5917,6 @@ snapshots: isarray@1.0.0: {} - isarray@2.0.5: {} - isexe@2.0.0: {} ix@5.0.0: @@ -6844,8 +5949,6 @@ snapshots: json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@3.0.2: {} json-parse-helpfulerror@1.0.3: @@ -6874,8 +5977,6 @@ snapshots: dependencies: json-buffer: 3.0.1 - kind-of@6.0.3: {} - kleur@4.1.5: {} kuler@2.0.0: {} @@ -6892,17 +5993,6 @@ snapshots: process-warning: 3.0.0 set-cookie-parser: 2.6.0 - lines-and-columns@1.2.4: {} - - load-yaml-file@0.2.0: - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 - - local-pkg@0.4.3: {} - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -6933,10 +6023,6 @@ snapshots: lossless-json@2.0.11: {} - loupe@2.3.7: - dependencies: - get-func-name: 2.0.2 - loupe@3.1.1: dependencies: get-func-name: 2.0.2 @@ -6952,9 +6038,9 @@ snapshots: lru-cache@7.18.3: {} - magic-string@0.30.10: + magic-string@0.30.11: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 make-error@1.3.6: {} @@ -7000,30 +6086,10 @@ snapshots: transitivePeerDependencies: - supports-color - map-obj@1.0.1: {} - - map-obj@4.3.0: {} - map-stream@0.1.0: {} memory-pager@1.5.0: {} - meow@6.1.1: - dependencies: - '@types/minimist': 1.2.5 - camelcase-keys: 6.2.2 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 2.5.0 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.13.1 - yargs-parser: 18.1.3 - - merge-stream@2.0.0: {} - merge2@1.4.1: {} micromatch@4.0.7: @@ -7033,14 +6099,10 @@ snapshots: mimic-fn@2.1.0: {} - mimic-fn@4.0.0: {} - mimic-response@3.1.0: {} mimic-response@4.0.0: {} - min-indent@1.0.1: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -7053,12 +6115,6 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimist-options@4.1.0: - dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 - minimist@1.2.8: {} minipass-collect@1.0.2: @@ -7111,17 +6167,8 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 - mixme@0.5.10: {} - mkdirp@1.0.4: {} - mlly@1.7.0: - dependencies: - acorn: 8.11.3 - pathe: 1.1.2 - pkg-types: 1.1.1 - ufo: 1.5.3 - mnemonist@0.39.5: dependencies: obliterator: 2.0.4 @@ -7143,6 +6190,8 @@ snapshots: moo@0.5.2: {} + mri@1.2.0: {} + ms@2.1.2: {} ms@2.1.3: {} @@ -7209,13 +6258,6 @@ snapshots: dependencies: abbrev: 1.1.1 - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.8 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 - normalize-package-data@5.0.0: dependencies: hosted-git-info: 6.1.1 @@ -7270,6 +6312,8 @@ snapshots: - bluebird - supports-color + npm-check-updates@17.1.2: {} + npm-install-checks@6.3.0: dependencies: semver: 7.6.2 @@ -7306,10 +6350,6 @@ snapshots: transitivePeerDependencies: - supports-color - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - npmlog@6.0.2: dependencies: are-we-there-yet: 3.0.1 @@ -7319,17 +6359,6 @@ snapshots: object-assign@4.1.1: {} - object-inspect@1.13.1: {} - - object-keys@1.1.1: {} - - object.assign@4.1.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - obliterator@2.0.4: {} on-exit-leak-free@2.1.2: {} @@ -7346,10 +6375,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - opentelemetry-instrumentation-fetch-node@1.2.0: dependencies: '@opentelemetry/api': 1.8.0 @@ -7389,10 +6414,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@4.0.0: - dependencies: - yocto-queue: 1.0.0 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -7416,6 +6437,8 @@ snapshots: registry-url: 6.0.1 semver: 7.6.2 + package-manager-detector@0.2.0: {} + pacote@15.2.0: dependencies: '@npmcli/git': 4.1.0 @@ -7442,21 +6465,12 @@ snapshots: parse-github-url@1.0.2: {} - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.24.6 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} path-scurry@1.11.1: @@ -7468,8 +6482,6 @@ snapshots: pathe@1.1.2: {} - pathval@1.1.1: {} - pathval@2.0.0: {} pause-stream@0.0.11: @@ -7497,6 +6509,8 @@ snapshots: picocolors@1.0.1: {} + picocolors@1.1.0: {} + picomatch@2.3.1: {} pify@4.0.1: {} @@ -7522,18 +6536,6 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - - pkg-types@1.1.1: - dependencies: - confbox: 0.1.7 - mlly: 1.7.0 - pathe: 1.1.2 - - possible-typed-array-names@1.0.0: {} - postcss@8.4.38: dependencies: nanoid: 3.3.7 @@ -7550,20 +6552,9 @@ snapshots: dependencies: xtend: 4.0.2 - preferred-pm@3.1.3: - dependencies: - find-up: 5.0.0 - find-yarn-workspace-root2: 1.2.16 - path-exists: 4.0.0 - which-pm: 2.0.0 - prettier@2.8.8: {} - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 + prettier@3.3.3: {} proc-log@3.0.0: {} @@ -7614,8 +6605,6 @@ snapshots: quick-format-unescaped@4.0.4: {} - quick-lru@4.0.1: {} - quick-lru@5.1.1: {} railroad-diagrams@1.0.0: {} @@ -7641,8 +6630,6 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-is@18.3.1: {} - read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 @@ -7655,19 +6642,6 @@ snapshots: normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -7712,20 +6686,8 @@ snapshots: real-require@0.2.0: {} - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - regenerator-runtime@0.14.1: {} - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 - registry-auth-token@5.0.2: dependencies: '@pnpm/npm-conf': 2.2.2 @@ -7748,8 +6710,6 @@ snapshots: transitivePeerDependencies: - supports-color - require-main-filename@2.0.0: {} - resolve-alpn@1.2.1: {} resolve-from@5.0.0: {} @@ -7829,23 +6789,10 @@ snapshots: dependencies: tslib: 2.6.2 - safe-array-concat@1.1.2: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 - safe-buffer@5.1.2: {} safe-buffer@5.2.1: {} - safe-regex-test@1.0.3: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 - safe-regex2@2.0.0: dependencies: ret: 0.2.2 @@ -7862,8 +6809,6 @@ snapshots: semver-utils@1.1.4: {} - semver@5.7.2: {} - semver@7.6.2: {} set-blocking@2.0.0: {} @@ -7879,13 +6824,6 @@ snapshots: gopd: 1.0.1 has-property-descriptors: 1.0.2 - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -7902,13 +6840,6 @@ snapshots: shimmer@1.2.1: {} - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 - siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -7939,15 +6870,6 @@ snapshots: smart-buffer@4.2.0: {} - smartwrap@2.0.2: - dependencies: - array.prototype.flat: 1.3.2 - breakword: 1.0.6 - grapheme-splitter: 1.0.4 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - yargs: 15.4.1 - socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 @@ -8031,10 +6953,6 @@ snapshots: dependencies: duplexer: 0.1.2 - stream-transform@2.1.3: - dependencies: - mixme: 0.5.10 - string-argv@0.3.2: {} string-width@4.2.3: @@ -8049,25 +6967,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - string_decoder@0.10.31: {} string_decoder@1.1.1: @@ -8088,20 +6987,10 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@3.0.0: {} - - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - strip-json-comments@2.0.1: {} strip-json-comments@5.0.1: {} - strip-literal@1.3.0: - dependencies: - acorn: 8.11.3 - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -8140,16 +7029,14 @@ snapshots: through@2.3.8: {} - tinybench@2.8.0: {} + tinybench@2.9.0: {} - tinypool@0.7.0: {} + tinyexec@0.3.0: {} tinypool@1.0.1: {} tinyrainbow@1.2.0: {} - tinyspy@2.2.1: {} - tinyspy@3.0.0: {} tmp@0.0.33: @@ -8170,13 +7057,11 @@ snapshots: tree-kill@1.2.2: {} - trim-newlines@3.0.1: {} - triple-beam@1.4.1: {} ts-codec@1.2.2: {} - ts-node-dev@2.0.0(@types/node@18.11.11)(typescript@5.2.2): + ts-node-dev@2.0.0(@types/node@22.5.5)(typescript@5.6.2): dependencies: chokidar: 3.6.0 dynamic-dedupe: 0.3.0 @@ -8186,65 +7071,43 @@ snapshots: rimraf: 2.7.1 source-map-support: 0.5.21 tree-kill: 1.2.2 - ts-node: 10.9.2(@types/node@18.11.11)(typescript@5.2.2) + ts-node: 10.9.2(@types/node@22.5.5)(typescript@5.6.2) tsconfig: 7.0.0 - typescript: 5.2.2 + typescript: 5.6.2 transitivePeerDependencies: - '@swc/core' - '@swc/wasm' - '@types/node' - ts-node@10.9.2(@types/node@18.11.11)(typescript@5.2.2): + ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 18.11.11 + '@types/node': 22.5.5 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.2.2 + typescript: 5.6.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.2(@types/node@18.19.50)(typescript@5.2.2): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.50 - acorn: 8.11.3 - acorn-walk: 8.3.2 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.2.2 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - tsc-watch@6.2.0(typescript@5.2.2): + tsc-watch@6.2.0(typescript@5.6.2): dependencies: cross-spawn: 7.0.3 node-cleanup: 2.1.2 ps-tree: 1.2.0 string-argv: 0.3.2 - typescript: 5.2.2 + typescript: 5.6.2 - tsconfck@3.0.3(typescript@5.2.2): + tsconfck@3.0.3(typescript@5.6.2): optionalDependencies: - typescript: 5.2.2 - - tsconfck@3.0.3(typescript@5.4.5): - optionalDependencies: - typescript: 5.4.5 + typescript: 5.6.2 tsconfig@7.0.0: dependencies: @@ -8255,16 +7118,6 @@ snapshots: tslib@2.6.2: {} - tty-table@4.2.3: - dependencies: - chalk: 4.1.2 - csv: 5.5.3 - kleur: 4.1.5 - smartwrap: 2.0.2 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - yargs: 17.7.2 - tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 @@ -8273,72 +7126,21 @@ snapshots: transitivePeerDependencies: - supports-color - type-detect@4.0.8: {} - - type-fest@0.13.1: {} - type-fest@0.21.3: {} - type-fest@0.6.0: {} - - type-fest@0.8.1: {} - type-fest@1.4.0: {} type-fest@2.19.0: {} - typed-array-buffer@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 - - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-length@1.0.6: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 - typedarray-to-buffer@3.1.5: dependencies: is-typedarray: 1.0.0 - typescript@5.2.2: {} - - typescript@5.4.5: {} - - ufo@1.5.3: {} - - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + typescript@5.6.2: {} undefsafe@2.0.5: {} - undici-types@5.26.5: {} + undici-types@6.19.8: {} unique-filename@2.0.1: dependencies: @@ -8400,14 +7202,12 @@ snapshots: vary@1.1.2: {} - vite-node@0.34.6(@types/node@18.11.11): + vite-node@2.1.1(@types/node@22.5.5): dependencies: cac: 6.7.14 debug: 4.3.7 - mlly: 1.7.0 pathe: 1.1.2 - picocolors: 1.0.1 - vite: 5.2.11(@types/node@18.11.11) + vite: 5.2.11(@types/node@22.5.5) transitivePeerDependencies: - '@types/node' - less @@ -8418,124 +7218,53 @@ snapshots: - supports-color - terser - vite-node@2.0.5(@types/node@18.19.50): - dependencies: - cac: 6.7.14 - debug: 4.3.7 - pathe: 1.1.2 - tinyrainbow: 1.2.0 - vite: 5.2.11(@types/node@18.19.50) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - - vite-tsconfig-paths@4.3.2(typescript@5.2.2)(vite@5.2.11(@types/node@18.11.11)): - dependencies: - debug: 4.3.7 - globrex: 0.1.2 - tsconfck: 3.0.3(typescript@5.2.2) - optionalDependencies: - vite: 5.2.11(@types/node@18.11.11) - transitivePeerDependencies: - - supports-color - - typescript - - vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.11(@types/node@18.11.11)): + vite-tsconfig-paths@4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)): dependencies: debug: 4.3.7 globrex: 0.1.2 - tsconfck: 3.0.3(typescript@5.4.5) + tsconfck: 3.0.3(typescript@5.6.2) optionalDependencies: - vite: 5.2.11(@types/node@18.11.11) + vite: 5.2.11(@types/node@22.5.5) transitivePeerDependencies: - supports-color - typescript - vite@5.2.11(@types/node@18.11.11): - dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 - optionalDependencies: - '@types/node': 18.11.11 - fsevents: 2.3.3 - - vite@5.2.11(@types/node@18.19.50): + vite@5.2.11(@types/node@22.5.5): dependencies: esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.18.0 optionalDependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 fsevents: 2.3.3 - vitest@0.34.6: - dependencies: - '@types/chai': 4.3.16 - '@types/chai-subset': 1.3.5 - '@types/node': 18.11.11 - '@vitest/expect': 0.34.6 - '@vitest/runner': 0.34.6 - '@vitest/snapshot': 0.34.6 - '@vitest/spy': 0.34.6 - '@vitest/utils': 0.34.6 - acorn: 8.11.3 - acorn-walk: 8.3.2 - cac: 6.7.14 - chai: 4.4.1 - debug: 4.3.4(supports-color@5.5.0) - local-pkg: 0.4.3 - magic-string: 0.30.10 - pathe: 1.1.2 - picocolors: 1.0.1 - std-env: 3.7.0 - strip-literal: 1.3.0 - tinybench: 2.8.0 - tinypool: 0.7.0 - vite: 5.2.11(@types/node@18.11.11) - vite-node: 0.34.6(@types/node@18.11.11) - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - - vitest@2.0.5(@types/node@18.19.50): + vitest@2.1.1(@types/node@22.5.5): dependencies: - '@ampproject/remapping': 2.3.0 - '@vitest/expect': 2.0.5 - '@vitest/pretty-format': 2.0.5 - '@vitest/runner': 2.0.5 - '@vitest/snapshot': 2.0.5 - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 + '@vitest/expect': 2.1.1 + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.2.11(@types/node@22.5.5)) + '@vitest/pretty-format': 2.1.1 + '@vitest/runner': 2.1.1 + '@vitest/snapshot': 2.1.1 + '@vitest/spy': 2.1.1 + '@vitest/utils': 2.1.1 chai: 5.1.1 debug: 4.3.7 - execa: 8.0.1 - magic-string: 0.30.10 + magic-string: 0.30.11 pathe: 1.1.2 std-env: 3.7.0 - tinybench: 2.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.2.11(@types/node@18.19.50) - vite-node: 2.0.5(@types/node@18.19.50) + vite: 5.2.11(@types/node@22.5.5) + vite-node: 2.1.1(@types/node@22.5.5) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 18.19.50 + '@types/node': 22.5.5 transitivePeerDependencies: - less - lightningcss + - msw - sass - stylus - sugarss @@ -8555,29 +7284,6 @@ snapshots: tr46: 4.1.1 webidl-conversions: 7.0.0 - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - which-module@2.0.1: {} - - which-pm@2.0.0: - dependencies: - load-yaml-file: 0.2.0 - path-exists: 4.0.0 - - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - which@1.3.1: dependencies: isexe: 2.0.0 @@ -8590,11 +7296,6 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@2.2.2: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -8663,8 +7364,6 @@ snapshots: xtend@4.0.2: {} - y18n@4.0.3: {} - y18n@5.0.8: {} yallist@2.1.2: {} @@ -8675,29 +7374,10 @@ snapshots: yaml@2.5.0: {} - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - yargs-parser@20.2.9: {} yargs-parser@21.1.1: {} - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 - yargs@16.2.0: dependencies: cliui: 7.0.4 @@ -8722,6 +7402,4 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.0.0: {} - zod@3.23.8: {} diff --git a/service/package.json b/service/package.json index 12b458d3a..4fe401bea 100644 --- a/service/package.json +++ b/service/package.json @@ -47,7 +47,7 @@ "nodemon": "^3.0.1", "npm-check-updates": "^16.14.4", "ts-node": "^10.9.1", - "typescript": "~5.2.2", - "vitest": "^0.34.6" + "typescript": "^5.6.2", + "vitest": "^2.1.1" } } diff --git a/test-client/package.json b/test-client/package.json index 322bb8811..830a123db 100644 --- a/test-client/package.json +++ b/test-client/package.json @@ -21,6 +21,6 @@ }, "devDependencies": { "@types/node": "18.11.11", - "typescript": "^5.2.2" + "typescript": "^5.6.2" } } From 11cf15e4df150c59764d903147248347f604db29 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 18 Sep 2024 12:17:34 +0200 Subject: [PATCH 201/298] Apply prettier changes. --- .prettierignore | 5 +++++ README.md | 5 +++-- .../module-postgres/src/replication/PgManager.ts | 5 ++++- .../module-postgres/src/replication/PgRelation.ts | 2 +- .../module-postgres/test/src/wal_stream_utils.ts | 5 ++++- packages/jpgwire/ca/README.md | 14 +++++++------- packages/jsonbig/README.md | 3 +++ .../service-core/src/auth/RemoteJWKSCollector.ts | 5 ++++- .../src/storage/mongo/MongoCompactor.ts | 6 +++++- .../mongo/MongoPersistedSyncRulesContent.ts | 5 ++++- .../src/storage/mongo/MongoSyncRulesLock.ts | 6 +++++- .../src/storage/mongo/PersistedBatch.ts | 5 ++++- packages/sync-rules/src/SqlBucketDescriptor.ts | 5 ++++- packages/sync-rules/src/TableQuerySchema.ts | 5 ++++- packages/sync-rules/src/errors.ts | 11 +++++++++-- service/README.md | 4 ++-- 16 files changed, 68 insertions(+), 23 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..a9893037d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +**/.git +**/node_modules +dist +lib +pnpm-lock.yaml diff --git a/README.md b/README.md index 303db1497..29d9a9149 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ _[PowerSync](https://www.powersync.com) is a Postgres-SQLite sync engine, which The service can be started using the public Docker image. See the image [notes](./service/README.md) # Monorepo Structure: + ## Packages - [packages/service-core](./packages/service-core/README.md) @@ -52,13 +53,13 @@ Contains the PowerSync service code. This project is used to build the `journeya - [docs](./docs/README.md) -Technical documentation regarding the implementation of PowerSync. +Technical documentation regarding the implementation of PowerSync. ## Test Client - [test-client](./test-client/README.md) -Contains a minimal client demonstrating direct usage of the HTTP stream sync API. This can be used to test sync rules in contexts such as automated testing. +Contains a minimal client demonstrating direct usage of the HTTP stream sync API. This can be used to test sync rules in contexts such as automated testing. # Developing diff --git a/modules/module-postgres/src/replication/PgManager.ts b/modules/module-postgres/src/replication/PgManager.ts index 41b064110..f89e14496 100644 --- a/modules/module-postgres/src/replication/PgManager.ts +++ b/modules/module-postgres/src/replication/PgManager.ts @@ -9,7 +9,10 @@ export class PgManager { private connectionPromises: Promise[] = []; - constructor(public options: NormalizedPostgresConnectionConfig, public poolOptions: pgwire.PgPoolOptions) { + constructor( + public options: NormalizedPostgresConnectionConfig, + public poolOptions: pgwire.PgPoolOptions + ) { // The pool is lazy - no connections are opened until a query is performed. this.pool = pgwire.connectPgWirePool(this.options, poolOptions); } diff --git a/modules/module-postgres/src/replication/PgRelation.ts b/modules/module-postgres/src/replication/PgRelation.ts index 8eebd0f03..f6d9ac900 100644 --- a/modules/module-postgres/src/replication/PgRelation.ts +++ b/modules/module-postgres/src/replication/PgRelation.ts @@ -9,7 +9,7 @@ export function getReplicaIdColumns(relation: PgoutputRelation): storage.ColumnD } else { return relation.columns .filter((c) => (c.flags & 0b1) != 0) - .map((c) => ({ name: c.name, typeId: c.typeOid } satisfies storage.ColumnDescriptor)); + .map((c) => ({ name: c.name, typeId: c.typeOid }) satisfies storage.ColumnDescriptor); } } export function getRelId(source: PgoutputRelation): number { diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index d9e4f2609..8af286baa 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -36,7 +36,10 @@ export class WalStreamTestContext { public storage?: SyncRulesBucketStorage; private replicationConnection?: pgwire.PgConnection; - constructor(public factory: BucketStorageFactory, public connectionManager: PgManager) {} + constructor( + public factory: BucketStorageFactory, + public connectionManager: PgManager + ) {} async dispose() { this.abortController.abort(); diff --git a/packages/jpgwire/ca/README.md b/packages/jpgwire/ca/README.md index 6e406e6e2..6bd7a532b 100644 --- a/packages/jpgwire/ca/README.md +++ b/packages/jpgwire/ca/README.md @@ -1,4 +1,3 @@ - ## AWS RDS https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.SSL.html#UsingWithRDS.SSL.CertificatesAllRegions @@ -11,12 +10,13 @@ https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/how-to-connec https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-certificate-rotation Includes: - * BaltimoreCyberTrustRoot - * DigiCertGlobalRootG2 Root CA - * Microsoft RSA Root Certificate Authority 2017 - * Microsoft ECC Root Certificate Authority 2017 - * DigiCert Global Root G3 - * DigiCert Global Root CA + +- BaltimoreCyberTrustRoot +- DigiCertGlobalRootG2 Root CA +- Microsoft RSA Root Certificate Authority 2017 +- Microsoft ECC Root Certificate Authority 2017 +- DigiCert Global Root G3 +- DigiCert Global Root CA ## Supabase diff --git a/packages/jsonbig/README.md b/packages/jsonbig/README.md index f18b78151..3490fb45a 100644 --- a/packages/jsonbig/README.md +++ b/packages/jsonbig/README.md @@ -1,6 +1,7 @@ # powersync-jsonbig JSON is used everywhere, including: + 1. PostgreSQL (json/jsonb types) 2. Sync rules input (values are normalized to JSON text). 3. Sync rule transformations (extracting values, constructing objects in the future) @@ -9,10 +10,12 @@ JSON is used everywhere, including: Where we can, JSON data is kept as strings and not parsed. This is so that: + 1. We don't add parsing / serializing overhead. 2. We don't change the data. Specifically: + 1. The SQLite type system makes a distinction between INTEGER and REAL values. We try to preserve this. 2. Integers in SQLite can be up to 64-bit. diff --git a/packages/service-core/src/auth/RemoteJWKSCollector.ts b/packages/service-core/src/auth/RemoteJWKSCollector.ts index 8971c7c5b..1419d3a49 100644 --- a/packages/service-core/src/auth/RemoteJWKSCollector.ts +++ b/packages/service-core/src/auth/RemoteJWKSCollector.ts @@ -22,7 +22,10 @@ export type RemoteJWKSCollectorOptions = { export class RemoteJWKSCollector implements KeyCollector { private url: URL; - constructor(url: string, protected options?: RemoteJWKSCollectorOptions) { + constructor( + url: string, + protected options?: RemoteJWKSCollectorOptions + ) { try { this.url = new URL(url); } catch (e) { diff --git a/packages/service-core/src/storage/mongo/MongoCompactor.ts b/packages/service-core/src/storage/mongo/MongoCompactor.ts index ff754973d..07c946bae 100644 --- a/packages/service-core/src/storage/mongo/MongoCompactor.ts +++ b/packages/service-core/src/storage/mongo/MongoCompactor.ts @@ -58,7 +58,11 @@ export class MongoCompactor { private maxOpId: bigint | undefined; private buckets: string[] | undefined; - constructor(private db: PowerSyncMongo, private group_id: number, options?: MongoCompactOptions) { + constructor( + private db: PowerSyncMongo, + private group_id: number, + options?: MongoCompactOptions + ) { this.idLimitBytes = (options?.memoryLimitMB ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024; this.moveBatchLimit = options?.moveBatchLimit ?? DEFAULT_MOVE_BATCH_LIMIT; this.moveBatchQueryLimit = options?.moveBatchQueryLimit ?? DEFAULT_MOVE_BATCH_QUERY_LIMIT; diff --git a/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts b/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts index 363766df4..0a68fe29b 100644 --- a/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts +++ b/packages/service-core/src/storage/mongo/MongoPersistedSyncRulesContent.ts @@ -19,7 +19,10 @@ export class MongoPersistedSyncRulesContent implements PersistedSyncRulesContent public current_lock: MongoSyncRulesLock | null = null; - constructor(private db: PowerSyncMongo, doc: mongo.WithId) { + constructor( + private db: PowerSyncMongo, + doc: mongo.WithId + ) { this.id = doc._id; this.sync_rules_content = doc.content; this.last_checkpoint_lsn = doc.last_checkpoint_lsn; diff --git a/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts b/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts index dd2c055e3..d693ad905 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts @@ -35,7 +35,11 @@ export class MongoSyncRulesLock implements ReplicationLock { return new MongoSyncRulesLock(db, sync_rules.id, lockId); } - constructor(private db: PowerSyncMongo, public sync_rules_id: number, private lock_id: string) { + constructor( + private db: PowerSyncMongo, + public sync_rules_id: number, + private lock_id: string + ) { this.refreshInterval = setInterval(async () => { try { await this.refresh(); diff --git a/packages/service-core/src/storage/mongo/PersistedBatch.ts b/packages/service-core/src/storage/mongo/PersistedBatch.ts index 58b0be659..106dcadfd 100644 --- a/packages/service-core/src/storage/mongo/PersistedBatch.ts +++ b/packages/service-core/src/storage/mongo/PersistedBatch.ts @@ -54,7 +54,10 @@ export class PersistedBatch { */ currentSize = 0; - constructor(private group_id: number, writtenSize: number) { + constructor( + private group_id: number, + writtenSize: number + ) { this.currentSize = writtenSize; } diff --git a/packages/sync-rules/src/SqlBucketDescriptor.ts b/packages/sync-rules/src/SqlBucketDescriptor.ts index a140a1fc2..0af7b1e33 100644 --- a/packages/sync-rules/src/SqlBucketDescriptor.ts +++ b/packages/sync-rules/src/SqlBucketDescriptor.ts @@ -30,7 +30,10 @@ export class SqlBucketDescriptor { name: string; bucket_parameters?: string[]; - constructor(name: string, public idSequence: IdSequence) { + constructor( + name: string, + public idSequence: IdSequence + ) { this.name = name; } diff --git a/packages/sync-rules/src/TableQuerySchema.ts b/packages/sync-rules/src/TableQuerySchema.ts index 748ebaab6..01a7a8cf4 100644 --- a/packages/sync-rules/src/TableQuerySchema.ts +++ b/packages/sync-rules/src/TableQuerySchema.ts @@ -2,7 +2,10 @@ import { ColumnDefinition, ExpressionType } from './ExpressionType.js'; import { QuerySchema, SourceSchemaTable } from './types.js'; export class TableQuerySchema implements QuerySchema { - constructor(private tables: SourceSchemaTable[], private alias: string) {} + constructor( + private tables: SourceSchemaTable[], + private alias: string + ) {} getType(table: string, column: string): ExpressionType { if (table != this.alias) { diff --git a/packages/sync-rules/src/errors.ts b/packages/sync-rules/src/errors.ts index d92da1c30..88a86547f 100644 --- a/packages/sync-rules/src/errors.ts +++ b/packages/sync-rules/src/errors.ts @@ -28,7 +28,11 @@ export class SqlRuleError extends Error { location?: ErrorLocation; type: 'warning' | 'fatal' = 'fatal'; - constructor(message: string, public sql: string, location?: NodeLocation | Expr) { + constructor( + message: string, + public sql: string, + location?: NodeLocation | Expr + ) { super(message); this.location = getLocation(location) ?? { start: 0, end: sql.length }; @@ -39,7 +43,10 @@ export class YamlError extends Error { location: ErrorLocation; type: 'warning' | 'fatal' = 'fatal'; - constructor(public source: Error, location?: ErrorLocation) { + constructor( + public source: Error, + location?: ErrorLocation + ) { super(source.message); if (location == null && source instanceof yaml.YAMLError) { diff --git a/service/README.md b/service/README.md index 7f1655179..01f7f44b3 100644 --- a/service/README.md +++ b/service/README.md @@ -2,7 +2,7 @@

-*[PowerSync](https://www.powersync.com) is a Postgres-SQLite sync engine, which helps developers to create local-first real-time reactive apps that work seamlessly both online and offline.* +_[PowerSync](https://www.powersync.com) is a Postgres-SQLite sync engine, which helps developers to create local-first real-time reactive apps that work seamlessly both online and offline._ # Quick reference @@ -48,4 +48,4 @@ View [license information](https://github.com/powersync-ja/powersync-service/blo As with all Docker images, these likely also contain other software which may be under other licenses (such as Bash, etc from the base distribution, along with any direct or indirect dependencies of the primary software being contained). -As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. \ No newline at end of file +As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within. From d279db17175433ddcf57d9d6c475adbdc7f72eac Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 18 Sep 2024 12:26:30 +0200 Subject: [PATCH 202/298] Fix type issues. --- packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts b/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts index d693ad905..76fee7e1e 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncRulesLock.ts @@ -9,7 +9,7 @@ import { logger } from '@powersync/lib-services-framework'; * replicates those sync rules at a time. */ export class MongoSyncRulesLock implements ReplicationLock { - private readonly refreshInterval: NodeJS.Timer; + private readonly refreshInterval: NodeJS.Timeout; static async createLock(db: PowerSyncMongo, sync_rules: PersistedSyncRulesContent): Promise { const lockId = crypto.randomBytes(8).toString('hex'); From f4331954caae893822076cc1be2c80d0fd322d5d Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 18 Sep 2024 12:31:20 +0200 Subject: [PATCH 203/298] Fix tests. --- modules/module-postgres/package.json | 2 +- .../src/utils/populate_test_data.ts | 44 +--------------- .../src/utils/populate_test_data_worker.ts | 50 ++++++++++++++++++ modules/module-postgres/vitest.config.ts | 8 ++- packages/service-core/package.json | 2 +- .../test/src/__snapshots__/sync.test.ts.snap | 14 ++--- packages/service-core/vitest.config.ts | 8 ++- pnpm-lock.yaml | 52 +------------------ test-client/package.json | 2 +- 9 files changed, 78 insertions(+), 104 deletions(-) create mode 100644 modules/module-postgres/src/utils/populate_test_data_worker.ts diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index 10fa404c8..aad70aba3 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -13,7 +13,7 @@ "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", "clean": "rm -rf ./lib && tsc -b --clean", - "test": "vitest --no-threads" + "test": "vitest" }, "exports": { ".": { diff --git a/modules/module-postgres/src/utils/populate_test_data.ts b/modules/module-postgres/src/utils/populate_test_data.ts index be4c67729..1d1c15de8 100644 --- a/modules/module-postgres/src/utils/populate_test_data.ts +++ b/modules/module-postgres/src/utils/populate_test_data.ts @@ -1,5 +1,4 @@ -import * as crypto from 'crypto'; -import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads'; +import { Worker } from 'node:worker_threads'; import * as pgwire from '@powersync/service-jpgwire'; @@ -12,49 +11,10 @@ export interface PopulateDataOptions { size: number; } -if (isMainThread || parentPort == null) { - // Not a worker - ignore -} else { - try { - const options = workerData as PopulateDataOptions; - - const result = await populateDataInner(options); - parentPort.postMessage(result); - process.exit(0); - } catch (e) { - // This is a bug, not a connection issue - console.error(e); - // Only closes the Worker thread - process.exit(2); - } -} - -async function populateDataInner(options: PopulateDataOptions) { - // Dedicated connection so we can release the memory easily - const initialDb = await pgwire.connectPgWire(options.connection, { type: 'standard' }); - const largeDescription = crypto.randomBytes(options.size / 2).toString('hex'); - let operation_count = 0; - for (let i = 0; i < options.num_transactions; i++) { - const prefix = `test${i}K`; - - await initialDb.query({ - statement: `INSERT INTO test_data(id, description, other) SELECT $1 || i, $2, 'foo' FROM generate_series(1, $3) i`, - params: [ - { type: 'varchar', value: prefix }, - { type: 'varchar', value: largeDescription }, - { type: 'int4', value: options.per_transaction } - ] - }); - operation_count += options.per_transaction; - } - await initialDb.end(); - return operation_count; -} - export async function populateData(options: PopulateDataOptions) { const WORKER_TIMEOUT = 30_000; - const worker = new Worker(new URL('./populate_test_data.js', import.meta.url), { + const worker = new Worker(new URL('./populate_test_data_worker.js', import.meta.url), { workerData: options }); const timeout = setTimeout(() => { diff --git a/modules/module-postgres/src/utils/populate_test_data_worker.ts b/modules/module-postgres/src/utils/populate_test_data_worker.ts new file mode 100644 index 000000000..5fd161103 --- /dev/null +++ b/modules/module-postgres/src/utils/populate_test_data_worker.ts @@ -0,0 +1,50 @@ +import * as crypto from 'crypto'; +import { isMainThread, parentPort, workerData } from 'node:worker_threads'; + +import * as pgwire from '@powersync/service-jpgwire'; +import type { PopulateDataOptions } from './populate_test_data.js'; + +// This util is actually for tests only, but we need it compiled to JS for the service to work, so it's placed in the service. + +if (isMainThread || parentPort == null) { + // Must not be imported - only expected to run in a worker + throw new Error('Do not import this file'); +} else { + try { + const options = workerData as PopulateDataOptions; + if (options == null) { + throw new Error('loaded worker without options'); + } + + const result = await populateDataInner(options); + parentPort.postMessage(result); + process.exit(0); + } catch (e) { + // This is a bug, not a connection issue + console.error(e); + // Only closes the Worker thread + process.exit(2); + } +} + +async function populateDataInner(options: PopulateDataOptions) { + // Dedicated connection so we can release the memory easily + const initialDb = await pgwire.connectPgWire(options.connection, { type: 'standard' }); + const largeDescription = crypto.randomBytes(options.size / 2).toString('hex'); + let operation_count = 0; + for (let i = 0; i < options.num_transactions; i++) { + const prefix = `test${i}K`; + + await initialDb.query({ + statement: `INSERT INTO test_data(id, description, other) SELECT $1 || i, $2, 'foo' FROM generate_series(1, $3) i`, + params: [ + { type: 'varchar', value: prefix }, + { type: 'varchar', value: largeDescription }, + { type: 'int4', value: options.per_transaction } + ] + }); + operation_count += options.per_transaction; + } + await initialDb.end(); + return operation_count; +} diff --git a/modules/module-postgres/vitest.config.ts b/modules/module-postgres/vitest.config.ts index b392696b7..7a39c1f71 100644 --- a/modules/module-postgres/vitest.config.ts +++ b/modules/module-postgres/vitest.config.ts @@ -4,6 +4,12 @@ import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [tsconfigPaths()], test: { - setupFiles: './test/src/setup.ts' + setupFiles: './test/src/setup.ts', + poolOptions: { + threads: { + singleThread: true + } + }, + pool: 'threads' } }); diff --git a/packages/service-core/package.json b/packages/service-core/package.json index 2e6f1d58a..ec7b14cd3 100644 --- a/packages/service-core/package.json +++ b/packages/service-core/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", - "test": "vitest --no-threads", + "test": "vitest", "clean": "rm -rf ./lib && tsc -b --clean" }, "dependencies": { diff --git a/packages/service-core/test/src/__snapshots__/sync.test.ts.snap b/packages/service-core/test/src/__snapshots__/sync.test.ts.snap index f1e9b6762..233f10df8 100644 --- a/packages/service-core/test/src/__snapshots__/sync.test.ts.snap +++ b/packages/service-core/test/src/__snapshots__/sync.test.ts.snap @@ -56,7 +56,7 @@ exports[`sync - mongodb > compacting data - invalidate checkpoint 2`] = ` "data": [ { "checksum": 1859363232n, - "data": "{\\"id\\":\\"t1\\",\\"description\\":\\"Test 1b\\"}", + "data": "{"id":"t1","description":"Test 1b"}", "object_id": "t1", "object_type": "test", "op": "PUT", @@ -65,7 +65,7 @@ exports[`sync - mongodb > compacting data - invalidate checkpoint 2`] = ` }, { "checksum": 3028503153n, - "data": "{\\"id\\":\\"t2\\",\\"description\\":\\"Test 2b\\"}", + "data": "{"id":"t2","description":"Test 2b"}", "object_id": "t2", "object_type": "test", "op": "PUT", @@ -146,7 +146,7 @@ exports[`sync - mongodb > sync global data 1`] = ` "data": [ { "checksum": 920318466n, - "data": "{\\"id\\":\\"t1\\",\\"description\\":\\"Test 1\\"}", + "data": "{"id":"t1","description":"Test 1"}", "object_id": "t1", "object_type": "test", "op": "PUT", @@ -155,7 +155,7 @@ exports[`sync - mongodb > sync global data 1`] = ` }, { "checksum": 3280762209n, - "data": "{\\"id\\":\\"t2\\",\\"description\\":\\"Test 2\\"}", + "data": "{"id":"t2","description":"Test 2"}", "object_id": "t2", "object_type": "test", "op": "PUT", @@ -199,7 +199,7 @@ exports[`sync - mongodb > sync legacy non-raw data 1`] = ` "checksum": 3442149460n, "data": { "description": "Test -\\"string\\"", +"string"", "id": "t1", "large_num": 12345678901234567890n, }, @@ -268,7 +268,7 @@ exports[`sync - mongodb > sync updates to global data 2`] = ` "data": [ { "checksum": 920318466n, - "data": "{\\"id\\":\\"t1\\",\\"description\\":\\"Test 1\\"}", + "data": "{"id":"t1","description":"Test 1"}", "object_id": "t1", "object_type": "test", "op": "PUT", @@ -311,7 +311,7 @@ exports[`sync - mongodb > sync updates to global data 3`] = ` "data": [ { "checksum": 3280762209n, - "data": "{\\"id\\":\\"t2\\",\\"description\\":\\"Test 2\\"}", + "data": "{"id":"t2","description":"Test 2"}", "object_id": "t2", "object_type": "test", "op": "PUT", diff --git a/packages/service-core/vitest.config.ts b/packages/service-core/vitest.config.ts index b392696b7..7a39c1f71 100644 --- a/packages/service-core/vitest.config.ts +++ b/packages/service-core/vitest.config.ts @@ -4,6 +4,12 @@ import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [tsconfigPaths()], test: { - setupFiles: './test/src/setup.ts' + setupFiles: './test/src/setup.ts', + poolOptions: { + threads: { + singleThread: true + } + }, + pool: 'threads' } }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 01125d62f..beb4d92be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,49 +94,6 @@ importers: specifier: ^2.1.1 version: 2.1.1(@types/node@22.5.5) - modules/module-mongodb: - dependencies: - '@powersync/lib-services-framework': - specifier: workspace:* - version: link:../../libs/lib-services - '@powersync/service-core': - specifier: workspace:* - version: link:../../packages/service-core - '@powersync/service-jsonbig': - specifier: workspace:* - version: link:../../packages/jsonbig - '@powersync/service-sync-rules': - specifier: workspace:* - version: link:../../packages/sync-rules - '@powersync/service-types': - specifier: workspace:* - version: link:../../packages/types - mongodb: - specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) - ts-codec: - specifier: ^1.2.2 - version: 1.2.2 - uri-js: - specifier: ^4.4.1 - version: 4.4.1 - uuid: - specifier: ^9.0.1 - version: 9.0.1 - devDependencies: - '@types/uuid': - specifier: ^9.0.4 - version: 9.0.8 - typescript: - specifier: ^5.6.2 - version: 5.6.2 - vite-tsconfig-paths: - specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) - vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5) - modules/module-postgres: dependencies: '@powersync/lib-services-framework': @@ -521,8 +478,8 @@ importers: version: 2.5.0 devDependencies: '@types/node': - specifier: 18.11.11 - version: 18.11.11 + specifier: ^22.5.5 + version: 22.5.5 typescript: specifier: ^5.6.2 version: 5.6.2 @@ -1356,9 +1313,6 @@ packages: '@types/node@15.14.9': resolution: {integrity: sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==} - '@types/node@18.11.11': - resolution: {integrity: sha512-KJ021B1nlQUBLopzZmPBVuGU9un7WJd/W4ya7Ih02B4Uwky5Nja0yGYav2EfYIk0RR2Q9oVhf60S2XR1BCWJ2g==} - '@types/node@22.5.5': resolution: {integrity: sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==} @@ -4830,8 +4784,6 @@ snapshots: '@types/node@15.14.9': {} - '@types/node@18.11.11': {} - '@types/node@22.5.5': dependencies: undici-types: 6.19.8 diff --git a/test-client/package.json b/test-client/package.json index 830a123db..075a7bb32 100644 --- a/test-client/package.json +++ b/test-client/package.json @@ -20,7 +20,7 @@ "yaml": "^2.5.0" }, "devDependencies": { - "@types/node": "18.11.11", + "@types/node": "^22.5.5", "typescript": "^5.6.2" } } From 6f54a28a0b3b4341a42e17a9896cc18aa0ef80fc Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 19 Sep 2024 17:14:40 +0200 Subject: [PATCH 204/298] Add changeset. --- .changeset/green-peas-roll.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/green-peas-roll.md diff --git a/.changeset/green-peas-roll.md b/.changeset/green-peas-roll.md new file mode 100644 index 000000000..61762c082 --- /dev/null +++ b/.changeset/green-peas-roll.md @@ -0,0 +1,6 @@ +--- +'@powersync/service-module-mongodb': minor +'@powersync/service-image': minor +--- + +Add MongoDB support (Alpha) From e9bf63ec86dad63c2bf9c6795a26d9cdf97df07d Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 23 Sep 2024 12:24:10 +0200 Subject: [PATCH 205/298] Fix aborting logic. --- .../src/replication/ChangeStream.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index f993ec171..7638b6bdf 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -403,6 +403,12 @@ export class ChangeStream { maxAwaitTimeMS: 200, fullDocument: 'updateLookup' }); + + if (this.abort_signal.aborted) { + stream.close(); + return; + } + this.abort_signal.addEventListener('abort', () => { stream.close(); }); @@ -410,16 +416,17 @@ export class ChangeStream { let waitForCheckpointLsn: string | null = null; while (true) { + if (this.abort_signal.aborted) { + break; + } + const changeDocument = await stream.tryNext(); - if (changeDocument == null) { + + if (changeDocument == null || this.abort_signal.aborted) { continue; } await touch(); - if (this.abort_signal.aborted) { - break; - } - if (startAfter != null && changeDocument.clusterTime?.lte(startAfter)) { continue; } From 03a18c78c920cc03036bf01b2c7968e0a5676374 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 23 Sep 2024 12:24:46 +0200 Subject: [PATCH 206/298] Normalize mongo connection parameters. --- modules/module-mongodb/src/types/types.ts | 60 ++----------------- packages/service-core/package.json | 1 + packages/service-core/src/db/mongo.ts | 8 ++- .../service-core/src/storage/mongo/config.ts | 40 +++++++++++++ .../service-core/src/storage/storage-index.ts | 1 + pnpm-lock.yaml | 3 + 6 files changed, 55 insertions(+), 58 deletions(-) create mode 100644 packages/service-core/src/storage/mongo/config.ts diff --git a/modules/module-mongodb/src/types/types.ts b/modules/module-mongodb/src/types/types.ts index 4aeef79d3..572a8b4dd 100644 --- a/modules/module-mongodb/src/types/types.ts +++ b/modules/module-mongodb/src/types/types.ts @@ -1,6 +1,6 @@ +import { normalizeMongoConfig } from '@powersync/service-core'; import * as service_types from '@powersync/service-types'; import * as t from 'ts-codec'; -import * as urijs from 'uri-js'; export const MONGO_CONNECTION_TYPE = 'mongodb' as const; @@ -22,20 +22,10 @@ export const MongoConnectionConfig = service_types.configFile.dataSourceConfig.a id: t.string.optional(), /** Tag used as reference in sync rules. Defaults to "default". Does not have to be unique. */ tag: t.string.optional(), - uri: t.string.optional(), - hostname: t.string.optional(), - port: service_types.configFile.portCodec.optional(), + uri: t.string, username: t.string.optional(), password: t.string.optional(), - database: t.string.optional(), - - /** Defaults to verify-full */ - sslmode: t.literal('verify-full').or(t.literal('verify-ca')).or(t.literal('disable')).optional(), - /** Required for verify-ca, optional for verify-full */ - cacert: t.string.optional(), - - client_certificate: t.string.optional(), - client_private_key: t.string.optional() + database: t.string.optional() }) ); @@ -55,56 +45,16 @@ export type ResolvedConnectionConfig = MongoConnectionConfig & NormalizedMongoCo * Returns destructured options. */ export function normalizeConnectionConfig(options: MongoConnectionConfig): NormalizedMongoConnectionConfig { - let uri: urijs.URIComponents; - if (options.uri) { - uri = urijs.parse(options.uri); - if (uri.scheme != 'mongodb') { - `Invalid URI - protocol must be postgresql, got ${uri.scheme}`; - } - } else { - uri = urijs.parse('mongodb:///'); - } - - const database = options.database ?? uri.path?.substring(1) ?? ''; - - const [uri_username, uri_password] = (uri.userinfo ?? '').split(':'); - - const username = options.username ?? uri_username; - const password = options.password ?? uri_password; - - if (database == '') { - throw new Error(`database required`); - } + const base = normalizeMongoConfig(options); return { id: options.id ?? 'default', tag: options.tag ?? 'default', - // TODO: remove username & password from uri - uri: options.uri ?? '', - database, - - username, - password + ...base }; } -/** - * Check whether the port is in a "safe" range. - * - * We do not support connecting to "privileged" ports. - */ -export function validatePort(port: string | number): number { - if (typeof port == 'string') { - port = parseInt(port); - } - if (port >= 1024 && port <= 49151) { - return port; - } else { - throw new Error(`Port ${port} not supported`); - } -} - /** * Construct a mongodb URI, without username, password or ssl options. * diff --git a/packages/service-core/package.json b/packages/service-core/package.json index 58f691586..025489ead 100644 --- a/packages/service-core/package.json +++ b/packages/service-core/package.json @@ -40,6 +40,7 @@ "mongodb": "^6.7.0", "node-fetch": "^3.3.2", "ts-codec": "^1.2.2", + "uri-js": "^4.4.1", "uuid": "^9.0.1", "winston": "^3.13.0", "yaml": "^2.3.2" diff --git a/packages/service-core/src/db/mongo.ts b/packages/service-core/src/db/mongo.ts index be915bdc3..33d6d5b86 100644 --- a/packages/service-core/src/db/mongo.ts +++ b/packages/service-core/src/db/mongo.ts @@ -2,6 +2,7 @@ import * as mongo from 'mongodb'; import * as timers from 'timers/promises'; import { configFile } from '@powersync/service-types'; +import { normalizeMongoConfig } from '../storage/storage-index.js'; /** * Time for new connection to timeout. @@ -23,10 +24,11 @@ export const MONGO_SOCKET_TIMEOUT_MS = 60_000; export const MONGO_OPERATION_TIMEOUT_MS = 30_000; export function createMongoClient(config: configFile.PowerSyncConfig['storage']) { - return new mongo.MongoClient(config.uri, { + const normalized = normalizeMongoConfig(config); + return new mongo.MongoClient(normalized.uri, { auth: { - username: config.username, - password: config.password + username: normalized.username, + password: normalized.password }, // Time for connection to timeout connectTimeoutMS: MONGO_CONNECT_TIMEOUT_MS, diff --git a/packages/service-core/src/storage/mongo/config.ts b/packages/service-core/src/storage/mongo/config.ts new file mode 100644 index 000000000..8ff241e25 --- /dev/null +++ b/packages/service-core/src/storage/mongo/config.ts @@ -0,0 +1,40 @@ +import * as urijs from 'uri-js'; + +export interface MongoConnectionConfig { + uri: string; + username?: string; + password?: string; + database?: string; +} + +/** + * Validate and normalize connection options. + * + * Returns destructured options. + * + * For use by both storage and mongo module. + */ +export function normalizeMongoConfig(options: MongoConnectionConfig) { + let uri = urijs.parse(options.uri); + + const database = options.database ?? uri.path?.substring(1) ?? ''; + + const userInfo = uri.userinfo?.split(':'); + + const username = options.username ?? userInfo?.[0]; + const password = options.password ?? userInfo?.[1]; + + if (database == '') { + throw new Error(`database required`); + } + + delete uri.userinfo; + + return { + uri: urijs.serialize(uri), + database, + + username, + password + }; +} diff --git a/packages/service-core/src/storage/storage-index.ts b/packages/service-core/src/storage/storage-index.ts index 3c137b8d1..42aec416d 100644 --- a/packages/service-core/src/storage/storage-index.ts +++ b/packages/service-core/src/storage/storage-index.ts @@ -16,3 +16,4 @@ export * from './mongo/MongoSyncRulesLock.js'; export * from './mongo/OperationBatch.js'; export * from './mongo/PersistedBatch.js'; export * from './mongo/util.js'; +export * from './mongo/config.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b8dd719e4..75f8ae93f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -321,6 +321,9 @@ importers: ts-codec: specifier: ^1.2.2 version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 uuid: specifier: ^9.0.1 version: 9.0.1 From f42873655d54c25a27f474a7be224ea93852ff0c Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 23 Sep 2024 15:07:55 +0200 Subject: [PATCH 207/298] Remove error message when closing a ChangStreamReplicationJob. --- .../src/replication/ChangeStreamReplicationJob.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts index 05b6c1e26..fb60e7aa7 100644 --- a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts @@ -79,6 +79,9 @@ export class ChangeStreamReplicationJob extends replication.AbstractReplicationJ }); await stream.replicate(); } catch (e) { + if (this.abortController.signal.aborted) { + return; + } this.logger.error(`Replication error`, e); if (e.cause != null) { // Without this additional log, the cause may not be visible in the logs. From 9de2d35d128b364c09ab5b3d543005f40095bb64 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 23 Sep 2024 15:08:11 +0200 Subject: [PATCH 208/298] Support M0 clusters; better error message on unsupported clusters. --- .../module-mongodb/src/replication/ChangeStream.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 7638b6bdf..323288001 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -147,8 +147,15 @@ export class ChangeStream { const sourceTables = this.sync_rules.getSourceTables(); await this.client.connect(); - const ping = await this.defaultDb.command({ ping: 1 }); - const startTime = ping.$clusterTime.clusterTime as mongo.Timestamp; + const hello = await this.defaultDb.command({ hello: 1 }); + const startTime = hello.lastWrite?.majorityOpTime as mongo.Timestamp; + if (hello.isdbgrid) { + throw new Error('Sharded MongoDB Clusters are not supported yet.'); + } else if (hello.setName == null) { + throw new Error('Standalone MongoDB instances are not supported - use a replicaset.'); + } else if (startTime == null) { + throw new Error('MongoDB lastWrite timestamp not found.'); + } const session = await this.client.startSession({ snapshot: true }); @@ -175,7 +182,7 @@ export class ChangeStream { await batch.flush(); await batch.keepalive(lsn); } else { - logger.info(`No snapshot clusterTime (no snapshot data?) - skipping commit.`); + throw new Error(`No snapshot clusterTime available.`); } } ); From 3185e026f71134e0c5c448bdff44198bf7afc3ea Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 23 Sep 2024 15:16:56 +0200 Subject: [PATCH 209/298] Fix sharded cluster check. --- modules/module-mongodb/src/replication/ChangeStream.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 323288001..9b8f9d5aa 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -149,11 +149,12 @@ export class ChangeStream { const hello = await this.defaultDb.command({ hello: 1 }); const startTime = hello.lastWrite?.majorityOpTime as mongo.Timestamp; - if (hello.isdbgrid) { - throw new Error('Sharded MongoDB Clusters are not supported yet.'); + if (hello.msg == 'isdbgrid') { + throw new Error('Sharded MongoDB Clusters are not supported yet (including MongoDB Serverless instances).'); } else if (hello.setName == null) { throw new Error('Standalone MongoDB instances are not supported - use a replicaset.'); } else if (startTime == null) { + // Not known where this would happen apart from the above cases throw new Error('MongoDB lastWrite timestamp not found.'); } const session = await this.client.startSession({ From 14b83eb4004f15390adf194e26f09b89c0e3959a Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Mon, 23 Sep 2024 17:47:36 +0200 Subject: [PATCH 210/298] Fix initial replication regression. --- modules/module-mongodb/src/replication/ChangeStream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 9b8f9d5aa..92392d863 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -148,7 +148,7 @@ export class ChangeStream { await this.client.connect(); const hello = await this.defaultDb.command({ hello: 1 }); - const startTime = hello.lastWrite?.majorityOpTime as mongo.Timestamp; + const startTime = hello.lastWrite?.majorityOpTime?.ts as mongo.Timestamp; if (hello.msg == 'isdbgrid') { throw new Error('Sharded MongoDB Clusters are not supported yet (including MongoDB Serverless instances).'); } else if (hello.setName == null) { From df2725ac623b031d9e11fb72d0006457120d6909 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 25 Sep 2024 16:54:24 +0200 Subject: [PATCH 211/298] Refactor schema definitions to not be postgres-specific. --- .../src/api/PostgresRouteAPIAdapter.ts | 6 ++- packages/sync-rules/src/ExpressionType.ts | 29 +++++++++- packages/sync-rules/src/StaticSchema.ts | 54 +++++++++++-------- packages/types/src/definitions.ts | 33 +++++++++++- 4 files changed, 93 insertions(+), 29 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 205a2c744..49f53c415 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -275,14 +275,14 @@ GROUP BY schemaname, tablename, quoted_name` ); const rows = pgwire.pgwireRows(results); - let schemas: Record = {}; + let schemas: Record = {}; for (let row of rows) { const schema = (schemas[row.schemaname] ??= { name: row.schemaname, tables: [] }); - const table = { + const table: service_types.DatabaseSchema['tables'][0] = { name: row.tablename, columns: [] as any[] }; @@ -296,7 +296,9 @@ GROUP BY schemaname, tablename, quoted_name` } table.columns.push({ name: column.attname, + sqlite_type: sync_rules.expressionTypeFromPostgresType(pg_type).typeFlags, type: column.data_type, + original_type: column.data_type, pg_type: pg_type }); } diff --git a/packages/sync-rules/src/ExpressionType.ts b/packages/sync-rules/src/ExpressionType.ts index 87b52c6e9..2121abbb0 100644 --- a/packages/sync-rules/src/ExpressionType.ts +++ b/packages/sync-rules/src/ExpressionType.ts @@ -4,7 +4,7 @@ export const TYPE_TEXT = 2; export const TYPE_INTEGER = 4; export const TYPE_REAL = 8; -export type SqliteType = 'null' | 'blob' | 'text' | 'integer' | 'real'; +export type SqliteType = 'null' | 'blob' | 'text' | 'integer' | 'real' | 'numeric'; export interface ColumnDefinition { name: string; @@ -34,7 +34,7 @@ export class ExpressionType { return new ExpressionType(typeFlags); } - static fromTypeText(type: SqliteType | 'numeric') { + static fromTypeText(type: SqliteType) { if (type == 'null') { return ExpressionType.NONE; } else if (type == 'blob') { @@ -72,3 +72,28 @@ export class ExpressionType { return this.typeFlags == TYPE_NONE; } } + +/** + * Here only for backwards-compatibility only. + */ +export function expressionTypeFromPostgresType(type: string | undefined): ExpressionType { + if (type?.endsWith('[]')) { + return ExpressionType.TEXT; + } + switch (type) { + case 'bool': + return ExpressionType.INTEGER; + case 'bytea': + return ExpressionType.BLOB; + case 'int2': + case 'int4': + case 'int8': + case 'oid': + return ExpressionType.INTEGER; + case 'float4': + case 'float8': + return ExpressionType.REAL; + default: + return ExpressionType.TEXT; + } +} diff --git a/packages/sync-rules/src/StaticSchema.ts b/packages/sync-rules/src/StaticSchema.ts index a807d471c..997b6da2f 100644 --- a/packages/sync-rules/src/StaticSchema.ts +++ b/packages/sync-rules/src/StaticSchema.ts @@ -1,4 +1,4 @@ -import { ColumnDefinition, ExpressionType } from './ExpressionType.js'; +import { ColumnDefinition, ExpressionType, expressionTypeFromPostgresType, SqliteType } from './ExpressionType.js'; import { SourceTableInterface } from './SourceTableInterface.js'; import { TablePattern } from './TablePattern.js'; import { SourceSchema, SourceSchemaTable } from './types.js'; @@ -14,11 +14,28 @@ export interface SourceTableDefinition { } export interface SourceColumnDefinition { + /** + * Column name. + */ name: string; + + /** + * Option 1: SQLite type flags - see ExpressionType.typeFlags. + * Option 2: SQLite type name in lowercase - 'text' | 'integer' | 'real' | 'numeric' | 'blob' | 'null' + */ + sqlite_type?: number | SqliteType; + /** - * Postgres type. + * Type name from the source database, e.g. "character varying(255)[]" */ - pg_type: string; + original_type?: string; + + /** + * Postgres type, kept for backwards-compatibility. + * + * @deprecated - use original_type instead + */ + pg_type?: string; } export interface SourceConnectionDefinition { @@ -75,28 +92,19 @@ export class StaticSchema implements SourceSchema { function mapColumn(column: SourceColumnDefinition): ColumnDefinition { return { name: column.name, - type: mapType(column.pg_type) + type: mapColumnType(column) }; } -function mapType(type: string | undefined): ExpressionType { - if (type?.endsWith('[]')) { - return ExpressionType.TEXT; - } - switch (type) { - case 'bool': - return ExpressionType.INTEGER; - case 'bytea': - return ExpressionType.BLOB; - case 'int2': - case 'int4': - case 'int8': - case 'oid': - return ExpressionType.INTEGER; - case 'float4': - case 'float8': - return ExpressionType.REAL; - default: - return ExpressionType.TEXT; +function mapColumnType(column: SourceColumnDefinition): ExpressionType { + if (typeof column.sqlite_type == 'number') { + return ExpressionType.of(column.sqlite_type); + } else if (typeof column.sqlite_type == 'string') { + return ExpressionType.fromTypeText(column.sqlite_type); + } else if (column.pg_type != null) { + // We still handle these types for backwards-compatibility of old schemas + return expressionTypeFromPostgresType(column.pg_type); + } else { + throw new Error(`Cannot determine SQLite type of ${JSON.stringify(column)}`); } } diff --git a/packages/types/src/definitions.ts b/packages/types/src/definitions.ts index 35dc64177..b7eff1a27 100644 --- a/packages/types/src/definitions.ts +++ b/packages/types/src/definitions.ts @@ -91,6 +91,15 @@ export const ConnectionStatusV2 = t.object({ }); export type ConnectionStatusV2 = t.Encoded; +export enum SqliteSchemaTypeText { + null = 'null', + blob = 'blob', + text = 'text', + integer = 'integer', + real = 'real', + numeric = 'numeric' +} + export const DatabaseSchema = t.object({ name: t.string, tables: t.array( @@ -99,14 +108,34 @@ export const DatabaseSchema = t.object({ columns: t.array( t.object({ name: t.string, + + /** + * Option 1: SQLite type flags - see ExpressionType.typeFlags. + * Option 2: SQLite type name in lowercase - 'text' | 'integer' | 'real' | 'numeric' | 'blob' | 'null' + */ + sqlite_type: t.number.or(t.Enum(SqliteSchemaTypeText)), + + /** + * Type name from the source database, e.g. "character varying(255)[]" + */ + original_type: t.string, + + /** + * Description for the field if available. + */ + description: t.string.optional(), + /** * Full type name, e.g. "character varying(255)[]" + * @deprecated - use original_type */ - type: t.string, + type: t.string.optional(), + /** * Internal postgres type, e.g. "varchar[]". + * @deprecated - use original_type */ - pg_type: t.string + pg_type: t.string.optional() }) ) }) From a197710fab3dadf06e05b35705153e9488af9884 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 25 Sep 2024 16:58:26 +0200 Subject: [PATCH 212/298] Rename original_type -> internal_type. --- modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts | 2 +- packages/sync-rules/src/StaticSchema.ts | 4 ++-- packages/types/src/definitions.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 49f53c415..3862daec5 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -298,7 +298,7 @@ GROUP BY schemaname, tablename, quoted_name` name: column.attname, sqlite_type: sync_rules.expressionTypeFromPostgresType(pg_type).typeFlags, type: column.data_type, - original_type: column.data_type, + internal_type: column.data_type, pg_type: pg_type }); } diff --git a/packages/sync-rules/src/StaticSchema.ts b/packages/sync-rules/src/StaticSchema.ts index 997b6da2f..5176190e0 100644 --- a/packages/sync-rules/src/StaticSchema.ts +++ b/packages/sync-rules/src/StaticSchema.ts @@ -28,12 +28,12 @@ export interface SourceColumnDefinition { /** * Type name from the source database, e.g. "character varying(255)[]" */ - original_type?: string; + internal_type?: string; /** * Postgres type, kept for backwards-compatibility. * - * @deprecated - use original_type instead + * @deprecated - use internal_type instead */ pg_type?: string; } diff --git a/packages/types/src/definitions.ts b/packages/types/src/definitions.ts index b7eff1a27..bdbf88d29 100644 --- a/packages/types/src/definitions.ts +++ b/packages/types/src/definitions.ts @@ -118,7 +118,7 @@ export const DatabaseSchema = t.object({ /** * Type name from the source database, e.g. "character varying(255)[]" */ - original_type: t.string, + internal_type: t.string, /** * Description for the field if available. @@ -127,13 +127,13 @@ export const DatabaseSchema = t.object({ /** * Full type name, e.g. "character varying(255)[]" - * @deprecated - use original_type + * @deprecated - use internal_type */ type: t.string.optional(), /** * Internal postgres type, e.g. "varchar[]". - * @deprecated - use original_type + * @deprecated - use internal_type instead */ pg_type: t.string.optional() }) From b0a4411b0b56e806cbb05d7ff464f342d10eb529 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 25 Sep 2024 17:57:37 +0200 Subject: [PATCH 213/298] Include source types in the generated schema comments (optional). --- .../sync-rules/src/DartSchemaGenerator.ts | 26 ++- packages/sync-rules/src/ExpressionType.ts | 1 + packages/sync-rules/src/SchemaGenerator.ts | 6 +- packages/sync-rules/src/SqlDataQuery.ts | 12 +- packages/sync-rules/src/StaticSchema.ts | 7 +- packages/sync-rules/src/TableQuerySchema.ts | 15 +- packages/sync-rules/src/TsSchemaGenerator.ts | 27 ++- packages/sync-rules/src/sql_filters.ts | 23 ++- packages/sync-rules/src/sql_support.ts | 20 +- packages/sync-rules/src/types.ts | 6 +- .../test/src/generate_schema.test.ts | 172 ++++++++++++++++++ .../sync-rules/test/src/sync_rules.test.ts | 106 ----------- 12 files changed, 267 insertions(+), 154 deletions(-) create mode 100644 packages/sync-rules/test/src/generate_schema.test.ts diff --git a/packages/sync-rules/src/DartSchemaGenerator.ts b/packages/sync-rules/src/DartSchemaGenerator.ts index e25fedf20..1a90884de 100644 --- a/packages/sync-rules/src/DartSchemaGenerator.ts +++ b/packages/sync-rules/src/DartSchemaGenerator.ts @@ -1,5 +1,5 @@ import { ColumnDefinition, TYPE_INTEGER, TYPE_REAL, TYPE_TEXT } from './ExpressionType.js'; -import { SchemaGenerator } from './SchemaGenerator.js'; +import { GenerateSchemaOptions, SchemaGenerator } from './SchemaGenerator.js'; import { SqlSyncRules } from './SqlSyncRules.js'; import { SourceSchema } from './types.js'; @@ -9,18 +9,34 @@ export class DartSchemaGenerator extends SchemaGenerator { readonly mediaType = 'text/x-dart'; readonly fileName = 'schema.dart'; - generate(source: SqlSyncRules, schema: SourceSchema): string { + generate(source: SqlSyncRules, schema: SourceSchema, options?: GenerateSchemaOptions): string { const tables = super.getAllTables(source, schema); return `Schema([ - ${tables.map((table) => this.generateTable(table.name, table.columns)).join(',\n ')} + ${tables.map((table) => this.generateTable(table.name, table.columns, options)).join(',\n ')} ]); `; } - private generateTable(name: string, columns: ColumnDefinition[]): string { + private generateTable(name: string, columns: ColumnDefinition[], options?: GenerateSchemaOptions): string { + const generated = columns.map((c, i) => { + const last = i == columns.length - 1; + const base = this.generateColumn(c); + let withFormatting: string; + if (last) { + withFormatting = ` ${base}`; + } else { + withFormatting = ` ${base},`; + } + + if (options?.includeTypeComments && c.originalType != null) { + return `${withFormatting} // ${c.originalType}`; + } else { + return withFormatting; + } + }); return `Table('${name}', [ - ${columns.map((c) => this.generateColumn(c)).join(',\n ')} +${generated.join('\n')} ])`; } diff --git a/packages/sync-rules/src/ExpressionType.ts b/packages/sync-rules/src/ExpressionType.ts index 2121abbb0..ad46c4408 100644 --- a/packages/sync-rules/src/ExpressionType.ts +++ b/packages/sync-rules/src/ExpressionType.ts @@ -9,6 +9,7 @@ export type SqliteType = 'null' | 'blob' | 'text' | 'integer' | 'real' | 'numeri export interface ColumnDefinition { name: string; type: ExpressionType; + originalType?: string; } export class ExpressionType { diff --git a/packages/sync-rules/src/SchemaGenerator.ts b/packages/sync-rules/src/SchemaGenerator.ts index 91bf48942..18e111b57 100644 --- a/packages/sync-rules/src/SchemaGenerator.ts +++ b/packages/sync-rules/src/SchemaGenerator.ts @@ -2,6 +2,10 @@ import { ColumnDefinition } from './ExpressionType.js'; import { SqlSyncRules } from './SqlSyncRules.js'; import { SourceSchema } from './types.js'; +export interface GenerateSchemaOptions { + includeTypeComments?: boolean; +} + export abstract class SchemaGenerator { protected getAllTables(source: SqlSyncRules, schema: SourceSchema) { let tables: Record> = {}; @@ -33,5 +37,5 @@ export abstract class SchemaGenerator { abstract readonly mediaType: string; abstract readonly fileName: string; - abstract generate(source: SqlSyncRules, schema: SourceSchema): string; + abstract generate(source: SqlSyncRules, schema: SourceSchema, options?: GenerateSchemaOptions): string; } diff --git a/packages/sync-rules/src/SqlDataQuery.ts b/packages/sync-rules/src/SqlDataQuery.ts index b5377d436..e9d6cb04c 100644 --- a/packages/sync-rules/src/SqlDataQuery.ts +++ b/packages/sync-rules/src/SqlDataQuery.ts @@ -123,7 +123,9 @@ export class SqlDataQuery { output[name] = clause.evaluate(tables); }, getTypes(schema, into) { - into[name] = { name, type: clause.getType(schema) }; + const def = clause.getColumnDefinition(schema); + + into[name] = { name, type: def?.type ?? ExpressionType.NONE, originalType: def?.originalType }; } }); } else { @@ -152,7 +154,7 @@ export class SqlDataQuery { // Not performing schema-based validation - assume there is an id hasId = true; } else { - const idType = querySchema.getType(alias, 'id'); + const idType = querySchema.getColumn(alias, 'id')?.type ?? ExpressionType.NONE; if (!idType.isNone()) { hasId = true; } @@ -296,12 +298,12 @@ export class SqlDataQuery { private getColumnOutputsFor(schemaTable: SourceSchemaTable, output: Record) { const querySchema: QuerySchema = { - getType: (table, column) => { + getColumn: (table, column) => { if (table == this.table!) { - return schemaTable.getType(column) ?? ExpressionType.NONE; + return schemaTable.getColumn(column); } else { // TODO: bucket parameters? - return ExpressionType.NONE; + return undefined; } }, getColumns: (table) => { diff --git a/packages/sync-rules/src/StaticSchema.ts b/packages/sync-rules/src/StaticSchema.ts index 5176190e0..aa27114c6 100644 --- a/packages/sync-rules/src/StaticSchema.ts +++ b/packages/sync-rules/src/StaticSchema.ts @@ -60,8 +60,8 @@ class SourceTableDetails implements SourceTableInterface, SourceSchemaTable { ); } - getType(column: string): ExpressionType | undefined { - return this.columns[column]?.type; + getColumn(column: string): ColumnDefinition | undefined { + return this.columns[column]; } getColumns(): ColumnDefinition[] { @@ -92,7 +92,8 @@ export class StaticSchema implements SourceSchema { function mapColumn(column: SourceColumnDefinition): ColumnDefinition { return { name: column.name, - type: mapColumnType(column) + type: mapColumnType(column), + originalType: column.internal_type }; } diff --git a/packages/sync-rules/src/TableQuerySchema.ts b/packages/sync-rules/src/TableQuerySchema.ts index 01a7a8cf4..c223371b3 100644 --- a/packages/sync-rules/src/TableQuerySchema.ts +++ b/packages/sync-rules/src/TableQuerySchema.ts @@ -1,23 +1,20 @@ -import { ColumnDefinition, ExpressionType } from './ExpressionType.js'; +import { ColumnDefinition } from './ExpressionType.js'; import { QuerySchema, SourceSchemaTable } from './types.js'; export class TableQuerySchema implements QuerySchema { - constructor( - private tables: SourceSchemaTable[], - private alias: string - ) {} + constructor(private tables: SourceSchemaTable[], private alias: string) {} - getType(table: string, column: string): ExpressionType { + getColumn(table: string, column: string): ColumnDefinition | undefined { if (table != this.alias) { - return ExpressionType.NONE; + return undefined; } for (let table of this.tables) { - const t = table.getType(column); + const t = table.getColumn(column); if (t != null) { return t; } } - return ExpressionType.NONE; + return undefined; } getColumns(table: string): ColumnDefinition[] { diff --git a/packages/sync-rules/src/TsSchemaGenerator.ts b/packages/sync-rules/src/TsSchemaGenerator.ts index 9e3e56e51..1a0dba58c 100644 --- a/packages/sync-rules/src/TsSchemaGenerator.ts +++ b/packages/sync-rules/src/TsSchemaGenerator.ts @@ -1,5 +1,5 @@ import { ColumnDefinition, TYPE_INTEGER, TYPE_REAL, TYPE_TEXT } from './ExpressionType.js'; -import { SchemaGenerator } from './SchemaGenerator.js'; +import { GenerateSchemaOptions, SchemaGenerator } from './SchemaGenerator.js'; import { SqlSyncRules } from './SqlSyncRules.js'; import { SourceSchema } from './types.js'; @@ -47,12 +47,12 @@ export class TsSchemaGenerator extends SchemaGenerator { } } - generate(source: SqlSyncRules, schema: SourceSchema): string { + generate(source: SqlSyncRules, schema: SourceSchema, options?: GenerateSchemaOptions): string { const tables = super.getAllTables(source, schema); return `${this.generateImports()} -${tables.map((table) => this.generateTable(table.name, table.columns)).join('\n\n')} +${tables.map((table) => this.generateTable(table.name, table.columns, options)).join('\n\n')} export const AppSchema = new Schema({ ${tables.map((table) => table.name).join(',\n ')} @@ -81,11 +81,28 @@ ${this.generateTypeExports()}`; } } - private generateTable(name: string, columns: ColumnDefinition[]): string { + private generateTable(name: string, columns: ColumnDefinition[], options?: GenerateSchemaOptions): string { + const generated = columns.map((c, i) => { + const last = i == columns.length - 1; + const base = this.generateColumn(c); + let withFormatting: string; + if (last) { + withFormatting = ` ${base}`; + } else { + withFormatting = ` ${base},`; + } + + if (options?.includeTypeComments && c.originalType != null) { + return `${withFormatting} // ${c.originalType}`; + } else { + return withFormatting; + } + }); + return `const ${name} = new Table( { // id column (text) is automatically included - ${columns.map((c) => this.generateColumn(c)).join(',\n ')} +${generated.join('\n')} }, { indexes: {} } );`; diff --git a/packages/sync-rules/src/sql_filters.ts b/packages/sync-rules/src/sql_filters.ts index 67c4b1603..10ea88053 100644 --- a/packages/sync-rules/src/sql_filters.ts +++ b/packages/sync-rules/src/sql_filters.ts @@ -198,8 +198,8 @@ export class SqlTools { evaluate(tables: QueryParameters): SqliteValue { return tables[table]?.[column]; }, - getType(schema) { - return schema.getType(table, column); + getColumnDefinition(schema) { + return schema.getColumn(table, column); } } satisfies RowValueClause; } else { @@ -514,8 +514,8 @@ export class SqlTools { private checkRef(table: string, ref: ExprRef) { if (this.schema) { - const type = this.schema.getType(table, ref.name); - if (type.typeFlags == TYPE_NONE) { + const type = this.schema.getColumn(table, ref.name); + if (type == null) { this.warn(`Column not found: ${ref.name}`, ref); } } @@ -613,9 +613,11 @@ export class SqlTools { const args = argClauses.map((e) => (e as RowValueClause).evaluate(tables)); return fnImpl.call(...args); }, - getType(schema) { - const argTypes = argClauses.map((e) => (e as RowValueClause).getType(schema)); - return fnImpl.getReturnType(argTypes); + getColumnDefinition(schema) { + const argTypes = argClauses.map( + (e) => (e as RowValueClause).getColumnDefinition(schema)?.type ?? ExpressionType.NONE + ); + return { name: `${fnImpl}()`, type: fnImpl.getReturnType(argTypes) }; } } satisfies RowValueClause; } else if (argsType == 'param') { @@ -671,8 +673,11 @@ function staticValueClause(value: SqliteValue): StaticValueClause { value: value, // RowValueClause compatibility evaluate: () => value, - getType() { - return ExpressionType.fromTypeText(sqliteTypeOf(value)); + getColumnDefinition() { + return { + name: 'literal', + type: ExpressionType.fromTypeText(sqliteTypeOf(value)) + }; }, // ParamterValueClause compatibility key: JSONBig.stringify(value), diff --git a/packages/sync-rules/src/sql_support.ts b/packages/sync-rules/src/sql_support.ts index 9d381f363..e2b074c55 100644 --- a/packages/sync-rules/src/sql_support.ts +++ b/packages/sync-rules/src/sql_support.ts @@ -66,10 +66,14 @@ export function compileStaticOperator(op: string, left: RowValueClause, right: R const rightValue = right.evaluate(tables); return evaluateOperator(op, leftValue, rightValue); }, - getType(schema) { - const typeLeft = left.getType(schema); - const typeRight = right.getType(schema); - return getOperatorReturnType(op, typeLeft, typeRight); + getColumnDefinition(schema) { + const typeLeft = left.getColumnDefinition(schema)?.type ?? ExpressionType.NONE; + const typeRight = right.getColumnDefinition(schema)?.type ?? ExpressionType.NONE; + const type = getOperatorReturnType(op, typeLeft, typeRight); + return { + name: '?', + type + }; } }; } @@ -95,8 +99,8 @@ export function andFilters(a: CompiledClause, b: CompiledClause): CompiledClause const bValue = sqliteBool(b.evaluate(tables)); return sqliteBool(aValue && bValue); }, - getType() { - return ExpressionType.INTEGER; + getColumnDefinition() { + return { name: 'and', type: ExpressionType.INTEGER }; } } satisfies RowValueClause; } @@ -156,8 +160,8 @@ export function orFilters(a: CompiledClause, b: CompiledClause): CompiledClause const bValue = sqliteBool(b.evaluate(tables)); return sqliteBool(aValue || bValue); }, - getType() { - return ExpressionType.INTEGER; + getColumnDefinition() { + return { name: 'or', type: ExpressionType.INTEGER }; } } satisfies RowValueClause; } diff --git a/packages/sync-rules/src/types.ts b/packages/sync-rules/src/types.ts index e27489435..5aa8ea378 100644 --- a/packages/sync-rules/src/types.ts +++ b/packages/sync-rules/src/types.ts @@ -280,7 +280,7 @@ export interface ParameterValueClause { } export interface QuerySchema { - getType(table: string, column: string): ExpressionType; + getColumn(table: string, column: string): ColumnDefinition | undefined; getColumns(table: string): ColumnDefinition[]; } @@ -292,7 +292,7 @@ export interface QuerySchema { */ export interface RowValueClause { evaluate(tables: QueryParameters): SqliteValue; - getType(schema: QuerySchema): ExpressionType; + getColumnDefinition(schema: QuerySchema): ColumnDefinition | undefined; } /** @@ -322,7 +322,7 @@ export interface QueryBucketIdOptions { export interface SourceSchemaTable { table: string; - getType(column: string): ExpressionType | undefined; + getColumn(column: string): ColumnDefinition | undefined; getColumns(): ColumnDefinition[]; } export interface SourceSchema { diff --git a/packages/sync-rules/test/src/generate_schema.test.ts b/packages/sync-rules/test/src/generate_schema.test.ts new file mode 100644 index 000000000..d742a7f8b --- /dev/null +++ b/packages/sync-rules/test/src/generate_schema.test.ts @@ -0,0 +1,172 @@ +import { describe, expect, test } from 'vitest'; +import { + DEFAULT_TAG, + DartSchemaGenerator, + JsLegacySchemaGenerator, + SqlSyncRules, + StaticSchema, + TsSchemaGenerator +} from '../../src/index.js'; + +import { PARSE_OPTIONS } from './util.js'; + +describe('schema generation', () => { + const schema = new StaticSchema([ + { + tag: DEFAULT_TAG, + schemas: [ + { + name: 'test_schema', + tables: [ + { + name: 'assets', + columns: [ + { name: 'id', sqlite_type: 'text', internal_type: 'uuid' }, + { name: 'name', sqlite_type: 'text', internal_type: 'text' }, + { name: 'count', sqlite_type: 'integer', internal_type: 'int4' }, + { name: 'owner_id', sqlite_type: 'text', internal_type: 'uuid' } + ] + } + ] + } + ] + } + ]); + + const rules = SqlSyncRules.fromYaml( + ` +bucket_definitions: + mybucket: + data: + - SELECT * FROM assets as assets1 + - SELECT id, name, count FROM assets as assets2 + - SELECT id, owner_id as other_id, foo FROM assets as ASSETS2 + `, + PARSE_OPTIONS + ); + + test('dart', () => { + expect(new DartSchemaGenerator().generate(rules, schema)).toEqual(`Schema([ + Table('assets1', [ + Column.text('name'), + Column.integer('count'), + Column.text('owner_id') + ]), + Table('assets2', [ + Column.text('name'), + Column.integer('count'), + Column.text('other_id'), + Column.text('foo') + ]) +]); +`); + + expect(new DartSchemaGenerator().generate(rules, schema, { includeTypeComments: true })).toEqual(`Schema([ + Table('assets1', [ + Column.text('name'), // text + Column.integer('count'), // int4 + Column.text('owner_id') // uuid + ]), + Table('assets2', [ + Column.text('name'), // text + Column.integer('count'), // int4 + Column.text('other_id'), // uuid + Column.text('foo') + ]) +]); +`); + }); + + test('js legacy', () => { + expect(new JsLegacySchemaGenerator().generate(rules, schema)).toEqual(`new Schema([ + new Table({ + name: 'assets1', + columns: [ + new Column({ name: 'name', type: ColumnType.TEXT }), + new Column({ name: 'count', type: ColumnType.INTEGER }), + new Column({ name: 'owner_id', type: ColumnType.TEXT }) + ] + }), + new Table({ + name: 'assets2', + columns: [ + new Column({ name: 'name', type: ColumnType.TEXT }), + new Column({ name: 'count', type: ColumnType.INTEGER }), + new Column({ name: 'other_id', type: ColumnType.TEXT }), + new Column({ name: 'foo', type: ColumnType.TEXT }) + ] + }) +]) +`); + }); + + test('ts', () => { + expect(new TsSchemaGenerator().generate(rules, schema, {})).toEqual( + `import { column, Schema, Table } from '@powersync/web'; +// OR: import { column, Schema, Table } from '@powersync/react-native'; + +const assets1 = new Table( + { + // id column (text) is automatically included + name: column.text, + count: column.integer, + owner_id: column.text + }, + { indexes: {} } +); + +const assets2 = new Table( + { + // id column (text) is automatically included + name: column.text, + count: column.integer, + other_id: column.text, + foo: column.text + }, + { indexes: {} } +); + +export const AppSchema = new Schema({ + assets1, + assets2 +}); + +export type Database = (typeof AppSchema)['types']; +` + ); + + expect(new TsSchemaGenerator().generate(rules, schema, { includeTypeComments: true })).toEqual( + `import { column, Schema, Table } from '@powersync/web'; +// OR: import { column, Schema, Table } from '@powersync/react-native'; + +const assets1 = new Table( + { + // id column (text) is automatically included + name: column.text, // text + count: column.integer, // int4 + owner_id: column.text // uuid + }, + { indexes: {} } +); + +const assets2 = new Table( + { + // id column (text) is automatically included + name: column.text, // text + count: column.integer, // int4 + other_id: column.text, // uuid + foo: column.text + }, + { indexes: {} } +); + +export const AppSchema = new Schema({ + assets1, + assets2 +}); + +export type Database = (typeof AppSchema)['types']; +` + ); + }); +}); diff --git a/packages/sync-rules/test/src/sync_rules.test.ts b/packages/sync-rules/test/src/sync_rules.test.ts index c7e88ad46..26ff3adb3 100644 --- a/packages/sync-rules/test/src/sync_rules.test.ts +++ b/packages/sync-rules/test/src/sync_rules.test.ts @@ -786,110 +786,4 @@ bucket_definitions: expect(rules.errors).toEqual([]); }); - - test('schema generation', () => { - const schema = new StaticSchema([ - { - tag: DEFAULT_TAG, - schemas: [ - { - name: 'test_schema', - tables: [ - { - name: 'assets', - columns: [ - { name: 'id', pg_type: 'uuid' }, - { name: 'name', pg_type: 'text' }, - { name: 'count', pg_type: 'int4' }, - { name: 'owner_id', pg_type: 'uuid' } - ] - } - ] - } - ] - } - ]); - - const rules = SqlSyncRules.fromYaml( - ` -bucket_definitions: - mybucket: - data: - - SELECT * FROM assets as assets1 - - SELECT id, name, count FROM assets as assets2 - - SELECT id, owner_id as other_id, foo FROM assets as ASSETS2 - `, - PARSE_OPTIONS - ); - - expect(new DartSchemaGenerator().generate(rules, schema)).toEqual(`Schema([ - Table('assets1', [ - Column.text('name'), - Column.integer('count'), - Column.text('owner_id') - ]), - Table('assets2', [ - Column.text('name'), - Column.integer('count'), - Column.text('other_id'), - Column.text('foo') - ]) -]); -`); - - expect(new JsLegacySchemaGenerator().generate(rules, schema)).toEqual(`new Schema([ - new Table({ - name: 'assets1', - columns: [ - new Column({ name: 'name', type: ColumnType.TEXT }), - new Column({ name: 'count', type: ColumnType.INTEGER }), - new Column({ name: 'owner_id', type: ColumnType.TEXT }) - ] - }), - new Table({ - name: 'assets2', - columns: [ - new Column({ name: 'name', type: ColumnType.TEXT }), - new Column({ name: 'count', type: ColumnType.INTEGER }), - new Column({ name: 'other_id', type: ColumnType.TEXT }), - new Column({ name: 'foo', type: ColumnType.TEXT }) - ] - }) -]) -`); - - expect(new TsSchemaGenerator().generate(rules, schema)).toEqual( - `import { column, Schema, Table } from '@powersync/web'; -// OR: import { column, Schema, Table } from '@powersync/react-native'; - -const assets1 = new Table( - { - // id column (text) is automatically included - name: column.text, - count: column.integer, - owner_id: column.text - }, - { indexes: {} } -); - -const assets2 = new Table( - { - // id column (text) is automatically included - name: column.text, - count: column.integer, - other_id: column.text, - foo: column.text - }, - { indexes: {} } -); - -export const AppSchema = new Schema({ - assets1, - assets2 -}); - -export type Database = (typeof AppSchema)['types']; -` - ); - }); }); From 3da71a9e1fdb8c545efcc3a8ffcab981afe84fdc Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 25 Sep 2024 18:00:07 +0200 Subject: [PATCH 214/298] Explicit TableSchema type. --- .../src/api/PostgresRouteAPIAdapter.ts | 2 +- packages/types/src/definitions.ts | 71 ++++++++++--------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 3862daec5..8a7e6e40c 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -282,7 +282,7 @@ GROUP BY schemaname, tablename, quoted_name` name: row.schemaname, tables: [] }); - const table: service_types.DatabaseSchema['tables'][0] = { + const table: service_types.TableSchema = { name: row.tablename, columns: [] as any[] }; diff --git a/packages/types/src/definitions.ts b/packages/types/src/definitions.ts index bdbf88d29..d387fb87e 100644 --- a/packages/types/src/definitions.ts +++ b/packages/types/src/definitions.ts @@ -100,47 +100,48 @@ export enum SqliteSchemaTypeText { numeric = 'numeric' } -export const DatabaseSchema = t.object({ +export const TableSchema = t.object({ name: t.string, - tables: t.array( + columns: t.array( t.object({ name: t.string, - columns: t.array( - t.object({ - name: t.string, - - /** - * Option 1: SQLite type flags - see ExpressionType.typeFlags. - * Option 2: SQLite type name in lowercase - 'text' | 'integer' | 'real' | 'numeric' | 'blob' | 'null' - */ - sqlite_type: t.number.or(t.Enum(SqliteSchemaTypeText)), - - /** - * Type name from the source database, e.g. "character varying(255)[]" - */ - internal_type: t.string, - - /** - * Description for the field if available. - */ - description: t.string.optional(), - - /** - * Full type name, e.g. "character varying(255)[]" - * @deprecated - use internal_type - */ - type: t.string.optional(), - - /** - * Internal postgres type, e.g. "varchar[]". - * @deprecated - use internal_type instead - */ - pg_type: t.string.optional() - }) - ) + + /** + * Option 1: SQLite type flags - see ExpressionType.typeFlags. + * Option 2: SQLite type name in lowercase - 'text' | 'integer' | 'real' | 'numeric' | 'blob' | 'null' + */ + sqlite_type: t.number.or(t.Enum(SqliteSchemaTypeText)), + + /** + * Type name from the source database, e.g. "character varying(255)[]" + */ + internal_type: t.string, + + /** + * Description for the field if available. + */ + description: t.string.optional(), + + /** + * Full type name, e.g. "character varying(255)[]" + * @deprecated - use internal_type + */ + type: t.string.optional(), + + /** + * Internal postgres type, e.g. "varchar[]". + * @deprecated - use internal_type instead + */ + pg_type: t.string.optional() }) ) }); +export type TableSchema = t.Encoded; + +export const DatabaseSchema = t.object({ + name: t.string, + tables: t.array(TableSchema) +}); export type DatabaseSchema = t.Encoded; export const InstanceSchema = t.object({ From 4ecaee26ada0be290575649cbeee153386f7b861 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 25 Sep 2024 18:04:46 +0200 Subject: [PATCH 215/298] Add changeset. --- .changeset/weak-cats-hug.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/weak-cats-hug.md diff --git a/.changeset/weak-cats-hug.md b/.changeset/weak-cats-hug.md new file mode 100644 index 000000000..df152386e --- /dev/null +++ b/.changeset/weak-cats-hug.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-sync-rules': minor +--- + +Optionally include original types in generated schemas as a comment. From bfea46165bbef4f49b3c4990f68070f8fe43d3e6 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 1 Oct 2024 16:49:13 +0200 Subject: [PATCH 216/298] remove mongodb publish config restriction in order to publish dev packages --- modules/module-mongodb/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/module-mongodb/package.json b/modules/module-mongodb/package.json index dfcd52e0e..b42949353 100644 --- a/modules/module-mongodb/package.json +++ b/modules/module-mongodb/package.json @@ -2,9 +2,6 @@ "name": "@powersync/service-module-mongodb", "repository": "https://github.com/powersync-ja/powersync-service", "types": "dist/index.d.ts", - "publishConfig": { - "access": "restricted" - }, "version": "0.0.1", "main": "dist/index.js", "license": "FSL-1.1-Apache-2.0", From fb52b595e4dc4feaa35a17e1ed537ac572028c64 Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Tue, 1 Oct 2024 17:03:25 +0200 Subject: [PATCH 217/298] need to explicitly set MongDB package access to public for publish. --- modules/module-mongodb/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/module-mongodb/package.json b/modules/module-mongodb/package.json index b42949353..5809bed99 100644 --- a/modules/module-mongodb/package.json +++ b/modules/module-mongodb/package.json @@ -6,6 +6,9 @@ "main": "dist/index.js", "license": "FSL-1.1-Apache-2.0", "type": "module", + "publishConfig": { + "access": "public" + }, "scripts": { "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", From 2eb566a4ff69907785203a240f4a6b0d33f69fbd Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 2 Oct 2024 11:36:44 +0200 Subject: [PATCH 218/298] Post-merge test fixes. --- .../service-core/test/src/compacting.test.ts | 25 +++++++++++-------- .../test/src/parameter_queries.test.ts | 10 ++++---- .../test/src/static_parameter_queries.test.ts | 16 ++++++------ 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/packages/service-core/test/src/compacting.test.ts b/packages/service-core/test/src/compacting.test.ts index 27c101644..e7902ab23 100644 --- a/packages/service-core/test/src/compacting.test.ts +++ b/packages/service-core/test/src/compacting.test.ts @@ -222,21 +222,22 @@ bucket_definitions: }); test('compacting (3)', async () => { - const sync_rules = SqlSyncRules.fromYaml(` + const sync_rules = testRules(` bucket_definitions: global: data: [select * from test] `); - const storage = (await factory()).getInstance({ id: 1, sync_rules, slot_name: 'test' }); + const storage = (await factory()).getInstance(sync_rules); - const result = await storage.startBatch({}, async (batch) => { + const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'insert', after: { id: 't1' - } + }, + afterReplicaId: 't1' }); await batch.save({ @@ -244,7 +245,8 @@ bucket_definitions: tag: 'insert', after: { id: 't2' - } + }, + afterReplicaId: 't2' }); await batch.save({ @@ -252,21 +254,22 @@ bucket_definitions: tag: 'delete', before: { id: 't1' - } + }, + beforeReplicaId: 't1' }); }); const checkpoint1 = result!.flushed_op; const checksumBefore = await storage.getChecksums(checkpoint1, ['global[]']); - console.log('before', checksumBefore); - const result2 = await storage.startBatch({}, async (batch) => { + const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, tag: 'delete', before: { id: 't2' - } + }, + beforeReplicaId: 't2' }); }); const checkpoint2 = result2!.flushed_op; @@ -280,7 +283,7 @@ bucket_definitions: expect(batchAfter.targetOp).toEqual(4n); expect(dataAfter).toMatchObject([ { - checksum: 857217610, + checksum: 1874612650, op: 'CLEAR', op_id: '4' } @@ -288,7 +291,7 @@ bucket_definitions: expect(checksumAfter.get('global[]')).toEqual({ bucket: 'global[]', count: 1, - checksum: 857217610 + checksum: 1874612650 }); }); } diff --git a/packages/sync-rules/test/src/parameter_queries.test.ts b/packages/sync-rules/test/src/parameter_queries.test.ts index 8ebc61ce0..59d0f1147 100644 --- a/packages/sync-rules/test/src/parameter_queries.test.ts +++ b/packages/sync-rules/test/src/parameter_queries.test.ts @@ -394,7 +394,7 @@ describe('parameter queries', () => { test('case-sensitive parameter queries (3)', () => { const sql = 'SELECT user_id FROM users WHERE Users.user_id = token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: `Unquoted identifiers are converted to lower-case. Use "Users" instead.` } ]); @@ -402,7 +402,7 @@ describe('parameter queries', () => { test('case-sensitive parameter queries (4)', () => { const sql = 'SELECT Users.user_id FROM users WHERE user_id = token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: `Unquoted identifiers are converted to lower-case. Use "Users" instead.` } ]); @@ -410,7 +410,7 @@ describe('parameter queries', () => { test('case-sensitive parameter queries (5)', () => { const sql = 'SELECT user_id FROM Users WHERE user_id = token_parameters.user_id'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: `Unquoted identifiers are converted to lower-case. Use "Users" instead.` } ]); @@ -418,7 +418,7 @@ describe('parameter queries', () => { test('case-sensitive parameter queries (6)', () => { const sql = 'SELECT userId FROM users'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: `Unquoted identifiers are converted to lower-case. Use "userId" instead.` } ]); @@ -426,7 +426,7 @@ describe('parameter queries', () => { test('case-sensitive parameter queries (7)', () => { const sql = 'SELECT user_id as userId FROM users'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: `Unquoted identifiers are converted to lower-case. Use "userId" instead.` } ]); diff --git a/packages/sync-rules/test/src/static_parameter_queries.test.ts b/packages/sync-rules/test/src/static_parameter_queries.test.ts index a9c556848..a82dd9106 100644 --- a/packages/sync-rules/test/src/static_parameter_queries.test.ts +++ b/packages/sync-rules/test/src/static_parameter_queries.test.ts @@ -85,28 +85,28 @@ describe('static parameter queries', () => { test('static value', function () { const sql = `SELECT WHERE 1`; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, {}))).toEqual(['mybucket[]']); }); test('static expression (1)', function () { const sql = `SELECT WHERE 1 = 1`; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, {}))).toEqual(['mybucket[]']); }); test('static expression (2)', function () { const sql = `SELECT WHERE 1 != 1`; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, {}))).toEqual([]); }); test('static IN expression', function () { const sql = `SELECT WHERE 'admin' IN '["admin", "superuser"]'`; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, {}) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, {}))).toEqual(['mybucket[]']); }); @@ -114,7 +114,7 @@ describe('static parameter queries', () => { test('IN for permissions in request.jwt() (1)', function () { // Can use -> or ->> here const sql = `SELECT 'read:users' IN (request.jwt() ->> 'permissions') as access_granted`; - const query = SqlParameterQuery.fromSql('mybucket', sql) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect( query.getStaticBucketIds(new RequestParameters({ sub: '', permissions: ['write', 'read:users'] }, {})) @@ -127,7 +127,7 @@ describe('static parameter queries', () => { test('IN for permissions in request.jwt() (2)', function () { // Can use -> or ->> here const sql = `SELECT WHERE 'read:users' IN (request.jwt() ->> 'permissions')`; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, {}) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect( query.getStaticBucketIds(new RequestParameters({ sub: '', permissions: ['write', 'read:users'] }, {})) @@ -139,7 +139,7 @@ describe('static parameter queries', () => { test('IN for permissions in request.jwt() (3)', function () { const sql = `SELECT WHERE request.jwt() ->> 'role' IN '["admin", "superuser"]'`; - const query = SqlParameterQuery.fromSql('mybucket', sql, undefined, {}) as StaticSqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; expect(query.errors).toEqual([]); expect(query.getStaticBucketIds(new RequestParameters({ sub: '', role: 'superuser' }, {}))).toEqual(['mybucket[]']); expect(query.getStaticBucketIds(new RequestParameters({ sub: '', role: 'superadmin' }, {}))).toEqual([]); @@ -147,7 +147,7 @@ describe('static parameter queries', () => { test('case-sensitive queries (1)', () => { const sql = 'SELECT request.user_id() as USER_ID'; - const query = SqlParameterQuery.fromSql('mybucket', sql) as SqlParameterQuery; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; expect(query.errors).toMatchObject([ { message: `Unquoted identifiers are converted to lower-case. Use "USER_ID" instead.` } ]); From 354f7624a099b4ebf04728b1f99e619f7467e39a Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 2 Oct 2024 11:38:37 +0200 Subject: [PATCH 219/298] Fix pnpm-lock. --- pnpm-lock.yaml | 312 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a346a0ea..bf6e89cf5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,10 +129,10 @@ importers: version: 9.0.8 typescript: specifier: ^5.2.2 - version: 5.2.2 + version: 5.6.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.2.2)(vite@5.2.11(@types/node@18.11.11)) + version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -789,6 +789,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1258,6 +1262,9 @@ packages: resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sindresorhus/is@5.6.0': resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} engines: {node: '>=14.16'} @@ -1302,6 +1309,12 @@ packages: '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/chai-subset@1.3.5': + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + + '@types/chai@4.3.20': + resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} + '@types/connect@3.4.36': resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} @@ -1413,6 +1426,9 @@ packages: '@types/ws@8.2.3': resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + '@vitest/expect@0.34.6': + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + '@vitest/expect@2.1.1': resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} @@ -1431,15 +1447,27 @@ packages: '@vitest/pretty-format@2.1.1': resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} + '@vitest/runner@0.34.6': + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/runner@2.1.1': resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} + '@vitest/snapshot@0.34.6': + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/snapshot@2.1.1': resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} + '@vitest/spy@0.34.6': + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/spy@2.1.1': resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} + '@vitest/utils@0.34.6': + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} @@ -1530,6 +1558,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -1559,6 +1591,9 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1654,6 +1689,10 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} engines: {node: '>=12'} @@ -1673,6 +1712,9 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1772,6 +1814,9 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -1844,6 +1889,10 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1870,6 +1919,10 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -2462,6 +2515,10 @@ packages: light-my-request@5.13.0: resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2490,6 +2547,9 @@ packages: lossless-json@2.0.11: resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@3.1.1: resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} @@ -2610,6 +2670,9 @@ packages: engines: {node: '>=10'} hasBin: true + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mnemonist@0.39.5: resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} @@ -2812,6 +2875,10 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -2875,6 +2942,9 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -2925,6 +2995,9 @@ packages: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true + pkg-types@1.2.0: + resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} + postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} @@ -2955,6 +3028,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + proc-log@3.0.0: resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3042,6 +3119,9 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + read-package-json-fast@3.0.2: resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3395,6 +3475,9 @@ packages: resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} engines: {node: '>=14.16'} + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -3437,6 +3520,10 @@ packages: tinyexec@0.3.0: resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + tinypool@1.0.1: resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3445,6 +3532,10 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.0: resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} engines: {node: '>=14.0.0'} @@ -3532,6 +3623,10 @@ packages: resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -3552,6 +3647,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} @@ -3614,6 +3712,11 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vite-node@0.34.6: + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + vite-node@2.1.1: resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3655,6 +3758,37 @@ packages: terser: optional: true + vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + vitest@2.1.1: resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3823,6 +3957,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} @@ -4110,6 +4248,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.4.15': {} @@ -4675,7 +4817,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.0 '@prisma/instrumentation': 5.15.0 '@sentry/core': 8.9.2 - '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) + '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) '@sentry/types': 8.9.2 '@sentry/utils': 8.9.2 optionalDependencies: @@ -4683,7 +4825,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': + '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) @@ -4721,6 +4863,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@sinclair/typebox@0.27.8': {} + '@sindresorhus/is@5.6.0': {} '@syncpoint/wkx@0.5.2': @@ -4759,6 +4903,12 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 22.5.5 + '@types/chai-subset@1.3.5': + dependencies: + '@types/chai': 4.3.20 + + '@types/chai@4.3.20': {} + '@types/connect@3.4.36': dependencies: '@types/node': 22.5.5 @@ -4886,6 +5036,12 @@ snapshots: dependencies: '@types/node': 22.5.5 + '@vitest/expect@0.34.6': + dependencies: + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.5.0 + '@vitest/expect@2.1.1': dependencies: '@vitest/spy': 2.1.1 @@ -4905,21 +5061,43 @@ snapshots: dependencies: tinyrainbow: 1.2.0 + '@vitest/runner@0.34.6': + dependencies: + '@vitest/utils': 0.34.6 + p-limit: 4.0.0 + pathe: 1.1.2 + '@vitest/runner@2.1.1': dependencies: '@vitest/utils': 2.1.1 pathe: 1.1.2 + '@vitest/snapshot@0.34.6': + dependencies: + magic-string: 0.30.11 + pathe: 1.1.2 + pretty-format: 29.7.0 + '@vitest/snapshot@2.1.1': dependencies: '@vitest/pretty-format': 2.1.1 magic-string: 0.30.11 pathe: 1.1.2 + '@vitest/spy@0.34.6': + dependencies: + tinyspy: 2.2.1 + '@vitest/spy@2.1.1': dependencies: tinyspy: 3.0.0 + '@vitest/utils@0.34.6': + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + '@vitest/utils@2.1.1': dependencies: '@vitest/pretty-format': 2.1.1 @@ -4999,6 +5177,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} anymatch@3.1.3: @@ -5023,6 +5203,8 @@ snapshots: array-union@2.1.0: {} + assertion-error@1.1.0: {} + assertion-error@2.0.1: {} async-mutex@0.5.0: @@ -5163,6 +5345,16 @@ snapshots: camelcase@7.0.1: {} + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + chai@5.1.1: dependencies: assertion-error: 2.0.1 @@ -5186,6 +5378,10 @@ snapshots: chardet@0.7.0: {} + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + check-error@2.1.1: {} chokidar@3.6.0: @@ -5287,6 +5483,8 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 + confbox@0.1.7: {} + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -5361,6 +5559,10 @@ snapshots: dependencies: mimic-response: 3.1.0 + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + deep-eql@5.0.2: {} deep-extend@0.6.0: {} @@ -5381,6 +5583,8 @@ snapshots: detect-indent@6.1.0: {} + diff-sequences@29.6.3: {} + diff@4.0.2: {} dir-glob@3.0.1: @@ -5994,6 +6198,8 @@ snapshots: process-warning: 3.0.0 set-cookie-parser: 2.6.0 + local-pkg@0.4.3: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -6024,6 +6230,10 @@ snapshots: lossless-json@2.0.11: {} + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + loupe@3.1.1: dependencies: get-func-name: 2.0.2 @@ -6170,6 +6380,13 @@ snapshots: mkdirp@1.0.4: {} + mlly@1.7.1: + dependencies: + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.2.0 + ufo: 1.5.4 + mnemonist@0.39.5: dependencies: obliterator: 2.0.4 @@ -6415,6 +6632,10 @@ snapshots: dependencies: yocto-queue: 0.1.0 + p-limit@4.0.0: + dependencies: + yocto-queue: 1.1.1 + p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -6483,6 +6704,8 @@ snapshots: pathe@1.1.2: {} + pathval@1.1.1: {} + pathval@2.0.0: {} pause-stream@0.0.11: @@ -6537,6 +6760,12 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 + pkg-types@1.2.0: + dependencies: + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + postcss@8.4.38: dependencies: nanoid: 3.3.7 @@ -6557,6 +6786,12 @@ snapshots: prettier@3.3.3: {} + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + proc-log@3.0.0: {} process-nextick-args@2.0.1: {} @@ -6631,6 +6866,8 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-is@18.3.1: {} + read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 @@ -6992,6 +7229,10 @@ snapshots: strip-json-comments@5.0.1: {} + strip-literal@1.3.0: + dependencies: + acorn: 8.11.3 + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -7034,10 +7275,14 @@ snapshots: tinyexec@0.3.0: {} + tinypool@0.7.0: {} + tinypool@1.0.1: {} tinyrainbow@1.2.0: {} + tinyspy@2.2.1: {} + tinyspy@3.0.0: {} tmp@0.0.33: @@ -7127,6 +7372,8 @@ snapshots: transitivePeerDependencies: - supports-color + type-detect@4.1.0: {} + type-fest@0.21.3: {} type-fest@1.4.0: {} @@ -7139,6 +7386,8 @@ snapshots: typescript@5.6.2: {} + ufo@1.5.4: {} + undefsafe@2.0.5: {} undici-types@6.19.8: {} @@ -7203,6 +7452,24 @@ snapshots: vary@1.1.2: {} + vite-node@0.34.6(@types/node@22.5.5): + dependencies: + cac: 6.7.14 + debug: 4.3.7 + mlly: 1.7.1 + pathe: 1.1.2 + picocolors: 1.1.0 + vite: 5.2.11(@types/node@22.5.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + vite-node@2.1.1(@types/node@22.5.5): dependencies: cac: 6.7.14 @@ -7239,6 +7506,41 @@ snapshots: '@types/node': 22.5.5 fsevents: 2.3.3 + vitest@0.34.6: + dependencies: + '@types/chai': 4.3.20 + '@types/chai-subset': 1.3.5 + '@types/node': 22.5.5 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + acorn: 8.11.3 + acorn-walk: 8.3.2 + cac: 6.7.14 + chai: 4.5.0 + debug: 4.3.7 + local-pkg: 0.4.3 + magic-string: 0.30.11 + pathe: 1.1.2 + picocolors: 1.1.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.9.0 + tinypool: 0.7.0 + vite: 5.2.11(@types/node@22.5.5) + vite-node: 0.34.6(@types/node@22.5.5) + why-is-node-running: 2.3.0 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + vitest@2.1.1(@types/node@22.5.5): dependencies: '@vitest/expect': 2.1.1 @@ -7403,4 +7705,6 @@ snapshots: yocto-queue@0.1.0: {} + yocto-queue@1.1.1: {} + zod@3.23.8: {} From b85150cfbfca0b5c18e8b6d28660277948f6dead Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 2 Oct 2024 12:05:09 +0200 Subject: [PATCH 220/298] Implement basic diagnostics apis for MongoDB. --- .../src/api/MongoRouteAPIAdapter.ts | 113 ++++++++++++++++-- .../src/replication/ChangeStream.ts | 6 +- modules/module-mongodb/src/utils.ts | 4 + packages/service-core/src/api/RouteAPI.ts | 2 +- 4 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 modules/module-mongodb/src/utils.ts diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index e3a3eea72..bcf53b195 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -1,4 +1,4 @@ -import { api, ParseSyncRulesOptions } from '@powersync/service-core'; +import { api, ParseSyncRulesOptions, SourceTable } from '@powersync/service-core'; import * as mongo from 'mongodb'; import * as sync_rules from '@powersync/service-sync-rules'; @@ -6,6 +6,7 @@ import * as service_types from '@powersync/service-types'; import * as types from '../types/types.js'; import { MongoManager } from '../replication/MongoManager.js'; import { createCheckpoint, getMongoLsn } from '../replication/MongoRelation.js'; +import { escapeRegExp } from '../utils.js'; export class MongoRouteAPIAdapter implements api.RouteAPI { protected client: mongo.MongoClient; @@ -37,11 +38,21 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { } async getConnectionStatus(): Promise { - // TODO: Implement const base = { id: this.config.id, uri: types.baseUri(this.config) }; + + try { + await this.client.connect(); + await this.db.command({ hello: 1 }); + } catch (e) { + return { + ...base, + connected: false, + errors: [{ level: 'fatal', message: e.message }] + }; + } return { ...base, connected: true, @@ -64,14 +75,100 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { tablePatterns: sync_rules.TablePattern[], sqlSyncRules: sync_rules.SqlSyncRules ): Promise { - // TODO: Implement - return []; - } + let result: api.PatternResult[] = []; + for (let tablePattern of tablePatterns) { + const schema = tablePattern.schema; - async getReplicationLag(syncRulesId: string): Promise { - // TODO: Implement + let patternResult: api.PatternResult = { + schema: schema, + pattern: tablePattern.tablePattern, + wildcard: tablePattern.isWildcard + }; + result.push(patternResult); + + let nameFilter: RegExp | string; + if (tablePattern.isWildcard) { + nameFilter = new RegExp('^' + escapeRegExp(tablePattern.tablePrefix)); + } else { + nameFilter = tablePattern.name; + } + + // Check if the collection exists + const collections = await this.client + .db(schema) + .listCollections( + { + name: nameFilter + }, + { nameOnly: true } + ) + .toArray(); + + if (tablePattern.isWildcard) { + patternResult.tables = []; + for (let collection of collections) { + const sourceTable = new SourceTable( + 0, + this.connectionTag, + collection.name, + schema, + collection.name, + [], + true + ); + const syncData = sqlSyncRules.tableSyncsData(sourceTable); + const syncParameters = sqlSyncRules.tableSyncsParameters(sourceTable); + patternResult.tables.push({ + schema, + name: collection.name, + replication_id: ['_id'], + data_queries: syncData, + parameter_queries: syncParameters, + errors: [] + }); + } + } else { + const sourceTable = new SourceTable( + 0, + this.connectionTag, + tablePattern.name, + schema, + tablePattern.name, + [], + true + ); + + const syncData = sqlSyncRules.tableSyncsData(sourceTable); + const syncParameters = sqlSyncRules.tableSyncsParameters(sourceTable); + + if (collections.length == 1) { + patternResult.table = { + schema, + name: tablePattern.name, + replication_id: ['_id'], + data_queries: syncData, + parameter_queries: syncParameters, + errors: [] + }; + } else { + patternResult.table = { + schema, + name: tablePattern.name, + replication_id: ['_id'], + data_queries: syncData, + parameter_queries: syncParameters, + errors: [{ level: 'warning', message: `Collection ${schema}.${tablePattern.name} not found` }] + }; + } + } + } + return result; + } - return 0; + async getReplicationLag(syncRulesId: string): Promise { + // There is no fast way to get replication lag in bytes in MongoDB. + // We can get replication lag in seconds, but need a different API for that. + return undefined; } async getReplicationHead(): Promise { diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 92392d863..3d36e1814 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -10,6 +10,7 @@ import { getMongoRelation, mongoLsnToTimestamp } from './MongoRelation.js'; +import { escapeRegExp } from '../utils.js'; export const ZERO_LSN = '0000000000000000'; @@ -496,8 +497,3 @@ async function touch() { // or reduce PING_INTERVAL here. return container.probes.touch(); } - -function escapeRegExp(string: string) { - // https://stackoverflow.com/a/3561711/214837 - return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); -} diff --git a/modules/module-mongodb/src/utils.ts b/modules/module-mongodb/src/utils.ts new file mode 100644 index 000000000..badee3083 --- /dev/null +++ b/modules/module-mongodb/src/utils.ts @@ -0,0 +1,4 @@ +export function escapeRegExp(string: string) { + // https://stackoverflow.com/a/3561711/214837 + return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); +} diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index 393d56342..f77f5c26b 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -44,7 +44,7 @@ export interface RouteAPI { * replicated yet, in bytes. * @param {string} syncRulesId An identifier representing which set of sync rules the lag is required for. */ - getReplicationLag(syncRulesId: string): Promise; + getReplicationLag(syncRulesId: string): Promise; /** * Get the current LSN or equivalent replication HEAD position identifier From b863b67032c36e2a169d4943e711c1ab4eec641a Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 2 Oct 2024 12:44:06 +0200 Subject: [PATCH 221/298] Add test for getConnectionSchema. --- .../src/api/MongoRouteAPIAdapter.ts | 116 +++++++++++++++++- .../test/src/mongo_test.test.ts | 49 +++++++- 2 files changed, 158 insertions(+), 7 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index bcf53b195..508f30e38 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -5,12 +5,12 @@ import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import * as types from '../types/types.js'; import { MongoManager } from '../replication/MongoManager.js'; -import { createCheckpoint, getMongoLsn } from '../replication/MongoRelation.js'; +import { constructAfterRecord, createCheckpoint, getMongoLsn } from '../replication/MongoRelation.js'; import { escapeRegExp } from '../utils.js'; export class MongoRouteAPIAdapter implements api.RouteAPI { protected client: mongo.MongoClient; - private db: mongo.Db; + public db: mongo.Db; connectionTag: string; defaultSchema: string; @@ -176,8 +176,116 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { } async getConnectionSchema(): Promise { - // TODO: Implement + const sampleSize = 50; - return []; + const databases = await this.db.admin().listDatabases({ authorizedDatabases: true, nameOnly: true }); + return ( + await Promise.all( + databases.databases.map(async (db) => { + if (db.name == 'local' || db.name == 'admin') { + return null; + } + const collections = await this.client.db(db.name).listCollections().toArray(); + + const tables = await Promise.all( + collections.map(async (collection) => { + const sampleDocuments = await this.db + .collection(collection.name) + .aggregate([{ $sample: { size: sampleSize } }]) + .toArray(); + + if (sampleDocuments.length > 0) { + const columns = this.getColumnsFromDocuments(sampleDocuments); + + return { + name: collection.name, + // Since documents are sampled in a random order, we need to sort + // to get a consistent order + columns: columns.sort((a, b) => a.name.localeCompare(b.name)) + } satisfies service_types.TableSchema; + } else { + return { + name: collection.name, + columns: [] + } satisfies service_types.TableSchema; + } + }) + ); + return { + name: db.name, + tables: tables + } satisfies service_types.DatabaseSchema; + }) + ) + ).filter((r) => r != null); + } + + private getColumnsFromDocuments(documents: mongo.BSON.Document[]) { + let columns = new Map }>(); + for (const document of documents) { + const parsed = constructAfterRecord(document); + for (const key in parsed) { + const value = parsed[key]; + const type = sync_rules.sqliteTypeOf(value); + const sqliteType = sync_rules.ExpressionType.fromTypeText(type); + let entry = columns.get(key); + if (entry == null) { + entry = { sqliteType, bsonTypes: new Set() }; + columns.set(key, entry); + } else { + entry.sqliteType = entry.sqliteType.or(sqliteType); + } + const bsonType = this.getBsonType(document[key]); + if (bsonType != null) { + entry.bsonTypes.add(bsonType); + } + } + } + return [...columns.entries()].map(([key, value]) => { + return { + name: key, + sqlite_type: value.sqliteType.typeFlags, + internal_type: value.bsonTypes.size == 0 ? '' : [...value.bsonTypes].join(' | ') + }; + }); + } + + private getBsonType(data: any): string | null { + if (data == null) { + // null or undefined + return 'Null'; + } else if (typeof data == 'string') { + return 'String'; + } else if (typeof data == 'number') { + if (Number.isInteger(data)) { + return 'Integer'; + } else { + return 'Double'; + } + } else if (typeof data == 'bigint') { + return 'Long'; + } else if (typeof data == 'boolean') { + return 'Boolean'; + } else if (data instanceof mongo.ObjectId) { + return 'ObjectId'; + } else if (data instanceof mongo.UUID) { + return 'UUID'; + } else if (data instanceof Date) { + return 'Date'; + } else if (data instanceof mongo.Timestamp) { + return 'Timestamp'; + } else if (data instanceof mongo.Binary) { + return 'Binary'; + } else if (data instanceof mongo.Long) { + return 'Long'; + } else if (Array.isArray(data)) { + return 'Array'; + } else if (data instanceof Uint8Array) { + return 'Binary'; + } else if (typeof data == 'object') { + return 'Object'; + } else { + return null; + } } } diff --git a/modules/module-mongodb/test/src/mongo_test.test.ts b/modules/module-mongodb/test/src/mongo_test.test.ts index 1b36dab90..ed03ebcd9 100644 --- a/modules/module-mongodb/test/src/mongo_test.test.ts +++ b/modules/module-mongodb/test/src/mongo_test.test.ts @@ -1,9 +1,10 @@ +import { MongoRouteAPIAdapter } from '@module/api/MongoRouteAPIAdapter.js'; import { ChangeStream } from '@module/replication/ChangeStream.js'; +import { constructAfterRecord } from '@module/replication/MongoRelation.js'; +import { SqliteRow } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; import { describe, expect, test } from 'vitest'; -import { clearTestDb, connectMongoData } from './util.js'; -import { SqliteRow } from '@powersync/service-sync-rules'; -import { constructAfterRecord } from '@module/replication/MongoRelation.js'; +import { clearTestDb, connectMongoData, TEST_CONNECTION_OPTIONS } from './util.js'; describe('mongo data types', () => { async function setupTable(db: mongo.Db) { @@ -202,6 +203,48 @@ describe('mongo data types', () => { await client.close(); } }); + + test('connection schema', async () => { + const adapter = new MongoRouteAPIAdapter({ + type: 'mongodb', + ...TEST_CONNECTION_OPTIONS + }); + try { + const db = adapter.db; + await clearTestDb(db); + + const collection = db.collection('test_data'); + await setupTable(db); + await insert(collection); + + const schema = await adapter.getConnectionSchema(); + const dbSchema = schema.filter((s) => s.name == TEST_CONNECTION_OPTIONS.database)[0]; + expect(dbSchema).not.toBeNull(); + expect(dbSchema.tables).toEqual([ + { + name: 'test_data', + columns: [ + { name: '_id', sqlite_type: 4, internal_type: 'Integer' }, + { name: 'bool', sqlite_type: 4, internal_type: 'Boolean' }, + { name: 'bytea', sqlite_type: 1, internal_type: 'Binary' }, + { name: 'date', sqlite_type: 2, internal_type: 'Date' }, + { name: 'float', sqlite_type: 8, internal_type: 'Double' }, + { name: 'int2', sqlite_type: 4, internal_type: 'Integer' }, + { name: 'int4', sqlite_type: 4, internal_type: 'Integer' }, + { name: 'int8', sqlite_type: 4, internal_type: 'Long' }, + { name: 'nested', sqlite_type: 2, internal_type: 'Object' }, + { name: 'null', sqlite_type: 0, internal_type: 'Null' }, + { name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' }, + { name: 'text', sqlite_type: 2, internal_type: 'String' }, + { name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' }, + { name: 'uuid', sqlite_type: 2, internal_type: 'UUID' } + ] + } + ]); + } finally { + await adapter.shutdown(); + } + }); }); /** From 5050204a5f7e3748533708e237d5134177a53144 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 2 Oct 2024 12:56:08 +0200 Subject: [PATCH 222/298] Improve schema filtering; return defaultSchema in API. --- .../src/api/MongoRouteAPIAdapter.ts | 73 ++++++++++--------- packages/service-core/src/api/RouteAPI.ts | 3 - packages/service-core/src/api/schema.ts | 8 +- packages/types/src/definitions.ts | 4 +- 4 files changed, 46 insertions(+), 42 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 508f30e38..1297a67b1 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -179,45 +179,46 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { const sampleSize = 50; const databases = await this.db.admin().listDatabases({ authorizedDatabases: true, nameOnly: true }); - return ( - await Promise.all( - databases.databases.map(async (db) => { - if (db.name == 'local' || db.name == 'admin') { - return null; - } - const collections = await this.client.db(db.name).listCollections().toArray(); + const filteredDatabases = databases.databases.filter((db) => { + return !['local', 'admin', 'config'].includes(db.name); + }); + return await Promise.all( + filteredDatabases.map(async (db) => { + const collections = await this.client.db(db.name).listCollections().toArray(); + const filtered = collections.filter((c) => { + return !['_powersync_checkpoints'].includes(c.name); + }); - const tables = await Promise.all( - collections.map(async (collection) => { - const sampleDocuments = await this.db - .collection(collection.name) - .aggregate([{ $sample: { size: sampleSize } }]) - .toArray(); + const tables = await Promise.all( + filtered.map(async (collection) => { + const sampleDocuments = await this.db + .collection(collection.name) + .aggregate([{ $sample: { size: sampleSize } }]) + .toArray(); - if (sampleDocuments.length > 0) { - const columns = this.getColumnsFromDocuments(sampleDocuments); + if (sampleDocuments.length > 0) { + const columns = this.getColumnsFromDocuments(sampleDocuments); - return { - name: collection.name, - // Since documents are sampled in a random order, we need to sort - // to get a consistent order - columns: columns.sort((a, b) => a.name.localeCompare(b.name)) - } satisfies service_types.TableSchema; - } else { - return { - name: collection.name, - columns: [] - } satisfies service_types.TableSchema; - } - }) - ); - return { - name: db.name, - tables: tables - } satisfies service_types.DatabaseSchema; - }) - ) - ).filter((r) => r != null); + return { + name: collection.name, + // Since documents are sampled in a random order, we need to sort + // to get a consistent order + columns: columns.sort((a, b) => a.name.localeCompare(b.name)) + } satisfies service_types.TableSchema; + } else { + return { + name: collection.name, + columns: [] + } satisfies service_types.TableSchema; + } + }) + ); + return { + name: db.name, + tables: tables + } satisfies service_types.DatabaseSchema; + }) + ); } private getColumnsFromDocuments(documents: mongo.BSON.Document[]) { diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index f77f5c26b..bbe92180c 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -54,9 +54,6 @@ export interface RouteAPI { /** * @returns The schema for tables inside the connected database. This is typically * used to validate sync rules. - * Side Note: https://github.com/powersync-ja/powersync-service/blob/33bbb8c0ab1c48555956593f427fc674a8f15768/packages/types/src/definitions.ts#L100 - * contains `pg_type` which we might need to deprecate and add another generic - * type field - or just use this field as the connection specific type. */ getConnectionSchema(): Promise; diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index aff6d770f..64b48e1a0 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -5,7 +5,9 @@ import * as api from '../api/api-index.js'; export async function getConnectionsSchema(api: api.RouteAPI): Promise { if (!api) { return { - connections: [] + connections: [], + defaultConnectionTag: 'default', + defaultSchema: '' }; } @@ -18,6 +20,8 @@ export async function getConnectionsSchema(api: api.RouteAPI): Promise; From 60f6414d5af7936b2293f676f0c6060fc33f6f15 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 3 Oct 2024 13:32:16 +0200 Subject: [PATCH 223/298] Initialize tag variable before use in nested function call. --- packages/service-core/src/api/diagnostics.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 7b6da99ce..eca98da9b 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -53,6 +53,8 @@ export async function getSyncRulesStatus( }; } + const sourceConfig = await apiHandler.getSourceConfig(); + const tag = sourceConfig.tag ?? DEFAULT_TAG; const systemStorage = live_status ? bucketStorage.getInstance(sync_rules) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; @@ -128,15 +130,12 @@ export async function getSyncRulesStatus( }) ); - const sourceConfig = await apiHandler.getSourceConfig(); - const tag = sourceConfig.tag ?? DEFAULT_TAG; - return { content: include_content ? sync_rules.sync_rules_content : undefined, connections: [ { id: sourceConfig.id ?? DEFAULT_DATASOURCE_ID, - tag: sourceConfig.tag ?? DEFAULT_TAG, + tag: tag, slot_name: sync_rules.slot_name, initial_replication_done: status?.snapshot_done ?? false, // TODO: Rename? From 04937a81adcabefeee73f40153f64cd0363ce35a Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 3 Oct 2024 14:17:12 +0200 Subject: [PATCH 224/298] Add comment for tag default value --- packages/service-core/src/api/diagnostics.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index eca98da9b..387ed4dff 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -54,6 +54,10 @@ export async function getSyncRulesStatus( } const sourceConfig = await apiHandler.getSourceConfig(); + // This is a bit weird. + // This method can run under some situations if no connection is configured. + // It will return a default tag if no connection is available. This default tag + // is not module specific. const tag = sourceConfig.tag ?? DEFAULT_TAG; const systemStorage = live_status ? bucketStorage.getInstance(sync_rules) : undefined; const status = await systemStorage?.getStatus(); From e4d19b2fd142badf589e712b3368afc5bcce1c14 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 3 Oct 2024 15:16:21 +0200 Subject: [PATCH 225/298] Changeset --- .changeset/olive-spoons-stare.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/olive-spoons-stare.md diff --git a/.changeset/olive-spoons-stare.md b/.changeset/olive-spoons-stare.md new file mode 100644 index 000000000..df58ecaf4 --- /dev/null +++ b/.changeset/olive-spoons-stare.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-core': patch +--- + +Moved tag variable initialization in diagnostics route to ensure it is initialized before usage From b8dfc98b0d575dd582b9bdf2c73a7273b7c4aad2 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 3 Oct 2024 15:44:38 +0200 Subject: [PATCH 226/298] Comment update --- packages/service-core/src/api/diagnostics.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 387ed4dff..2ebf5ada5 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -54,10 +54,8 @@ export async function getSyncRulesStatus( } const sourceConfig = await apiHandler.getSourceConfig(); - // This is a bit weird. - // This method can run under some situations if no connection is configured. - // It will return a default tag if no connection is available. This default tag - // is not module specific. + // This method can run under some situations if no connection is configured yet. + // It will return a default tag in such a case. This default tag is not module specific. const tag = sourceConfig.tag ?? DEFAULT_TAG; const systemStorage = live_status ? bucketStorage.getInstance(sync_rules) : undefined; const status = await systemStorage?.getStatus(); From 4a654f1f3f9608270e3747902f6a0e083ba0e673 Mon Sep 17 00:00:00 2001 From: stevensJourney <51082125+stevensJourney@users.noreply.github.com> Date: Fri, 4 Oct 2024 17:20:11 +0200 Subject: [PATCH 227/298] list all databases (#96) --- .../src/api/MongoRouteAPIAdapter.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 1297a67b1..589870f66 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -3,9 +3,9 @@ import * as mongo from 'mongodb'; import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; -import * as types from '../types/types.js'; import { MongoManager } from '../replication/MongoManager.js'; -import { constructAfterRecord, createCheckpoint, getMongoLsn } from '../replication/MongoRelation.js'; +import { constructAfterRecord, createCheckpoint } from '../replication/MongoRelation.js'; +import * as types from '../types/types.js'; import { escapeRegExp } from '../utils.js'; export class MongoRouteAPIAdapter implements api.RouteAPI { @@ -178,13 +178,24 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { async getConnectionSchema(): Promise { const sampleSize = 50; - const databases = await this.db.admin().listDatabases({ authorizedDatabases: true, nameOnly: true }); + const databases = await this.db.admin().listDatabases({ nameOnly: true }); const filteredDatabases = databases.databases.filter((db) => { return !['local', 'admin', 'config'].includes(db.name); }); - return await Promise.all( + const databaseSchemas = await Promise.all( filteredDatabases.map(async (db) => { - const collections = await this.client.db(db.name).listCollections().toArray(); + /** + * Filtering the list of database with `authorizedDatabases: true` + * does not produce the full list of databases under some circumstances. + * This catches any potential auth errors. + */ + let collections: mongo.CollectionInfo[]; + try { + collections = await this.client.db(db.name).listCollections().toArray(); + } catch (ex) { + return null; + } + const filtered = collections.filter((c) => { return !['_powersync_checkpoints'].includes(c.name); }); @@ -219,6 +230,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { } satisfies service_types.DatabaseSchema; }) ); + return databaseSchemas.filter((schema) => !!schema); } private getColumnsFromDocuments(documents: mongo.BSON.Document[]) { From 9683172496c41e2f67b380f396439b7cefa94d41 Mon Sep 17 00:00:00 2001 From: stevensJourney <51082125+stevensJourney@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:50:14 +0200 Subject: [PATCH 228/298] [MongoDB] Schema Endpoint Fix (#97) * test: populate schema type field * pg_type restore --- modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts | 5 ++++- packages/types/src/definitions.ts | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 589870f66..e96cc8cb8 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -255,10 +255,13 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { } } return [...columns.entries()].map(([key, value]) => { + const internal_type = value.bsonTypes.size == 0 ? '' : [...value.bsonTypes].join(' | '); return { name: key, + type: internal_type, sqlite_type: value.sqliteType.typeFlags, - internal_type: value.bsonTypes.size == 0 ? '' : [...value.bsonTypes].join(' | ') + internal_type, + pg_type: internal_type }; }); } diff --git a/packages/types/src/definitions.ts b/packages/types/src/definitions.ts index 9f176c62a..dc3bdf879 100644 --- a/packages/types/src/definitions.ts +++ b/packages/types/src/definitions.ts @@ -126,13 +126,13 @@ export const TableSchema = t.object({ * Full type name, e.g. "character varying(255)[]" * @deprecated - use internal_type */ - type: t.string.optional(), + type: t.string, /** * Internal postgres type, e.g. "varchar[]". * @deprecated - use internal_type instead */ - pg_type: t.string.optional() + pg_type: t.string }) ) }); From 39638f4b109a60d19557c7ef772d3ea58fa55772 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 8 Oct 2024 11:48:31 +0200 Subject: [PATCH 229/298] Lockfile update and merge conflict fixes --- packages/service-core/src/api/diagnostics.ts | 9 +- packages/types/src/config/PowerSyncConfig.ts | 1 - pnpm-lock.yaml | 437 ++++++++++++++++++- 3 files changed, 437 insertions(+), 10 deletions(-) diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index ad261f4cf..22d9530ef 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -63,13 +63,6 @@ export async function getSyncRulesStatus( let tables_flat: TableInfo[] = []; - const sourceConfig = await api?.getSourceConfig(); - // This is a bit weird. - // This method can run under some situations if no connection is configured. - // It will return a default tag if not connection is available. This default tag - // is not module specific. - const tag = sourceConfig?.tag ?? DEFAULT_TAG; - if (check_connection) { const source_table_patterns = rules.getSourceTables(); const resolved_tables = await apiHandler.getDebugTablesInfo(source_table_patterns, rules); @@ -88,7 +81,7 @@ export async function getSyncRulesStatus( const lastCheckpoint = await systemStorage.getCheckpoint(); replication_lag_bytes = await apiHandler.getReplicationLag({ replication_identifier: systemStorage.slot_name, - last_checkpoint_identifier: lastCheckpoint.lsn + last_checkpoint_identifier: lastCheckpoint.checkpoint }); } catch (e) { // Ignore diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index 53b238570..7f87f7002 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -95,7 +95,6 @@ export type StorageConfig = t.Decoded; export const powerSyncConfig = t.object({ replication: t .object({ - connections: t.array(DataSourceConfig).optional() // This uses the generic config which may have additional fields connections: t.array(genericDataSourceConfig).optional() }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index beb4d92be..16007b473 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,6 +94,58 @@ importers: specifier: ^2.1.1 version: 2.1.1(@types/node@22.5.5) + modules/module-mysql: + dependencies: + '@powersync/lib-services-framework': + specifier: workspace:* + version: link:../../libs/lib-services + '@powersync/service-core': + specifier: workspace:* + version: link:../../packages/service-core + '@powersync/service-jsonbig': + specifier: workspace:* + version: link:../../packages/jsonbig + '@powersync/service-sync-rules': + specifier: workspace:* + version: link:../../packages/sync-rules + '@powersync/service-types': + specifier: workspace:* + version: link:../../packages/types + '@vlasky/zongji': + specifier: link:../../../zongji + version: link:../../../zongji + async: + specifier: ^3.2.4 + version: 3.2.5 + mysql2: + specifier: ^3.11.0 + version: 3.11.3 + ts-codec: + specifier: ^1.2.2 + version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + devDependencies: + '@types/async': + specifier: ^3.2.24 + version: 3.2.24 + '@types/uuid': + specifier: ^9.0.8 + version: 9.0.8 + typescript: + specifier: ^5.5.4 + version: 5.6.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) + vitest: + specifier: ^0.34.6 + version: 0.34.6 + modules/module-postgres: dependencies: '@powersync/lib-services-framework': @@ -373,6 +425,9 @@ importers: '@powersync/service-jsonbig': specifier: workspace:* version: link:../packages/jsonbig + '@powersync/service-module-mysql': + specifier: workspace:* + version: link:../modules/module-mysql '@powersync/service-module-postgres': specifier: workspace:* version: link:../modules/module-postgres @@ -740,6 +795,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1209,6 +1268,9 @@ packages: resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sindresorhus/is@5.6.0': resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} engines: {node: '>=14.16'} @@ -1253,6 +1315,12 @@ packages: '@types/body-parser@1.19.5': resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + '@types/chai-subset@1.3.5': + resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} + + '@types/chai@4.3.20': + resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} + '@types/connect@3.4.36': resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} @@ -1364,6 +1432,9 @@ packages: '@types/ws@8.2.3': resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} + '@vitest/expect@0.34.6': + resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} + '@vitest/expect@2.1.1': resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} @@ -1382,15 +1453,27 @@ packages: '@vitest/pretty-format@2.1.1': resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} + '@vitest/runner@0.34.6': + resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} + '@vitest/runner@2.1.1': resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} + '@vitest/snapshot@0.34.6': + resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} + '@vitest/snapshot@2.1.1': resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} + '@vitest/spy@0.34.6': + resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} + '@vitest/spy@2.1.1': resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} + '@vitest/utils@0.34.6': + resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} + '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} @@ -1423,6 +1506,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -1481,6 +1569,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -1510,6 +1602,9 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1527,6 +1622,10 @@ packages: avvio@8.3.2: resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1605,6 +1704,10 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} engines: {node: '>=12'} @@ -1624,6 +1727,9 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1723,6 +1829,9 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -1795,6 +1904,10 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1817,10 +1930,18 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -2045,6 +2166,9 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2298,6 +2422,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -2413,6 +2540,10 @@ packages: light-my-request@5.13.0: resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2438,9 +2569,15 @@ packages: resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} engines: {node: '>= 12.0.0'} + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + lossless-json@2.0.11: resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + loupe@3.1.1: resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} @@ -2459,6 +2596,10 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lru.min@1.1.1: + resolution: {integrity: sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} @@ -2561,6 +2702,9 @@ packages: engines: {node: '>=10'} hasBin: true + mlly@1.7.2: + resolution: {integrity: sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==} + mnemonist@0.39.5: resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} @@ -2614,6 +2758,14 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mysql2@3.11.3: + resolution: {integrity: sha512-Qpu2ADfbKzyLdwC/5d4W7+5Yz7yBzCU05YWt5npWzACST37wJsB23wgOSo00qi043urkiRwXtEvJc9UnuLX/MQ==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2763,6 +2915,10 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -2826,6 +2982,9 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -2876,6 +3035,9 @@ packages: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true + pkg-types@1.2.1: + resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} + postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} @@ -2906,6 +3068,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + proc-log@3.0.0: resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -2993,6 +3159,9 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + read-package-json-fast@3.0.2: resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3160,6 +3329,9 @@ packages: engines: {node: '>=10'} hasBin: true + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -3285,6 +3457,10 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + ssri@10.0.6: resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3346,6 +3522,9 @@ packages: resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} engines: {node: '>=14.16'} + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -3388,6 +3567,10 @@ packages: tinyexec@0.3.0: resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + tinypool@1.0.1: resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3396,6 +3579,10 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} + tinyspy@2.2.1: + resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} + engines: {node: '>=14.0.0'} + tinyspy@3.0.0: resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} engines: {node: '>=14.0.0'} @@ -3483,6 +3670,10 @@ packages: resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -3503,6 +3694,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} @@ -3565,6 +3759,11 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vite-node@0.34.6: + resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} + engines: {node: '>=v14.18.0'} + hasBin: true + vite-node@2.1.1: resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3606,6 +3805,37 @@ packages: terser: optional: true + vitest@0.34.6: + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + vitest@2.1.1: resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3774,6 +4004,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} @@ -4061,6 +4295,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jest/schemas@29.6.3': + dependencies: + '@sinclair/typebox': 0.27.8 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.4.15': {} @@ -4672,6 +4910,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@sinclair/typebox@0.27.8': {} + '@sindresorhus/is@5.6.0': {} '@syncpoint/wkx@0.5.2': @@ -4710,6 +4950,12 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 22.5.5 + '@types/chai-subset@1.3.5': + dependencies: + '@types/chai': 4.3.20 + + '@types/chai@4.3.20': {} + '@types/connect@3.4.36': dependencies: '@types/node': 22.5.5 @@ -4837,6 +5083,12 @@ snapshots: dependencies: '@types/node': 22.5.5 + '@vitest/expect@0.34.6': + dependencies: + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + chai: 4.5.0 + '@vitest/expect@2.1.1': dependencies: '@vitest/spy': 2.1.1 @@ -4856,21 +5108,43 @@ snapshots: dependencies: tinyrainbow: 1.2.0 + '@vitest/runner@0.34.6': + dependencies: + '@vitest/utils': 0.34.6 + p-limit: 4.0.0 + pathe: 1.1.2 + '@vitest/runner@2.1.1': dependencies: '@vitest/utils': 2.1.1 pathe: 1.1.2 + '@vitest/snapshot@0.34.6': + dependencies: + magic-string: 0.30.11 + pathe: 1.1.2 + pretty-format: 29.7.0 + '@vitest/snapshot@2.1.1': dependencies: '@vitest/pretty-format': 2.1.1 magic-string: 0.30.11 pathe: 1.1.2 + '@vitest/spy@0.34.6': + dependencies: + tinyspy: 2.2.1 + '@vitest/spy@2.1.1': dependencies: tinyspy: 3.0.0 + '@vitest/utils@0.34.6': + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + '@vitest/utils@2.1.1': dependencies: '@vitest/pretty-format': 2.1.1 @@ -4898,6 +5172,8 @@ snapshots: acorn@8.11.3: {} + acorn@8.12.1: {} + agent-base@6.0.2: dependencies: debug: 4.3.7 @@ -4950,6 +5226,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} anymatch@3.1.3: @@ -4974,6 +5252,8 @@ snapshots: array-union@2.1.0: {} + assertion-error@1.1.0: {} + assertion-error@2.0.1: {} async-mutex@0.5.0: @@ -4989,6 +5269,8 @@ snapshots: '@fastify/error': 3.4.1 fastq: 1.17.1 + aws-ssl-profiles@1.1.2: {} + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -5114,6 +5396,16 @@ snapshots: camelcase@7.0.1: {} + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + chai@5.1.1: dependencies: assertion-error: 2.0.1 @@ -5137,6 +5429,10 @@ snapshots: chardet@0.7.0: {} + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + check-error@2.1.1: {} chokidar@3.6.0: @@ -5238,6 +5534,8 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 + confbox@0.1.8: {} + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -5312,6 +5610,10 @@ snapshots: dependencies: mimic-response: 3.1.0 + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + deep-eql@5.0.2: {} deep-extend@0.6.0: {} @@ -5330,8 +5632,12 @@ snapshots: delegates@1.0.0: {} + denque@2.1.0: {} + detect-indent@6.1.0: {} + diff-sequences@29.6.3: {} + diff@4.0.2: {} dir-glob@3.0.1: @@ -5585,6 +5891,10 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + get-caller-file@2.0.5: {} get-func-name@2.0.2: {} @@ -5730,7 +6040,6 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - optional: true ieee754@1.2.1: {} @@ -5851,6 +6160,8 @@ snapshots: is-path-inside@3.0.3: {} + is-property@1.0.2: {} + is-stream@2.0.1: {} is-subdir@1.2.0: @@ -5945,6 +6256,8 @@ snapshots: process-warning: 3.0.0 set-cookie-parser: 2.6.0 + local-pkg@0.4.3: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -5973,8 +6286,14 @@ snapshots: safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 + long@5.2.3: {} + lossless-json@2.0.11: {} + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + loupe@3.1.1: dependencies: get-func-name: 2.0.2 @@ -5990,6 +6309,8 @@ snapshots: lru-cache@7.18.3: {} + lru.min@1.1.1: {} + magic-string@0.30.11: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -6121,6 +6442,13 @@ snapshots: mkdirp@1.0.4: {} + mlly@1.7.2: + dependencies: + acorn: 8.12.1 + pathe: 1.1.2 + pkg-types: 1.2.1 + ufo: 1.5.4 + mnemonist@0.39.5: dependencies: obliterator: 2.0.4 @@ -6150,6 +6478,22 @@ snapshots: mute-stream@1.0.0: {} + mysql2@3.11.3: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.2.3 + lru.min: 1.1.1 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + nanoid@3.3.7: {} nearley@2.20.1: @@ -6366,6 +6710,10 @@ snapshots: dependencies: yocto-queue: 0.1.0 + p-limit@4.0.0: + dependencies: + yocto-queue: 1.1.1 + p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -6434,6 +6782,8 @@ snapshots: pathe@1.1.2: {} + pathval@1.1.1: {} + pathval@2.0.0: {} pause-stream@0.0.11: @@ -6488,6 +6838,12 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 + pkg-types@1.2.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.2 + pathe: 1.1.2 + postcss@8.4.38: dependencies: nanoid: 3.3.7 @@ -6508,6 +6864,12 @@ snapshots: prettier@3.3.3: {} + pretty-format@29.7.0: + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.3.1 + proc-log@3.0.0: {} process-nextick-args@2.0.1: {} @@ -6582,6 +6944,8 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 + react-is@18.3.1: {} + read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 @@ -6763,6 +7127,8 @@ snapshots: semver@7.6.2: {} + seq-queue@0.0.5: {} + set-blocking@2.0.0: {} set-cookie-parser@2.6.0: {} @@ -6887,6 +7253,8 @@ snapshots: sprintf-js@1.1.3: {} + sqlstring@2.3.3: {} + ssri@10.0.6: dependencies: minipass: 7.1.2 @@ -6943,6 +7311,10 @@ snapshots: strip-json-comments@5.0.1: {} + strip-literal@1.3.0: + dependencies: + acorn: 8.11.3 + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -6985,10 +7357,14 @@ snapshots: tinyexec@0.3.0: {} + tinypool@0.7.0: {} + tinypool@1.0.1: {} tinyrainbow@1.2.0: {} + tinyspy@2.2.1: {} + tinyspy@3.0.0: {} tmp@0.0.33: @@ -7078,6 +7454,8 @@ snapshots: transitivePeerDependencies: - supports-color + type-detect@4.1.0: {} + type-fest@0.21.3: {} type-fest@1.4.0: {} @@ -7090,6 +7468,8 @@ snapshots: typescript@5.6.2: {} + ufo@1.5.4: {} + undefsafe@2.0.5: {} undici-types@6.19.8: {} @@ -7154,6 +7534,24 @@ snapshots: vary@1.1.2: {} + vite-node@0.34.6(@types/node@22.5.5): + dependencies: + cac: 6.7.14 + debug: 4.3.7 + mlly: 1.7.2 + pathe: 1.1.2 + picocolors: 1.1.0 + vite: 5.2.11(@types/node@22.5.5) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + vite-node@2.1.1(@types/node@22.5.5): dependencies: cac: 6.7.14 @@ -7190,6 +7588,41 @@ snapshots: '@types/node': 22.5.5 fsevents: 2.3.3 + vitest@0.34.6: + dependencies: + '@types/chai': 4.3.20 + '@types/chai-subset': 1.3.5 + '@types/node': 22.5.5 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/utils': 0.34.6 + acorn: 8.11.3 + acorn-walk: 8.3.2 + cac: 6.7.14 + chai: 4.5.0 + debug: 4.3.7 + local-pkg: 0.4.3 + magic-string: 0.30.11 + pathe: 1.1.2 + picocolors: 1.1.0 + std-env: 3.7.0 + strip-literal: 1.3.0 + tinybench: 2.9.0 + tinypool: 0.7.0 + vite: 5.2.11(@types/node@22.5.5) + vite-node: 0.34.6(@types/node@22.5.5) + why-is-node-running: 2.3.0 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + vitest@2.1.1(@types/node@22.5.5): dependencies: '@vitest/expect': 2.1.1 @@ -7354,4 +7787,6 @@ snapshots: yocto-queue@0.1.0: {} + yocto-queue@1.1.1: {} + zod@3.23.8: {} From aec43b9d5079e53a2f72c7965b245137fc5f6415 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 9 Oct 2024 16:09:53 +0200 Subject: [PATCH 230/298] Added dev docker compose for mysql --- .../dev/docker/mysql/docker-compose.yaml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/modules/module-mysql/dev/docker/mysql/docker-compose.yaml b/modules/module-mysql/dev/docker/mysql/docker-compose.yaml index e69de29bb..6016d6a55 100644 --- a/modules/module-mysql/dev/docker/mysql/docker-compose.yaml +++ b/modules/module-mysql/dev/docker/mysql/docker-compose.yaml @@ -0,0 +1,17 @@ +services: + mysql: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: root_password + MYSQL_DATABASE: mydatabase + MYSQL_USER: myuser + MYSQL_PASSWORD: mypassword + ports: + - "3306:3306" + volumes: + - ./init-scripts/my.cnf:/etc/mysql/my.cnf + - ./init-scripts/mysql.sql:/docker-entrypoint-initdb.d/init_user.sql + - mysql_data:/var/lib/mysql + +volumes: + mysql_data: \ No newline at end of file From 09cbcf320dd6e961f856dacd0378eb432d0b4a19 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 9 Oct 2024 16:12:20 +0200 Subject: [PATCH 231/298] Added MySQL connection manager and use appropriate connections --- modules/module-mysql/package.json | 4 +- .../module-mysql/src/common/ReplicatedGTID.ts | 14 ++-- .../src/common/check-source-configuration.ts | 6 +- .../module-mysql/src/common/common-index.ts | 2 +- .../src/common/get-replication-columns.ts | 6 +- .../src/common/read-executed-gtid.ts | 44 +++++++++++ .../src/common/read-master-gtid.ts | 28 ------- .../src/replication/MySQLConnectionManager.ts | 75 +++++++++++++++++++ modules/module-mysql/src/utils/mysql_utils.ts | 16 ++-- 9 files changed, 144 insertions(+), 51 deletions(-) create mode 100644 modules/module-mysql/src/common/read-executed-gtid.ts delete mode 100644 modules/module-mysql/src/common/read-master-gtid.ts create mode 100644 modules/module-mysql/src/replication/MySQLConnectionManager.ts diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index c3bf76b20..8db50999b 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -28,7 +28,8 @@ "@powersync/service-jsonbig": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@vlasky/zongji": "link:../../../zongji", + "@powersync/mysql-zongji": "0.0.0-dev-20241008105633", + "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", "ts-codec": "^1.2.2", @@ -36,6 +37,7 @@ "uuid": "^9.0.1" }, "devDependencies": { + "@types/semver": "^7.5.4", "@types/async": "^3.2.24", "@types/uuid": "^9.0.8", "typescript": "^5.5.4", diff --git a/modules/module-mysql/src/common/ReplicatedGTID.ts b/modules/module-mysql/src/common/ReplicatedGTID.ts index 713f1f174..f87456ee1 100644 --- a/modules/module-mysql/src/common/ReplicatedGTID.ts +++ b/modules/module-mysql/src/common/ReplicatedGTID.ts @@ -37,14 +37,12 @@ export class ReplicatedGTID { throw new Error(`Invalid serialized GTID: ${comparable}`); } - const position: BinLogPosition = { - filename: components[2], - offset: parseInt(components[3]) - }; - return { raw_gtid: components[1], - position + position: { + filename: components[2], + offset: parseInt(components[3]) + } satisfies BinLogPosition }; } @@ -113,9 +111,9 @@ export class ReplicatedGTID { /** * Calculates the distance in bytes from this GTID to the provided argument. */ - async distanceTo(db: mysql.Pool, to: ReplicatedGTID): Promise { + async distanceTo(db: mysql.Connection, to: ReplicatedGTID): Promise { const [logFiles] = await mysql_utils.retriedQuery({ - db, + connection: db, query: `SHOW BINARY LOGS;` }); diff --git a/modules/module-mysql/src/common/check-source-configuration.ts b/modules/module-mysql/src/common/check-source-configuration.ts index 74bd7d02d..03e10c063 100644 --- a/modules/module-mysql/src/common/check-source-configuration.ts +++ b/modules/module-mysql/src/common/check-source-configuration.ts @@ -1,10 +1,10 @@ import mysql from 'mysql2/promise'; import * as mysql_utils from '../utils/mysql_utils.js'; -export async function checkSourceConfiguration(db: mysql.Pool) { +export async function checkSourceConfiguration(connection: mysql.Connection) { const errors: string[] = []; const [[result]] = await mysql_utils.retriedQuery({ - db, + connection, query: ` SELECT @@GLOBAL.gtid_mode AS gtid_mode, @@ -38,7 +38,7 @@ export async function checkSourceConfiguration(db: mysql.Pool) { } const [[binLogFormatResult]] = await mysql_utils.retriedQuery({ - db, + connection, query: `SHOW VARIABLES LIKE 'binlog_format';` }); diff --git a/modules/module-mysql/src/common/common-index.ts b/modules/module-mysql/src/common/common-index.ts index 8fa5a7cef..6da005718 100644 --- a/modules/module-mysql/src/common/common-index.ts +++ b/modules/module-mysql/src/common/common-index.ts @@ -2,5 +2,5 @@ export * from './check-source-configuration.js'; export * from './get-replication-columns.js'; export * from './get-tables-from-pattern.js'; export * from './mysql-to-sqlite.js'; -export * from './read-master-gtid.js'; +export * from './read-executed-gtid.js'; export * from './ReplicatedGTID.js'; diff --git a/modules/module-mysql/src/common/get-replication-columns.ts b/modules/module-mysql/src/common/get-replication-columns.ts index 63b58b53a..a6d8b0abe 100644 --- a/modules/module-mysql/src/common/get-replication-columns.ts +++ b/modules/module-mysql/src/common/get-replication-columns.ts @@ -19,7 +19,7 @@ export async function getReplicationIdentityColumns( ): Promise { const { db, schema, table_name } = options; const [primaryKeyColumns] = await mysql_utils.retriedQuery({ - db, + connection: db, query: ` SELECT s.COLUMN_NAME AS name, @@ -55,7 +55,7 @@ export async function getReplicationIdentityColumns( // TODO: test code with tables with unique keys, compound key etc. // No primary key, find the first valid unique key const [uniqueKeyColumns] = await mysql_utils.retriedQuery({ - db, + connection: db, query: ` SELECT s.INDEX_NAME, @@ -92,7 +92,7 @@ export async function getReplicationIdentityColumns( } const [allColumns] = await mysql_utils.retriedQuery({ - db, + connection: db, query: ` SELECT s.COLUMN_NAME AS name, diff --git a/modules/module-mysql/src/common/read-executed-gtid.ts b/modules/module-mysql/src/common/read-executed-gtid.ts new file mode 100644 index 000000000..5452129ae --- /dev/null +++ b/modules/module-mysql/src/common/read-executed-gtid.ts @@ -0,0 +1,44 @@ +import mysql from 'mysql2/promise'; +import * as mysql_utils from '../utils/mysql_utils.js'; +import { gte } from 'semver'; + +import { ReplicatedGTID } from './ReplicatedGTID.js'; + +/** + * Gets the current master HEAD GTID + */ +export async function readExecutedGtid(connection: mysql.Connection): Promise { + const [[versionResult]] = await mysql_utils.retriedQuery({ + connection, + query: `SELECT VERSION() as version` + }); + + const version = versionResult.version as string; + let binlogStatus: mysql.RowDataPacket; + if (gte(version, '8.4.0')) { + // Get the BinLog status + const [[binLogResult]] = await mysql_utils.retriedQuery({ + connection, + query: `SHOW BINARY LOG STATUS` + }); + binlogStatus = binLogResult; + } else { + // TODO Check if this works for version 5.7 + // Get the BinLog status + const [[binLogResult]] = await mysql_utils.retriedQuery({ + connection, + query: `SHOW MASTER STATUS` + }); + binlogStatus = binLogResult; + } + const position = { + filename: binlogStatus.File, + offset: parseInt(binlogStatus.Position) + }; + + return new ReplicatedGTID({ + // The head always points to the next position to start replication from + position, + raw_gtid: binlogStatus.Executed_Gtid_Set + }); +} diff --git a/modules/module-mysql/src/common/read-master-gtid.ts b/modules/module-mysql/src/common/read-master-gtid.ts deleted file mode 100644 index c80a1b32a..000000000 --- a/modules/module-mysql/src/common/read-master-gtid.ts +++ /dev/null @@ -1,28 +0,0 @@ -import mysql from 'mysql2/promise'; -import * as mysql_utils from '../utils/mysql_utils.js'; - -import { ReplicatedGTID } from './ReplicatedGTID.js'; - -/** - * Gets the current master HEAD GTID - */ -export async function readMasterGtid(db: mysql.Connection): Promise { - // Get the GTID - const [[gtidResult]] = await db.query('SELECT @@GLOBAL.gtid_executed as GTID;'); - - // Get the BinLog position if master - const [[masterResult]] = await mysql_utils.retriedQuery({ - db, - query: `SHOW master STATUS` - }); - const position = { - filename: masterResult.File, - offset: parseInt(masterResult.Position) - }; - - return new ReplicatedGTID({ - // The head always points to the next position to start replication from - position, - raw_gtid: gtidResult.GTID - }); -} diff --git a/modules/module-mysql/src/replication/MySQLConnectionManager.ts b/modules/module-mysql/src/replication/MySQLConnectionManager.ts new file mode 100644 index 000000000..8f42ccf6c --- /dev/null +++ b/modules/module-mysql/src/replication/MySQLConnectionManager.ts @@ -0,0 +1,75 @@ +import { ResolvedConnectionConfig } from '../types/types.js'; +import mysql from 'mysql2/promise'; +import * as mysql_utils from '../utils/mysql_utils.js'; +import ZongJi from '@vlasky/zongji'; + +export class MySQLConnectionManager { + /** + * Do not use this for any transactions. + */ + public readonly pool: mysql.Pool; + + private binlogListeners: ZongJi[] = []; + + constructor( + public options: ResolvedConnectionConfig, + public poolOptions: mysql.PoolOptions + ) { + // The pool is lazy - no connections are opened until a query is performed. + this.pool = mysql_utils.createPool(options, poolOptions); + } + + public get connectionTag() { + return this.options.tag; + } + + public get connectionId() { + return this.options.id; + } + + public get databaseName() { + return this.options.database; + } + + public get serverId() { + return this.options.; + } + + /** + * Create a new replication listener + */ + createBinlogListener(): ZongJi { + const listener = new ZongJi({ + host: this.options.hostname, + user: this.options.username, + password: this.options.password + }); + + this.binlogListeners.push(listener); + + return listener; + } + + /** + * Get a connection from the pool. It should be released back to the pool when done. + */ + async getConnection(): Promise { + return this.pool.getConnection(); + } + + async end(): Promise { + for (const listener of this.binlogListeners) { + listener.stop(); + } + + await this.pool.end(); + } + + destroy() { + for (const listener of this.binlogListeners) { + listener.stop(); + } + + this.pool.destroy(); + } +} diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 971f61f0d..230e73155 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -2,8 +2,8 @@ import { logger } from '@powersync/lib-services-framework'; import mysql from 'mysql2/promise'; import * as types from '../types/types.js'; -export type RetiredMySQLQueryOptions = { - db: mysql.Connection; +export type RetriedQueryOptions = { + connection: mysql.Connection; query: string; params?: any[]; retries?: number; @@ -12,12 +12,12 @@ export type RetiredMySQLQueryOptions = { /** * Retry a simple query - up to 2 attempts total. */ -export async function retriedQuery(options: RetiredMySQLQueryOptions) { - const { db, query, params = [], retries = 2 } = options; +export async function retriedQuery(options: RetriedQueryOptions) { + const { connection, query, params = [], retries = 2 } = options; for (let tries = retries; ; tries--) { try { logger.debug(`Executing query: ${query}`); - return db.query(query, params); + return connection.query(query, params); } catch (e) { if (tries == 1) { throw e; @@ -27,19 +27,21 @@ export async function retriedQuery(options: RetiredMySQLQueryOptions) { } } -export function createPool(config: types.ResolvedConnectionConfig) { +export function createPool(config: types.ResolvedConnectionConfig, options?: mysql.PoolOptions): mysql.Pool { const sslOptions = { ca: config.cacert, key: config.client_private_key, cert: config.client_certificate }; const hasSSLOptions = Object.values(sslOptions).some((v) => !!v); + // TODO confirm if default options are fine for Powersync use case return mysql.createPool({ host: config.hostname, user: config.username, password: config.password, database: config.database, - ssl: hasSSLOptions ? sslOptions : undefined + ssl: hasSSLOptions ? sslOptions : undefined, + ...(options || {}) }); } From c8751d7e86c75b8387bcdf7a4ac399517bdad854 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 9 Oct 2024 16:17:14 +0200 Subject: [PATCH 232/298] Added stricter type definition for checkpoints Moved BinlogStream Added type mapper for mysql -> sqlite types Removed schemaV2 since it is no longer needed --- .../src/common/mysql-to-sqlite.ts | 49 ++++++++++ .../{stream => }/MysqlBinLogStream.ts | 90 ++++++++++--------- .../src/api/PostgresRouteAPIAdapter.ts | 4 +- packages/service-core/src/api/RouteAPI.ts | 7 +- packages/service-core/src/api/schema.ts | 19 +--- .../service-core/src/storage/BucketStorage.ts | 16 ++-- .../storage/mongo/MongoSyncBucketStorage.ts | 8 +- .../service-core/src/util/protocol-types.ts | 2 +- packages/types/src/definitions.ts | 34 ------- 9 files changed, 121 insertions(+), 108 deletions(-) rename modules/module-mysql/src/replication/{stream => }/MysqlBinLogStream.ts (87%) diff --git a/modules/module-mysql/src/common/mysql-to-sqlite.ts b/modules/module-mysql/src/common/mysql-to-sqlite.ts index 3dd4c8ee1..120f7ab5f 100644 --- a/modules/module-mysql/src/common/mysql-to-sqlite.ts +++ b/modules/module-mysql/src/common/mysql-to-sqlite.ts @@ -1,4 +1,5 @@ import * as sync_rules from '@powersync/service-sync-rules'; +import { ExpressionType } from '@powersync/service-sync-rules'; export function toSQLiteRow(row: Record): sync_rules.SqliteRow { for (let key in row) { @@ -8,3 +9,51 @@ export function toSQLiteRow(row: Record): sync_rules.SqliteRow { } return sync_rules.toSyncRulesRow(row); } + +export function toExpressionTypeFromMySQLType(mysqlType: string | undefined): ExpressionType { + if (!mysqlType) { + return ExpressionType.TEXT; + } + + const upperCaseType = mysqlType.toUpperCase(); + // Handle type with parameters like VARCHAR(255), DECIMAL(10,2), etc. + const baseType = upperCaseType.split('(')[0]; + + switch (baseType) { + case 'BIT': + case 'BOOL': + case 'BOOLEAN': + case 'TINYINT': + case 'SMALLINT': + case 'MEDIUMINT': + case 'INT': + case 'INTEGER': + case 'BIGINT': + case 'UNSIGNED BIGINT': + return ExpressionType.INTEGER; + case 'BINARY': + case 'VARBINARY': + case 'TINYBLOB': + case 'MEDIUMBLOB': + case 'LONGBLOB': + case 'BLOB': + case 'GEOMETRY': + case 'POINT': + case 'LINESTRING': + case 'POLYGON': + case 'MULTIPOINT': + case 'MULTILINESTRING': + case 'MULTIPOLYGON': + case 'GEOMETRYCOLLECTION': + return ExpressionType.BLOB; + case 'FLOAT': + case 'DOUBLE': + case 'REAL': + return ExpressionType.REAL; + case 'JSON': + return ExpressionType.TEXT; + default: + // In addition to the normal text types, includes: DECIMAL, NUMERIC, DATE, TIME, DATETIME, TIMESTAMP, YEAR, ENUM, SET + return ExpressionType.TEXT; + } +} diff --git a/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts similarity index 87% rename from modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts rename to modules/module-mysql/src/replication/MysqlBinLogStream.ts index 1c5abbcca..9f070c64a 100644 --- a/modules/module-mysql/src/replication/stream/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -6,15 +6,14 @@ import { framework, storage } from '@powersync/service-core'; import mysql, { RowDataPacket } from 'mysql2/promise'; import ZongJi, { BinLogEvent } from '@vlasky/zongji'; -import * as common from '../../common/common-index.js'; -import { NormalizedMySQLConnectionConfig } from '../../types/types.js'; -import * as zongji_utils from '../zongji/zongji-utils.js'; +import * as common from '../common/common-index.js'; +import * as zongji_utils from './zongji/zongji-utils.js'; +import { MySQLConnectionManager } from './MySQLConnectionManager.js'; export interface BinLogStreamOptions { - pool: mysql.Pool; - connection_config: NormalizedMySQLConnectionConfig; + connections: MySQLConnectionManager; storage: storage.SyncRulesBucketStorage; - abort_signal: AbortSignal; + abortSignal: AbortSignal; } interface MysqlRelId { @@ -33,37 +32,40 @@ function getMysqlRelId(source: MysqlRelId): string { } export class MysqlBinLogStream { - sync_rules: sync_rules.SqlSyncRules; - group_id: number; + private readonly sync_rules: sync_rules.SqlSyncRules; + private readonly group_id: number; private readonly storage: storage.SyncRulesBucketStorage; - private abort_signal: AbortSignal; + private readonly connections: MySQLConnectionManager; + + private abortSignal: AbortSignal; - private pool: mysql.Pool; private relation_cache = new Map(); constructor(protected options: BinLogStreamOptions) { this.storage = options.storage; - this.sync_rules = options.storage.sync_rules; + this.connections = options.connections; + this.sync_rules = options.storage.getParsedSyncRules({ defaultSchema: this.defaultSchema }); this.group_id = options.storage.group_id; - this.pool = options.pool; - - this.abort_signal = options.abort_signal; + this.abortSignal = options.abortSignal; } get connectionTag() { - return this.options.connection_config.tag; + return this.connections.connectionTag; } get connectionId() { - // TODO this is currently hardcoded to 1 in most places - // return this.options.connection_config.id; - return 1; + // Default to 1 if not set + return this.connections.connectionId ? Number.parseInt(this.connections.connectionId) : 1; } get stopped() { - return this.abort_signal.aborted; + return this.abortSignal.aborted; + } + + get defaultSchema() { + return this.connections.databaseName; } async handleRelation(batch: storage.BucketStorageBatch, entity: storage.SourceEntityDescriptor, snapshot: boolean) { @@ -74,6 +76,7 @@ export class MysqlBinLogStream { entity_descriptor: entity, sync_rules: this.sync_rules }); + this.relation_cache.set(entity.objectId, result.table); // Drop conflicting tables. This includes for example renamed tables. await batch.drop(result.dropTables); @@ -91,11 +94,11 @@ export class MysqlBinLogStream { let gtid: common.ReplicatedGTID; // Start the snapshot inside a transaction. // We use a dedicated connection for this. - const connection = await this.pool.getConnection(); + const connection = await this.connections.getConnection(); try { await connection.query('BEGIN'); try { - gtid = await common.readMasterGtid(this.pool); + gtid = await common.readExecutedGtid(connection); await this.snapshotTable(batch, connection, result.table); await connection.query('COMMIT'); } catch (e) { @@ -103,7 +106,7 @@ export class MysqlBinLogStream { throw e; } } finally { - await connection.end(); + connection.release(); } const [table] = await batch.markSnapshotDone([result.table], gtid.comparable); return table; @@ -116,14 +119,16 @@ export class MysqlBinLogStream { batch: storage.BucketStorageBatch, tablePattern: sync_rules.TablePattern ): Promise { + const schema = tablePattern.schema; if (tablePattern.connectionTag != this.connectionTag) { return []; } let tableRows: any[]; const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; + const connection = await this.connections.getConnection(); if (tablePattern.isWildcard) { - const result = await this.pool.query( + const result = await connection.query( `SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?; @@ -132,7 +137,7 @@ WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?; ); tableRows = result[0]; } else { - const result = await this.pool.query( + const result = await connection.query( `SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; @@ -141,7 +146,7 @@ WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; ); tableRows = result[0]; } - let result: storage.SourceTable[] = []; + let tables: storage.SourceTable[] = []; for (let row of tableRows) { const name = row['TABLE_NAME'] as string; @@ -149,20 +154,20 @@ WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; continue; } - const rs = await this.pool.query( + const result = await connection.query( `SELECT 1 FROM information_schema.tables WHERE table_schema = ? AND table_name = ? AND table_type = 'BASE TABLE';`, [tablePattern.schema, tablePattern.name] ); - if (rs[0].length == 0) { + if (result[0].length == 0) { logger.info(`Skipping ${tablePattern.schema}.${name} - no table exists/is not a base table`); continue; } - const cresult = await common.getReplicationIdentityColumns({ - db: this.pool, + const replicationColumns = await common.getReplicationIdentityColumns({ + db: connection, schema: tablePattern.schema, table_name: tablePattern.name }); @@ -173,22 +178,22 @@ AND table_type = 'BASE TABLE';`, name, schema: tablePattern.schema, objectId: getMysqlRelId(tablePattern), - replicationColumns: cresult.columns + replicationColumns: replicationColumns.columns }, false ); - result.push(table); + tables.push(table); } - return result; + + connection.release(); + return tables; } /** * Checks if the initial sync has been completed yet. */ protected async checkInitialReplicated(): Promise { - await common.checkSourceConfiguration(this.pool); - const status = await this.storage.getStatus(); if (status.snapshot_done && status.checkpoint_lsn) { logger.info(`Initial replication already done. MySQL appears healthy`); @@ -205,13 +210,14 @@ AND table_type = 'BASE TABLE';`, */ async replicateInitial() { await this.storage.clear(); - const headGTID = await common.readMasterGtid(this.pool); + const connection = await this.connections.getConnection(); + const headGTID = await common.readExecutedGtid(connection); logger.info(`Using GTID:: '${headGTID}'`); - await this.pool.query('BEGIN'); + await connection.query('BEGIN'); try { logger.info(`Starting initial replication`); const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({}, async (batch) => { + await this.storage.startBatch({zeroLSN: , defaultSchema: this.defaultSchema}, async (batch) => { for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, tablePattern); for (let table of tables) { @@ -262,6 +268,10 @@ AND table_type = 'BASE TABLE';`, } async initReplication() { + const connection = await this.connections.getConnection(); + await common.checkSourceConfiguration(connection); + connection.release(); + const initialReplicationCompleted = await this.checkInitialReplicated(); const sourceTables = this.sync_rules.getSourceTables(); @@ -283,7 +293,7 @@ AND table_type = 'BASE TABLE';`, const { checkpoint_lsn } = await this.storage.getStatus(); const fromGTID = checkpoint_lsn ? common.ReplicatedGTID.fromSerialized(checkpoint_lsn) - : await common.readMasterGtid(this.pool); + : await common.readExecutedGtid(this.pool); const binLogPositionState = fromGTID.position; await this.storage.startBatch({}, async (batch) => { @@ -418,7 +428,7 @@ AND table_type = 'BASE TABLE';`, } }, 1); - zongji.on('binlog', (evt: any) => { + zongji.on('binlog', (evt: BinLogEvent) => { queue.push(evt); }); @@ -436,7 +446,7 @@ AND table_type = 'BASE TABLE';`, queue.kill(); reject(error); }); - this.abort_signal.addEventListener( + this.abortSignal.addEventListener( 'abort', async () => { zongji.stop(); diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index 4cc55d7d5..f3c7b9aef 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -204,7 +204,7 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { } async getReplicationLag(options: api.ReplicationLagOptions): Promise { - const { replication_identifier: slotName } = options; + const { bucketStorage: slotName } = options; const results = await pg_utils.retriedQuery(this.pool, { statement: `SELECT slot_name, @@ -229,7 +229,7 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`, return String(lsn); } - async getConnectionSchema(): Promise { + async getConnectionSchema(): Promise { // https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts const results = await pg_utils.retriedQuery( this.pool, diff --git a/packages/service-core/src/api/RouteAPI.ts b/packages/service-core/src/api/RouteAPI.ts index d9afd0b9d..e56c2d718 100644 --- a/packages/service-core/src/api/RouteAPI.ts +++ b/packages/service-core/src/api/RouteAPI.ts @@ -1,6 +1,6 @@ import { SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; import * as types from '@powersync/service-types'; -import { ParseSyncRulesOptions } from '../storage/BucketStorage.js'; +import { ParseSyncRulesOptions, SyncRulesBucketStorage } from '../storage/BucketStorage.js'; export interface PatternResult { schema: string; @@ -11,8 +11,7 @@ export interface PatternResult { } export interface ReplicationLagOptions { - replication_identifier: string; - last_checkpoint_identifier: string; + bucketStorage: SyncRulesBucketStorage; } /** @@ -62,7 +61,7 @@ export interface RouteAPI { * contains `pg_type` which we might need to deprecate and add another generic * type field - or just use this field as the connection specific type. */ - getConnectionSchema(): Promise; + getConnectionSchema(): Promise; /** * Executes a query and return the result from the data source. This is currently used in the diff --git a/packages/service-core/src/api/schema.ts b/packages/service-core/src/api/schema.ts index 0315697de..9412ba2fb 100644 --- a/packages/service-core/src/api/schema.ts +++ b/packages/service-core/src/api/schema.ts @@ -10,28 +10,13 @@ export async function getConnectionsSchema(api: api.RouteAPI): Promise ({ - ...schema, - tables: schema.tables.map((table) => ({ - ...table, - columns: table.columns.map((column) => ({ - ...column, - // Defaults back to the original type if not provided - pg_type: column.internal_type ?? column.type - })) - })) - })) + schemas: await api.getConnectionSchema() } ] }; diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 8af73ccbd..11df97948 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -98,20 +98,22 @@ export interface BucketStorageFactory { getPowerSyncInstanceId(): Promise; } -export interface WriteCheckpoint { - base: ActiveCheckpoint; - writeCheckpoint: bigint | null; -} - -export interface ActiveCheckpoint { +export interface Checkpoint { readonly checkpoint: util.OpId; readonly lsn: string | null; +} +export interface ActiveCheckpoint extends Checkpoint { hasSyncRules(): boolean; getBucketStorage(): Promise; } +export interface WriteCheckpoint { + base: ActiveCheckpoint; + writeCheckpoint: bigint | null; +} + export interface StorageMetrics { /** * Size of operations (bucket_data) @@ -207,7 +209,7 @@ export interface SyncRulesBucketStorage { callback: (batch: BucketStorageBatch) => Promise ): Promise; - getCheckpoint(): Promise<{ checkpoint: util.OpId }>; + getCheckpoint(): Promise; getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules; diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 3844f5b22..92cccf88d 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -7,6 +7,7 @@ import * as util from '../../util/util-index.js'; import { BucketDataBatchOptions, BucketStorageBatch, + Checkpoint, CompactOptions, DEFAULT_DOCUMENT_BATCH_LIMIT, DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES, @@ -55,15 +56,16 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { return this.parsedSyncRulesCache; } - async getCheckpoint() { + async getCheckpoint(): Promise { const doc = await this.db.sync_rules.findOne( { _id: this.group_id }, { - projection: { last_checkpoint: 1 } + projection: { last_checkpoint: 1, last_checkpoint_lsn: 1 } } ); return { - checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n) + checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n), + lsn: doc?.last_checkpoint_lsn ?? null }; } diff --git a/packages/service-core/src/util/protocol-types.ts b/packages/service-core/src/util/protocol-types.ts index 5c7e47064..f522d19fb 100644 --- a/packages/service-core/src/util/protocol-types.ts +++ b/packages/service-core/src/util/protocol-types.ts @@ -88,7 +88,7 @@ export type StreamingSyncLine = */ export type OpId = string; -export interface Checkpoint { +interface Checkpoint { last_op_id: OpId; write_checkpoint?: OpId; buckets: BucketChecksum[]; diff --git a/packages/types/src/definitions.ts b/packages/types/src/definitions.ts index 6d850b925..d387fb87e 100644 --- a/packages/types/src/definitions.ts +++ b/packages/types/src/definitions.ts @@ -144,29 +144,6 @@ export const DatabaseSchema = t.object({ }); export type DatabaseSchema = t.Encoded; -export const DatabaseSchemaV2 = t.object({ - name: t.string, - tables: t.array( - t.object({ - name: t.string, - columns: t.array( - t.object({ - name: t.string, - /** - * Full type name, e.g. "character varying(255)[]" - */ - type: t.string, - /** - * Internal type, e.g. "varchar[]". - */ - internal_type: t.string.optional() - }) - ) - }) - ) -}); -export type DatabaseSchemaV2 = t.Encoded; - export const InstanceSchema = t.object({ connections: t.array( t.object({ @@ -177,14 +154,3 @@ export const InstanceSchema = t.object({ ) }); export type InstanceSchema = t.Encoded; - -export const InstanceSchemaV2 = t.object({ - connections: t.array( - t.object({ - id: t.string.optional(), - tag: t.string, - schemas: t.array(DatabaseSchemaV2) - }) - ) -}); -export type InstanceSchemaV2 = t.Encoded; From 9e7b41d820aa0fb2fff5d3244bc0ee8c44ccdc6c Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 10 Oct 2024 17:30:54 +0200 Subject: [PATCH 233/298] Fixed diagnostics route merge conflict Renamed binlog replication job --- ...plicatorJob.ts => BinLogReplicationJob.ts} | 36 ++++++++----------- packages/service-core/src/api/diagnostics.ts | 4 +-- .../service-core/test/src/compacting.test.ts | 13 +++---- 3 files changed, 20 insertions(+), 33 deletions(-) rename modules/module-mysql/src/replication/{BinLogReplicatorJob.ts => BinLogReplicationJob.ts} (71%) diff --git a/modules/module-mysql/src/replication/BinLogReplicatorJob.ts b/modules/module-mysql/src/replication/BinLogReplicationJob.ts similarity index 71% rename from modules/module-mysql/src/replication/BinLogReplicatorJob.ts rename to modules/module-mysql/src/replication/BinLogReplicationJob.ts index 890c1abc5..2350b727f 100644 --- a/modules/module-mysql/src/replication/BinLogReplicatorJob.ts +++ b/modules/module-mysql/src/replication/BinLogReplicationJob.ts @@ -1,34 +1,24 @@ import { container } from '@powersync/lib-services-framework'; -import * as types from '../types/types.js'; - -import * as mysql_utils from '../utils/mysql_utils.js'; - import { replication } from '@powersync/service-core'; -import { MysqlBinLogStream } from './stream/MysqlBinLogStream.js'; +import { MysqlBinLogStream } from './MysqlBinLogStream.js'; +import { MySQLConnectionManagerFactory } from './MySQLConnectionManagerFactory.js'; export interface BinLogReplicationJobOptions extends replication.AbstractReplicationJobOptions { - /** - * Connection config required to create a MySQL Pool - */ - connectionConfig: types.ResolvedConnectionConfig; + connectionFactory: MySQLConnectionManagerFactory; } -export class BinLogReplicatorJob extends replication.AbstractReplicationJob { - protected connectionConfig: types.ResolvedConnectionConfig; +export class BinLogReplicationJob extends replication.AbstractReplicationJob { + private connectionFactory: MySQLConnectionManagerFactory; constructor(options: BinLogReplicationJobOptions) { super(options); - this.connectionConfig = options.connectionConfig; + this.connectionFactory = options.connectionFactory; } get slot_name() { return this.options.storage.slot_name; } - async cleanUp(): Promise { - // This MySQL module does not create anything which requires cleanup on the MySQL server. - } - async keepAlive() {} async replicate() { @@ -67,17 +57,19 @@ export class BinLogReplicatorJob extends replication.AbstractReplicationJob { // New connections on every iteration (every error with retry), // otherwise we risk repeating errors related to the connection, // such as caused by cached PG schemas. - const pool = mysql_utils.createPool(this.connectionConfig); + const connectionManager = this.connectionFactory.create({ + // Pool connections are only used intermittently. + idleTimeout: 30_000 + }); try { await this.rateLimiter?.waitUntilAllowed({ signal: this.abortController.signal }); if (this.isStopped) { return; } const stream = new MysqlBinLogStream({ - abort_signal: this.abortController.signal, - connection_config: this.connectionConfig, - pool, - storage: this.options.storage + abortSignal: this.abortController.signal, + storage: this.options.storage, + connections: connectionManager }); await stream.replicate(); } catch (e) { @@ -99,7 +91,7 @@ export class BinLogReplicatorJob extends replication.AbstractReplicationJob { this.rateLimiter?.reportError(e); } } finally { - await pool.end(); + await connectionManager.end(); } } } diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 22d9530ef..3d562e5fd 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -78,10 +78,8 @@ export async function getSyncRulesStatus( if (systemStorage) { try { - const lastCheckpoint = await systemStorage.getCheckpoint(); replication_lag_bytes = await apiHandler.getReplicationLag({ - replication_identifier: systemStorage.slot_name, - last_checkpoint_identifier: lastCheckpoint.checkpoint + bucketStorage: systemStorage }); } catch (e) { // Ignore diff --git a/packages/service-core/test/src/compacting.test.ts b/packages/service-core/test/src/compacting.test.ts index 4cf3daed3..b5caf72a3 100644 --- a/packages/service-core/test/src/compacting.test.ts +++ b/packages/service-core/test/src/compacting.test.ts @@ -1,12 +1,9 @@ import { SaveOperationTag } from '@/storage/BucketStorage.js'; import { MongoCompactOptions } from '@/storage/mongo/MongoCompactor.js'; -import { SqlSyncRules } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { validateCompactedBucket } from './bucket_validation.js'; import { oneFromAsync } from './stream_utils.js'; -import { BATCH_OPTIONS, makeTestTable, MONGO_STORAGE_FACTORY, rid, testRules, ZERO_LSN } from './util.js'; -import { ParseSyncRulesOptions, PersistedSyncRulesContent, StartBatchOptions } from '@/storage/BucketStorage.js'; -import { getUuidReplicaIdentityBson } from '@/util/util-index.js'; +import { BATCH_OPTIONS, makeTestTable, MONGO_STORAGE_FACTORY, rid, testRules } from './util.js'; const TEST_TABLE = makeTestTable('test', ['id']); @@ -234,7 +231,7 @@ bucket_definitions: const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't1' }, @@ -243,7 +240,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'insert', + tag: SaveOperationTag.INSERT, after: { id: 't2' }, @@ -252,7 +249,7 @@ bucket_definitions: await batch.save({ sourceTable: TEST_TABLE, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 't1' }, @@ -266,7 +263,7 @@ bucket_definitions: const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => { await batch.save({ sourceTable: TEST_TABLE, - tag: 'delete', + tag: SaveOperationTag.DELETE, before: { id: 't2' }, From 12191afca8e8459ee386fdc67271459ba16ee7e3 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 10 Oct 2024 17:32:23 +0200 Subject: [PATCH 234/298] Add MySQLConnection management Updated config for the Zongji binlog listener --- modules/module-mysql/package.json | 1 - .../src/replication/MySQLConnectionManager.ts | 65 ++++++++++++++----- .../MySQLConnectionManagerFactory.ts | 28 ++++++++ .../src/replication/zongji/zongji-utils.ts | 2 +- .../src/replication/zongji/zongji.d.ts | 2 +- modules/module-mysql/tsconfig.json | 6 -- 6 files changed, 80 insertions(+), 24 deletions(-) create mode 100644 modules/module-mysql/src/replication/MySQLConnectionManagerFactory.ts diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 8db50999b..4c4f42b07 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -25,7 +25,6 @@ "dependencies": { "@powersync/lib-services-framework": "workspace:*", "@powersync/service-core": "workspace:*", - "@powersync/service-jsonbig": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", "@powersync/mysql-zongji": "0.0.0-dev-20241008105633", diff --git a/modules/module-mysql/src/replication/MySQLConnectionManager.ts b/modules/module-mysql/src/replication/MySQLConnectionManager.ts index 8f42ccf6c..d673d47af 100644 --- a/modules/module-mysql/src/replication/MySQLConnectionManager.ts +++ b/modules/module-mysql/src/replication/MySQLConnectionManager.ts @@ -1,22 +1,28 @@ import { ResolvedConnectionConfig } from '../types/types.js'; -import mysql from 'mysql2/promise'; +import mysqlPromise from 'mysql2/promise'; +import mysql, { RowDataPacket } from 'mysql2'; import * as mysql_utils from '../utils/mysql_utils.js'; -import ZongJi from '@vlasky/zongji'; +import ZongJi from '@powersync/mysql-zongji'; export class MySQLConnectionManager { /** - * Do not use this for any transactions. + * Pool that can create streamable connections */ - public readonly pool: mysql.Pool; + private readonly pool: mysql.Pool; + /** + * Pool that can create promise-based connections + */ + private readonly promisePool: mysqlPromise.Pool; private binlogListeners: ZongJi[] = []; constructor( public options: ResolvedConnectionConfig, - public poolOptions: mysql.PoolOptions + public poolOptions: mysqlPromise.PoolOptions ) { // The pool is lazy - no connections are opened until a query is performed. this.pool = mysql_utils.createPool(options, poolOptions); + this.promisePool = this.pool.promise(); } public get connectionTag() { @@ -31,10 +37,6 @@ export class MySQLConnectionManager { return this.options.database; } - public get serverId() { - return this.options.; - } - /** * Create a new replication listener */ @@ -51,10 +53,37 @@ export class MySQLConnectionManager { } /** - * Get a connection from the pool. It should be released back to the pool when done. + * Run a query using a connection from the pool + * A promise with the result is returned + * @param query + * @param params */ - async getConnection(): Promise { - return this.pool.getConnection(); + async query(query: string, params?: any[]) { + return this.promisePool.query(query, params); + } + + /** + * Get a streamable connection from this manager's pool + * The connection should be released when it is no longer needed + */ + async getStreamingConnection(): Promise { + return new Promise((resolve, reject) => { + this.pool.getConnection((err, connection) => { + if (err) { + reject(err); + } else { + resolve(connection); + } + }); + }); + } + + /** + * Get a promise connection from this manager's pool + * The connection should be released when it is no longer needed + */ + async getConnection(): Promise { + return this.promisePool.getConnection(); } async end(): Promise { @@ -62,14 +91,20 @@ export class MySQLConnectionManager { listener.stop(); } - await this.pool.end(); + await new Promise((resolve, reject) => { + this.pool.end((err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); } destroy() { for (const listener of this.binlogListeners) { listener.stop(); } - - this.pool.destroy(); } } diff --git a/modules/module-mysql/src/replication/MySQLConnectionManagerFactory.ts b/modules/module-mysql/src/replication/MySQLConnectionManagerFactory.ts new file mode 100644 index 000000000..ea87f60ec --- /dev/null +++ b/modules/module-mysql/src/replication/MySQLConnectionManagerFactory.ts @@ -0,0 +1,28 @@ +import { logger } from '@powersync/lib-services-framework'; +import mysql from 'mysql2/promise'; +import { MySQLConnectionManager } from './MySQLConnectionManager.js'; +import { ResolvedConnectionConfig } from '../types/types.js'; + +export class MySQLConnectionManagerFactory { + private readonly connectionManagers: MySQLConnectionManager[]; + private readonly connectionConfig: ResolvedConnectionConfig; + + constructor(connectionConfig: ResolvedConnectionConfig) { + this.connectionConfig = connectionConfig; + this.connectionManagers = []; + } + + create(poolOptions: mysql.PoolOptions) { + const manager = new MySQLConnectionManager(this.connectionConfig, poolOptions); + this.connectionManagers.push(manager); + return manager; + } + + async shutdown() { + logger.info('Shutting down MySQL connection Managers...'); + for (const manager of this.connectionManagers) { + await manager.end(); + } + logger.info('MySQL connection Managers shutdown completed.'); + } +} diff --git a/modules/module-mysql/src/replication/zongji/zongji-utils.ts b/modules/module-mysql/src/replication/zongji/zongji-utils.ts index 1e4126f96..36122b636 100644 --- a/modules/module-mysql/src/replication/zongji/zongji-utils.ts +++ b/modules/module-mysql/src/replication/zongji/zongji-utils.ts @@ -5,7 +5,7 @@ import { BinLogRotationEvent, BinLogUpdateEvent, BinLogXidEvent -} from '@vlasky/zongji'; +} from '@powersync/mysql-zongji'; export function eventIsGTIDLog(event: BinLogEvent): event is BinLogGTIDLogEvent { return event.getEventName() == 'gtidlog'; diff --git a/modules/module-mysql/src/replication/zongji/zongji.d.ts b/modules/module-mysql/src/replication/zongji/zongji.d.ts index d9ff46691..e14ff3abe 100644 --- a/modules/module-mysql/src/replication/zongji/zongji.d.ts +++ b/modules/module-mysql/src/replication/zongji/zongji.d.ts @@ -1,4 +1,4 @@ -declare module '@vlasky/zongji' { +declare module '@powersync/mysql-zongji' { export type ZongjiOptions = { host: string; user: string; diff --git a/modules/module-mysql/tsconfig.json b/modules/module-mysql/tsconfig.json index 0b03246f5..a9d72169d 100644 --- a/modules/module-mysql/tsconfig.json +++ b/modules/module-mysql/tsconfig.json @@ -13,12 +13,6 @@ { "path": "../../packages/types" }, - { - "path": "../../packages/jsonbig" - }, - { - "path": "../../packages/jpgwire" - }, { "path": "../../packages/sync-rules" }, From cf002beda2b89606a37087d3d0ba6afc388dc84e Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 10 Oct 2024 17:33:15 +0200 Subject: [PATCH 235/298] Made streamable mysql connections available --- .../src/api/MySQLRouteAPIAdapter.ts | 72 +++++++++++++------ .../module-mysql/src/common/ReplicatedGTID.ts | 19 ++--- .../src/common/check-source-configuration.ts | 4 +- .../src/common/get-replication-columns.ts | 4 +- .../src/common/read-executed-gtid.ts | 6 +- modules/module-mysql/src/utils/mysql_utils.ts | 11 ++- 6 files changed, 72 insertions(+), 44 deletions(-) diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 1f55f19cb..44f115e8b 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -1,11 +1,12 @@ -import { api, storage } from '@powersync/service-core'; +import { api, ParseSyncRulesOptions, storage } from '@powersync/service-core'; import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; import * as common from '../common/common-index.js'; -import * as types from '../types/types.js'; import * as mysql_utils from '../utils/mysql_utils.js'; +import * as types from '../types/types.js'; +import { toExpressionTypeFromMySQLType } from '../common/common-index.js'; type SchemaResult = { schema_name: string; @@ -17,7 +18,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { protected pool: mysql.Pool; constructor(protected config: types.ResolvedConnectionConfig) { - this.pool = mysql_utils.createPool(config); + this.pool = mysql_utils.createPool(config).promise(); } async shutdown(): Promise { @@ -28,6 +29,13 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { return this.config; } + getParseSyncRulesOptions(): ParseSyncRulesOptions { + return { + // In MySQL Schema and Database are the same thing. There is no default database + defaultSchema: this.config.database + }; + } + async getConnectionStatus(): Promise { const base = { id: this.config.id, @@ -44,8 +52,9 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { errors: [{ level: 'fatal', message: `${e.code} - message: ${e.message}` }] }; } + const connection = await this.pool.getConnection(); try { - const errors = await common.checkSourceConfiguration(this.pool); + const errors = await common.checkSourceConfiguration(connection); if (errors.length) { return { ...base, @@ -59,6 +68,8 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { connected: true, errors: [{ level: 'fatal', message: e.message }] }; + } finally { + connection.release(); } return { ...base, @@ -68,7 +79,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async executeQuery(query: string, params: any[]): Promise { - if (!this.config.debug_enabled) { + if (!this.config.debug_api) { return service_types.internal_routes.ExecuteSqlResponse.encode({ results: { columns: [], @@ -171,8 +182,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { if (results.length == 0) { // Table not found - const details = await this.getDebugTableInfo(tablePattern, tablePattern.name, sqlSyncRules); - patternResult.table = details; + patternResult.table = await this.getDebugTableInfo(tablePattern, tablePattern.name, sqlSyncRules); } else { const row = results[0]; patternResult.table = await this.getDebugTableInfo(tablePattern, row.table_name, sqlSyncRules); @@ -192,14 +202,18 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { let idColumnsResult: common.ReplicationIdentityColumnsResult | null = null; let idColumnsError: service_types.ReplicationError | null = null; + let connection: mysql.PoolConnection | null = null; try { + connection = await this.pool.getConnection(); idColumnsResult = await common.getReplicationIdentityColumns({ - db: this.pool, + db: connection, schema, table_name: tableName }); } catch (ex) { idColumnsError = { level: 'fatal', message: ex.message }; + } finally { + connection?.release(); } const idColumns = idColumnsResult?.columns ?? []; @@ -236,12 +250,17 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async getReplicationLag(options: api.ReplicationLagOptions): Promise { - const { last_checkpoint_identifier } = options; + const { bucketStorage } = options; + const lastCheckpoint = await bucketStorage.getCheckpoint(); - const current = common.ReplicatedGTID.fromSerialized(last_checkpoint_identifier); - const head = await common.readMasterGtid(this.pool); + const current = lastCheckpoint.lsn + ? common.ReplicatedGTID.fromSerialized(lastCheckpoint.lsn) + : common.ReplicatedGTID.ZERO; - const lag = await current.distanceTo(this.pool, head); + const connection = await this.pool.getConnection(); + const head = await common.readExecutedGtid(connection); + const lag = await current.distanceTo(connection, head); + connection.release(); if (lag == null) { throw new Error(`Could not determine replication lag`); } @@ -250,11 +269,13 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { } async getReplicationHead(): Promise { - const result = await common.readMasterGtid(this.pool); + const connection = await this.pool.getConnection(); + const result = await common.readExecutedGtid(connection); + connection.release(); return result.comparable; } - async getConnectionSchema(): Promise { + async getConnectionSchema(): Promise { const [results] = await this.retriedQuery({ query: ` SELECT @@ -298,7 +319,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { */ return Object.values( - (results as SchemaResult[]).reduce((hash: Record, result) => { + (results as SchemaResult[]).reduce((hash: Record, result) => { const schema = hash[result.schema_name] || (hash[result.schema_name] = { @@ -310,7 +331,10 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { name: result.table_name, columns: result.columns.map((column) => ({ name: column.column_name, - type: column.data_type + type: column.data_type, + sqlite_type: toExpressionTypeFromMySQLType(column.data_type).typeFlags, + internal_type: column.data_type, + pg_type: column.data_type })) }); @@ -319,11 +343,15 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { ); } - protected retriedQuery(options: { query: string; params?: any[] }) { - return mysql_utils.retriedQuery({ - db: this.pool, - query: options.query, - params: options.params - }); + protected async retriedQuery(options: { query: string; params?: any[] }) { + const connection = await this.pool.getConnection(); + + return mysql_utils + .retriedQuery({ + connection: connection, + query: options.query, + params: options.params + }) + .finally(() => connection.release()); } } diff --git a/modules/module-mysql/src/common/ReplicatedGTID.ts b/modules/module-mysql/src/common/ReplicatedGTID.ts index f87456ee1..7d28b0d3a 100644 --- a/modules/module-mysql/src/common/ReplicatedGTID.ts +++ b/modules/module-mysql/src/common/ReplicatedGTID.ts @@ -31,7 +31,11 @@ export type BinLogGTIDEvent = { * and position where this GTID could be located. */ export class ReplicatedGTID { - static deserialize(comparable: string): ReplicatedGTIDSpecification { + static fromSerialized(comparable: string): ReplicatedGTID { + return new ReplicatedGTID(ReplicatedGTID.deserialize(comparable)); + } + + private static deserialize(comparable: string): ReplicatedGTIDSpecification { const components = comparable.split('|'); if (components.length < 3) { throw new Error(`Invalid serialized GTID: ${comparable}`); @@ -46,10 +50,6 @@ export class ReplicatedGTID { }; } - static fromSerialized(comparable: string): ReplicatedGTID { - return new ReplicatedGTID(ReplicatedGTID.deserialize(comparable)); - } - static fromBinLogEvent(event: BinLogGTIDEvent) { const { raw_gtid, position } = event; const stringGTID = `${uuid.stringify(raw_gtid.server_id)}:${raw_gtid.transaction_range}`; @@ -80,12 +80,15 @@ export class ReplicatedGTID { return this.options.raw_gtid; } + get serverId() { + return this.options.raw_gtid.split(':')[0]; + } + /** * Transforms a GTID into a comparable string format, ensuring lexicographical * order aligns with the GTID's relative age. This assumes that all GTIDs * have the same server ID. * - * @param gtid - The GTID string in the format `server_id:transaction_ranges` * @returns A comparable string in the format * `padded_end_transaction|raw_gtid|binlog_filename|binlog_position` */ @@ -111,9 +114,9 @@ export class ReplicatedGTID { /** * Calculates the distance in bytes from this GTID to the provided argument. */ - async distanceTo(db: mysql.Connection, to: ReplicatedGTID): Promise { + async distanceTo(connection: mysql.Connection, to: ReplicatedGTID): Promise { const [logFiles] = await mysql_utils.retriedQuery({ - connection: db, + connection, query: `SHOW BINARY LOGS;` }); diff --git a/modules/module-mysql/src/common/check-source-configuration.ts b/modules/module-mysql/src/common/check-source-configuration.ts index 03e10c063..bcc7a4fa3 100644 --- a/modules/module-mysql/src/common/check-source-configuration.ts +++ b/modules/module-mysql/src/common/check-source-configuration.ts @@ -1,7 +1,7 @@ -import mysql from 'mysql2/promise'; +import mysqlPromise from 'mysql2/promise'; import * as mysql_utils from '../utils/mysql_utils.js'; -export async function checkSourceConfiguration(connection: mysql.Connection) { +export async function checkSourceConfiguration(connection: mysqlPromise.Connection) { const errors: string[] = []; const [[result]] = await mysql_utils.retriedQuery({ connection, diff --git a/modules/module-mysql/src/common/get-replication-columns.ts b/modules/module-mysql/src/common/get-replication-columns.ts index a6d8b0abe..2242ed6ef 100644 --- a/modules/module-mysql/src/common/get-replication-columns.ts +++ b/modules/module-mysql/src/common/get-replication-columns.ts @@ -1,9 +1,9 @@ import { storage } from '@powersync/service-core'; -import mysql from 'mysql2/promise'; +import mysqlPromise from 'mysql2/promise'; import * as mysql_utils from '../utils/mysql_utils.js'; export type GetReplicationColumnsOptions = { - db: mysql.Connection; + db: mysqlPromise.Connection; schema: string; table_name: string; }; diff --git a/modules/module-mysql/src/common/read-executed-gtid.ts b/modules/module-mysql/src/common/read-executed-gtid.ts index 5452129ae..2b52ffe2c 100644 --- a/modules/module-mysql/src/common/read-executed-gtid.ts +++ b/modules/module-mysql/src/common/read-executed-gtid.ts @@ -1,4 +1,4 @@ -import mysql from 'mysql2/promise'; +import mysqlPromise from 'mysql2/promise'; import * as mysql_utils from '../utils/mysql_utils.js'; import { gte } from 'semver'; @@ -7,14 +7,14 @@ import { ReplicatedGTID } from './ReplicatedGTID.js'; /** * Gets the current master HEAD GTID */ -export async function readExecutedGtid(connection: mysql.Connection): Promise { +export async function readExecutedGtid(connection: mysqlPromise.Connection): Promise { const [[versionResult]] = await mysql_utils.retriedQuery({ connection, query: `SELECT VERSION() as version` }); const version = versionResult.version as string; - let binlogStatus: mysql.RowDataPacket; + let binlogStatus: mysqlPromise.RowDataPacket; if (gte(version, '8.4.0')) { // Get the BinLog status const [[binLogResult]] = await mysql_utils.retriedQuery({ diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 230e73155..349a2c877 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -1,9 +1,10 @@ import { logger } from '@powersync/lib-services-framework'; -import mysql from 'mysql2/promise'; +import mysql from 'mysql2'; +import mysqlPromise from 'mysql2/promise'; import * as types from '../types/types.js'; export type RetriedQueryOptions = { - connection: mysql.Connection; + connection: mysqlPromise.Connection; query: string; params?: any[]; retries?: number; @@ -17,7 +18,7 @@ export async function retriedQuery(options: RetriedQueryOptions) { for (let tries = retries; ; tries--) { try { logger.debug(`Executing query: ${query}`); - return connection.query(query, params); + return connection.query(query, params); } catch (e) { if (tries == 1) { throw e; @@ -44,7 +45,3 @@ export function createPool(config: types.ResolvedConnectionConfig, options?: mys ...(options || {}) }); } - -export function createConnection(config: types.ResolvedConnectionConfig) { - return mysql.createConnection(config); -} From 9941e207294833e344a1c18af378bb392fbf67b7 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 10 Oct 2024 17:34:39 +0200 Subject: [PATCH 236/298] Updated BinlogStream to use appropriate connections for snapshot streaming. Lots of cleanup and consolidation --- .../module-mysql/src/module/MySQLModule.ts | 72 +-- .../src/replication/BinLogReplicator.ts | 33 +- .../src/replication/MysqlBinLogStream.ts | 435 +++++++++--------- 3 files changed, 257 insertions(+), 283 deletions(-) diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts index 20255187a..8283e6c5c 100644 --- a/modules/module-mysql/src/module/MySQLModule.ts +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -1,9 +1,10 @@ -import { api, replication, system, utils } from '@powersync/service-core'; +import { api, ConfigurationFileSyncRulesProvider, replication, system, TearDownOptions } from '@powersync/service-core'; import { MySQLRouteAPIAdapter } from '../api/MySQLRouteAPIAdapter.js'; import { BinLogReplicator } from '../replication/BinLogReplicator.js'; import { MySQLErrorRateLimiter } from '../replication/MySQLErrorRateLimiter.js'; import * as types from '../types/types.js'; +import { MySQLConnectionManagerFactory } from '../replication/MySQLConnectionManagerFactory.js'; export class MySQLModule extends replication.ReplicationModule { constructor() { @@ -16,33 +17,23 @@ export class MySQLModule extends replication.ReplicationModule { await super.initialize(context); - - // TODO move this to the binlog consumer - // jpgwire.setMetricsRecorder({ - // addBytesRead(bytes) { - // context.metrics.data_replicated_bytes.add(bytes); - // } - // }); } - protected createRouteAPIAdapter(config: types.MySQLConnectionConfig): api.RouteAPI { - return new MySQLRouteAPIAdapter(this.resolveConfig(config)); + protected createRouteAPIAdapter(): api.RouteAPI { + return new MySQLRouteAPIAdapter(this.resolveConfig(this.decodedConfig!)); } - protected createReplicator( - config: types.MySQLConnectionConfig, - context: system.ServiceContext - ): replication.AbstractReplicator { - const resolvedConfig = this.resolveConfig(config); + protected createReplicator(context: system.ServiceContext): replication.AbstractReplicator { + const normalisedConfig = this.resolveConfig(this.decodedConfig!); + const syncRuleProvider = new ConfigurationFileSyncRulesProvider(context.configuration.sync_rules); + const connectionFactory = new MySQLConnectionManagerFactory(normalisedConfig); + return new BinLogReplicator({ - connectionConfig: resolvedConfig, - id: this.getDefaultId(resolvedConfig.database), - rateLimiter: new MySQLErrorRateLimiter(), - syncRuleProvider: { - // TODO should maybe improve this - get: () => utils.loadSyncRules(context.configuration) - }, - storageFactory: context.storage + id: this.getDefaultId(normalisedConfig.database), + syncRuleProvider: syncRuleProvider, + storageEngine: context.storageEngine, + connectionFactory: connectionFactory, + rateLimiter: new MySQLErrorRateLimiter() }); } @@ -56,38 +47,7 @@ export class MySQLModule extends replication.ReplicationModule { - // TODO this needs the service context to operate. - // Should this keep a refference? - // const mongoDB = storage.createPowerSyncMongo(context.configuration.storage); - // try { - // // TODO this should not be necessary since the service context - // // has already been initialized. - // // However we need a direct mongo connection for this. - // // Maybe we can add termination methods to the storage. - // // TODO improve this when other storage methods or connections are implemented - // logger.info(`Waiting for auth`); - // await db.mongo.waitForAuth(mongoDB.db); - // logger.info(`Terminating replication slots`); - // const connections = (context.configuration.connections ?? []) - // .filter((c) => c.type == 'postgresql') - // .map((c) => types.PostgresConnectionConfig.decode(c as any)); - // for (const connection of connections) { - // await terminateReplicators(context.storage, this.resolveConfig(connection)); - // } - // const database = mongoDB.db; - // logger.info(`Dropping database ${database.namespace}`); - // await database.dropDatabase(); - // logger.info(`Done`); - // await mongoDB.client.close(); - // // If there was an error connecting to postgress, the process may stay open indefinitely. - // // This forces an exit. - // // We do not consider those errors a teardown failure. - // process.exit(0); - // } catch (e) { - // logger.error(`Teardown failure`, e); - // await mongoDB.client.close(); - // process.exit(1); - // } + async teardown(options: TearDownOptions): Promise { + // No specific teardown required for MySQL } } diff --git a/modules/module-mysql/src/replication/BinLogReplicator.ts b/modules/module-mysql/src/replication/BinLogReplicator.ts index 2f0ebb663..ca07f4a0a 100644 --- a/modules/module-mysql/src/replication/BinLogReplicator.ts +++ b/modules/module-mysql/src/replication/BinLogReplicator.ts @@ -1,28 +1,35 @@ -import { replication } from '@powersync/service-core'; -import * as types from '../types/types.js'; -import { BinLogReplicatorJob } from './BinLogReplicatorJob.js'; +import { replication, storage } from '@powersync/service-core'; +import { BinLogReplicationJob } from './BinLogReplicationJob.js'; +import { MySQLConnectionManagerFactory } from './MySQLConnectionManagerFactory.js'; export interface BinLogReplicatorOptions extends replication.AbstractReplicatorOptions { - /** - * Connection config required to a MySQL Pool - */ - connectionConfig: types.ResolvedConnectionConfig; + connectionFactory: MySQLConnectionManagerFactory; } -export class BinLogReplicator extends replication.AbstractReplicator { - protected connectionConfig: types.ResolvedConnectionConfig; +export class BinLogReplicator extends replication.AbstractReplicator { + private readonly connectionFactory: MySQLConnectionManagerFactory; constructor(options: BinLogReplicatorOptions) { super(options); - this.connectionConfig = options.connectionConfig; + this.connectionFactory = options.connectionFactory; } - createJob(options: replication.CreateJobOptions): BinLogReplicatorJob { - return new BinLogReplicatorJob({ + createJob(options: replication.CreateJobOptions): BinLogReplicationJob { + return new BinLogReplicationJob({ id: this.createJobId(options.storage.group_id), storage: options.storage, lock: options.lock, - connectionConfig: this.connectionConfig + connectionFactory: this.connectionFactory, + rateLimiter: this.rateLimiter }); } + + async cleanUp(syncRulesStorage: storage.SyncRulesBucketStorage): Promise { + // The MySQL module does not create anything which requires cleanup on the MySQL server. + } + + async stop(): Promise { + await super.stop(); + await this.connectionFactory.shutdown(); + } } diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/MysqlBinLogStream.ts index 9f070c64a..100901023 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/MysqlBinLogStream.ts @@ -2,13 +2,14 @@ import { logger } from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import async from 'async'; -import { framework, storage } from '@powersync/service-core'; -import mysql, { RowDataPacket } from 'mysql2/promise'; +import { framework, getUuidReplicaIdentityBson, storage } from '@powersync/service-core'; +import mysql from 'mysql2'; -import ZongJi, { BinLogEvent } from '@vlasky/zongji'; +import { BinLogEvent } from '@powersync/mysql-zongji'; import * as common from '../common/common-index.js'; import * as zongji_utils from './zongji/zongji-utils.js'; import { MySQLConnectionManager } from './MySQLConnectionManager.js'; +import { ReplicatedGTID } from '../common/common-index.js'; export interface BinLogStreamOptions { connections: MySQLConnectionManager; @@ -94,15 +95,16 @@ export class MysqlBinLogStream { let gtid: common.ReplicatedGTID; // Start the snapshot inside a transaction. // We use a dedicated connection for this. - const connection = await this.connections.getConnection(); + const connection = await this.connections.getStreamingConnection(); + const promiseConnection = connection.connection.promise(); try { - await connection.query('BEGIN'); + await promiseConnection.query('BEGIN'); try { - gtid = await common.readExecutedGtid(connection); - await this.snapshotTable(batch, connection, result.table); - await connection.query('COMMIT'); + gtid = await common.readExecutedGtid(promiseConnection); + await this.snapshotTable(connection.connection, batch, result.table); + await promiseConnection.query('COMMIT'); } catch (e) { - await connection.query('ROLLBACK'); + await promiseConnection.query('ROLLBACK'); throw e; } } finally { @@ -119,16 +121,14 @@ export class MysqlBinLogStream { batch: storage.BucketStorageBatch, tablePattern: sync_rules.TablePattern ): Promise { - const schema = tablePattern.schema; if (tablePattern.connectionTag != this.connectionTag) { return []; } let tableRows: any[]; const prefix = tablePattern.isWildcard ? tablePattern.tablePrefix : undefined; - const connection = await this.connections.getConnection(); if (tablePattern.isWildcard) { - const result = await connection.query( + const result = await this.connections.query( `SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?; @@ -137,7 +137,7 @@ WHERE TABLE_SCHEMA = ? AND TABLE_NAME LIKE ?; ); tableRows = result[0]; } else { - const result = await connection.query( + const result = await this.connections.query( `SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; @@ -154,7 +154,7 @@ WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?; continue; } - const result = await connection.query( + const result = await this.connections.query( `SELECT 1 FROM information_schema.tables WHERE table_schema = ? AND table_name = ? @@ -166,11 +166,13 @@ AND table_type = 'BASE TABLE';`, continue; } + const connection = await this.connections.getConnection(); const replicationColumns = await common.getReplicationIdentityColumns({ db: connection, schema: tablePattern.schema, table_name: tablePattern.name }); + connection.release(); const table = await this.handleRelation( batch, @@ -185,8 +187,6 @@ AND table_type = 'BASE TABLE';`, tables.push(table); } - - connection.release(); return tables; } @@ -203,56 +203,81 @@ AND table_type = 'BASE TABLE';`, } /** - * Snapshots initial tables + * Does the initial replication of the database tables. * * If (partial) replication was done before on this slot, this clears the state * and starts again from scratch. */ - async replicateInitial() { + async startInitialReplication() { await this.storage.clear(); - const connection = await this.connections.getConnection(); - const headGTID = await common.readExecutedGtid(connection); + // Replication will be performed in a single transaction on this connection + const connection = await this.connections.getStreamingConnection(); + const promiseConnection = connection.connection.promise(); + const headGTID = await common.readExecutedGtid(promiseConnection); logger.info(`Using GTID:: '${headGTID}'`); - await connection.query('BEGIN'); try { logger.info(`Starting initial replication`); + await promiseConnection.query('START TRANSACTION'); + await promiseConnection.query('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ACCESS_MODE READ ONLY'); const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({zeroLSN: , defaultSchema: this.defaultSchema}, async (batch) => { - for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, this.pool, table); - await batch.markSnapshotDone([table], headGTID.comparable); - await framework.container.probes.touch(); + await this.storage.startBatch( + { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, + async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, tablePattern); + for (let table of tables) { + await this.snapshotTable(connection.connection, batch, table); + await batch.markSnapshotDone([table], headGTID.comparable); + await framework.container.probes.touch(); + } } + await batch.commit(headGTID.comparable); } - await batch.commit(headGTID.comparable); - }); + ); logger.info(`Initial replication done`); - await this.pool.query('COMMIT'); + await connection.query('COMMIT'); } catch (e) { - await this.pool.query('ROLLBACK'); + await connection.query('ROLLBACK'); throw e; + } finally { + connection.release(); } } private async snapshotTable( - batch: storage.BucketStorageBatch, connection: mysql.Connection, + batch: storage.BucketStorageBatch, table: storage.SourceTable ) { logger.info(`Replicating ${table.qualifiedName}`); - // TODO this could be large. Should we chunk this - const [rows] = await connection.query(`SELECT * FROM ${table.schema}.${table.table}`); - for (const record of rows) { - await batch.save({ - tag: storage.SaveOperationTag.INSERT, - sourceTable: table, - before: undefined, - after: common.toSQLiteRow(record) - }); - } - await batch.flush(); + // TODO count rows + + return new Promise((resolve, reject) => { + // MAX_EXECUTION_TIME(0) hint disables execution timeout for this query + connection + .query(`SELECT /*+ MAX_EXECUTION_TIME(0) */ * FROM ${table.schema}.${table.table}`) + .on('error', (err) => { + reject(err); + }) + .on('result', async (row) => { + connection.pause(); + const record = common.toSQLiteRow(row); + + await batch.save({ + tag: storage.SaveOperationTag.INSERT, + sourceTable: table, + before: undefined, + beforeReplicaId: undefined, + after: record, + afterReplicaId: getUuidReplicaIdentityBson(record, table.replicaIdColumns) + }); + connection.resume(); + }) + .on('end', async function () { + await batch.flush(); + resolve(); + }); + }); } async replicate() { @@ -273,193 +298,159 @@ AND table_type = 'BASE TABLE';`, connection.release(); const initialReplicationCompleted = await this.checkInitialReplicated(); - - const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({}, async (batch) => { - for (let tablePattern of sourceTables) { - await this.getQualifiedTableNames(batch, tablePattern); - } - }); - if (!initialReplicationCompleted) { - await this.replicateInitial(); + await this.startInitialReplication(); + } + } + + private getTable(tableId: string): storage.SourceTable { + const table = this.relation_cache.get(tableId); + if (table == null) { + // We should always receive a replication message before the relation is used. + // If we can't find it, it's a bug. + throw new Error(`Missing relation cache for ${tableId}`); } + return table; } async streamChanges() { // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); + const connection = await this.connections.getConnection(); const { checkpoint_lsn } = await this.storage.getStatus(); const fromGTID = checkpoint_lsn ? common.ReplicatedGTID.fromSerialized(checkpoint_lsn) - : await common.readExecutedGtid(this.pool); + : await common.readExecutedGtid(connection); const binLogPositionState = fromGTID.position; + connection.release(); - await this.storage.startBatch({}, async (batch) => { - const zongji = new ZongJi({ - host: this.options.connection_config.hostname, - user: this.options.connection_config.username, - password: this.options.connection_config.password - }); - - let currentGTID: common.ReplicatedGTID | null = null; - - const queue = async.queue(async (evt: BinLogEvent) => { - // State machine - switch (true) { - case zongji_utils.eventIsGTIDLog(evt): - currentGTID = common.ReplicatedGTID.fromBinLogEvent({ - raw_gtid: { - server_id: evt.serverId, - transaction_range: evt.transactionRange - }, - position: { - filename: binLogPositionState.filename, - offset: evt.nextPosition - } - }); - break; - case zongji_utils.eventIsRotation(evt): - // Update the position - binLogPositionState.filename = evt.binlogName; - binLogPositionState.offset = evt.position; - break; - case zongji_utils.eventIsWriteMutation(evt): - // TODO, can multiple tables be present? - const writeTableInfo = evt.tableMap[evt.tableId]; - - await this.writeChanges(batch, { - type: storage.SaveOperationTag.INSERT, - data: evt.rows, - database: writeTableInfo.parentSchema, - table: writeTableInfo.tableName, - // TODO cleanup - sourceTable: ( - await this.storage.resolveTable({ - connection_id: this.connectionId, - connection_tag: this.connectionTag, - entity_descriptor: { - name: writeTableInfo.tableName, - objectId: evt.tableId, - schema: writeTableInfo.parentSchema, - replicationColumns: writeTableInfo.columns.map((c: any, index: number) => ({ - name: c.name, - type: writeTableInfo.columnSchemas[index].COLUMN_TYPE - })) - }, - group_id: this.group_id, - sync_rules: this.sync_rules - }) - ).table - }); - break; - case zongji_utils.eventIsUpdateMutation(evt): - const updateTableInfo = evt.tableMap[evt.tableId]; - await this.writeChanges(batch, { - type: storage.SaveOperationTag.UPDATE, - data: evt.rows.map((row) => row.after), - previous_data: evt.rows.map((row) => row.before), - database: updateTableInfo.parentSchema, - table: updateTableInfo.tableName, - // TODO cleanup - sourceTable: ( - await this.storage.resolveTable({ - connection_id: this.connectionId, - connection_tag: this.connectionTag, - entity_descriptor: { - name: updateTableInfo.tableName, - objectId: evt.tableId, - schema: updateTableInfo.parentSchema, - replicationColumns: updateTableInfo.columns.map((c: any, index: number) => ({ - name: c.name, - type: updateTableInfo.columnSchemas[index].COLUMN_TYPE - })) - }, - group_id: this.group_id, - sync_rules: this.sync_rules - }) - ).table - }); - break; - case zongji_utils.eventIsDeleteMutation(evt): - // TODO, can multiple tables be present? - const deleteTableInfo = evt.tableMap[evt.tableId]; - await this.writeChanges(batch, { - type: storage.SaveOperationTag.DELETE, - data: evt.rows, - database: deleteTableInfo.parentSchema, - table: deleteTableInfo.tableName, - // TODO cleanup - sourceTable: ( - await this.storage.resolveTable({ - connection_id: this.connectionId, - connection_tag: this.connectionTag, - entity_descriptor: { - name: deleteTableInfo.tableName, - objectId: evt.tableId, - schema: deleteTableInfo.parentSchema, - replicationColumns: deleteTableInfo.columns.map((c: any, index: number) => ({ - name: c.name, - type: deleteTableInfo.columnSchemas[index].COLUMN_TYPE - })) - }, - group_id: this.group_id, - sync_rules: this.sync_rules - }) - ).table - }); - break; - case zongji_utils.eventIsXid(evt): - // Need to commit with a replicated GTID with updated next position - await batch.commit( - new common.ReplicatedGTID({ - raw_gtid: currentGTID!.raw, + await this.storage.startBatch( + { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, + async (batch) => { + const zongji = this.connections.createBinlogListener(); + + let currentGTID: common.ReplicatedGTID | null = null; + + const queue = async.queue(async (evt: BinLogEvent) => { + // State machine + switch (true) { + case zongji_utils.eventIsGTIDLog(evt): + currentGTID = common.ReplicatedGTID.fromBinLogEvent({ + raw_gtid: { + server_id: evt.serverId, + transaction_range: evt.transactionRange + }, position: { filename: binLogPositionState.filename, offset: evt.nextPosition } - }).comparable - ); - currentGTID = null; - // chunks_replicated_total.add(1); - // TODO update other metrics - break; - } - }, 1); - - zongji.on('binlog', (evt: BinLogEvent) => { - queue.push(evt); - }); + }); + break; + case zongji_utils.eventIsRotation(evt): + // Update the position + binLogPositionState.filename = evt.binlogName; + binLogPositionState.offset = evt.position; + break; + case zongji_utils.eventIsWriteMutation(evt): + // TODO, can multiple tables be present? + const writeTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { + type: storage.SaveOperationTag.INSERT, + data: evt.rows, + database: writeTableInfo.parentSchema, + table: writeTableInfo.tableName, + sourceTable: this.getTable( + getMysqlRelId({ + schema: writeTableInfo.parentSchema, + name: writeTableInfo.tableName + }) + ) + }); + break; + case zongji_utils.eventIsUpdateMutation(evt): + const updateTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { + type: storage.SaveOperationTag.UPDATE, + data: evt.rows.map((row) => row.after), + previous_data: evt.rows.map((row) => row.before), + database: updateTableInfo.parentSchema, + table: updateTableInfo.tableName, + sourceTable: this.getTable( + getMysqlRelId({ + schema: updateTableInfo.parentSchema, + name: updateTableInfo.tableName + }) + ) + }); + break; + case zongji_utils.eventIsDeleteMutation(evt): + // TODO, can multiple tables be present? + const deleteTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { + type: storage.SaveOperationTag.DELETE, + data: evt.rows, + database: deleteTableInfo.parentSchema, + table: deleteTableInfo.tableName, + // TODO cleanup + sourceTable: this.getTable( + getMysqlRelId({ + schema: deleteTableInfo.parentSchema, + name: deleteTableInfo.tableName + }) + ) + }); + break; + case zongji_utils.eventIsXid(evt): + // Need to commit with a replicated GTID with updated next position + await batch.commit( + new common.ReplicatedGTID({ + raw_gtid: currentGTID!.raw, + position: { + filename: binLogPositionState.filename, + offset: evt.nextPosition + } + }).comparable + ); + currentGTID = null; + // chunks_replicated_total.add(1); + break; + } + }, 1); - zongji.start({ - includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'], - excludeEvents: [], - filename: binLogPositionState.filename, - position: binLogPositionState.offset - }); + zongji.on('binlog', (evt: BinLogEvent) => { + queue.push(evt); + }); - // Forever young - await new Promise((resolve, reject) => { - queue.error((error) => { - zongji.stop(); - queue.kill(); - reject(error); + zongji.start({ + includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'], + excludeEvents: [], + filename: binLogPositionState.filename, + position: binLogPositionState.offset }); - this.abortSignal.addEventListener( - 'abort', - async () => { + + // Forever young + await new Promise((resolve, reject) => { + queue.error((error) => { zongji.stop(); queue.kill(); - if (!queue.length) { - await queue.drain(); - } - resolve(); - }, - { once: true } - ); - }); - }); + reject(error); + }); + this.abortSignal.addEventListener( + 'abort', + async () => { + zongji.stop(); + queue.kill(); + if (!queue.length) { + await queue.drain(); + } + resolve(); + }, + { once: true } + ); + }); + } + ); } private async writeChanges( @@ -496,26 +487,42 @@ AND table_type = 'BASE TABLE';`, ): Promise { switch (msg.type) { case storage.SaveOperationTag.INSERT: + const record = common.toSQLiteRow(msg.data); return await batch.save({ tag: storage.SaveOperationTag.INSERT, sourceTable: msg.sourceTable, before: undefined, - after: common.toSQLiteRow(msg.data) + beforeReplicaId: undefined, + after: record, + afterReplicaId: getUuidReplicaIdentityBson(record, msg.sourceTable.replicaIdColumns) }); case storage.SaveOperationTag.UPDATE: + // "before" may be null if the replica id columns are unchanged + // It's fine to treat that the same as an insert. + const beforeUpdated = msg.previous_data ? common.toSQLiteRow(msg.previous_data) : undefined; + const after = common.toSQLiteRow(msg.data); + return await batch.save({ tag: storage.SaveOperationTag.UPDATE, sourceTable: msg.sourceTable, - before: msg.previous_data ? common.toSQLiteRow(msg.previous_data) : undefined, - after: common.toSQLiteRow(msg.data) + before: beforeUpdated, + beforeReplicaId: beforeUpdated + ? getUuidReplicaIdentityBson(beforeUpdated, msg.sourceTable.replicaIdColumns) + : undefined, + after: common.toSQLiteRow(msg.data), + afterReplicaId: getUuidReplicaIdentityBson(after, msg.sourceTable.replicaIdColumns) }); case storage.SaveOperationTag.DELETE: + const beforeDeleted = common.toSQLiteRow(msg.data); + return await batch.save({ tag: storage.SaveOperationTag.DELETE, sourceTable: msg.sourceTable, - before: common.toSQLiteRow(msg.data), - after: undefined + before: beforeDeleted, + beforeReplicaId: getUuidReplicaIdentityBson(beforeDeleted, msg.sourceTable.replicaIdColumns), + after: undefined, + afterReplicaId: undefined }); default: return null; From 49d01cdab54811a0522abff99d483166c6a66a67 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Thu, 10 Oct 2024 17:35:04 +0200 Subject: [PATCH 237/298] Lockfile update --- pnpm-lock.yaml | 60 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16007b473..7da85c3c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,27 +99,27 @@ importers: '@powersync/lib-services-framework': specifier: workspace:* version: link:../../libs/lib-services + '@powersync/mysql-zongji': + specifier: 0.0.0-dev-20241008105633 + version: 0.0.0-dev-20241008105633 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core - '@powersync/service-jsonbig': - specifier: workspace:* - version: link:../../packages/jsonbig '@powersync/service-sync-rules': specifier: workspace:* version: link:../../packages/sync-rules '@powersync/service-types': specifier: workspace:* version: link:../../packages/types - '@vlasky/zongji': - specifier: link:../../../zongji - version: link:../../../zongji async: specifier: ^3.2.4 version: 3.2.5 mysql2: specifier: ^3.11.0 version: 3.11.3 + semver: + specifier: ^7.5.4 + version: 7.6.2 ts-codec: specifier: ^1.2.2 version: 1.2.2 @@ -133,6 +133,9 @@ importers: '@types/async': specifier: ^3.2.24 version: 3.2.24 + '@types/semver': + specifier: ^7.5.4 + version: 7.5.8 '@types/uuid': specifier: ^9.0.8 version: 9.0.8 @@ -1143,6 +1146,10 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} + '@powersync/mysql-zongji@0.0.0-dev-20241008105633': + resolution: {integrity: sha512-HNKV9etnhQ7VqpHJHqyW94OSNIOprWDRmhLR1iPsE7DYyvrDexk28Tc8bnxc0ti2JfwO47rwWrP/C1F1bfTXcw==} + engines: {node: '>=20.0.0'} + '@prisma/instrumentation@5.15.0': resolution: {integrity: sha512-fCWOOOajTKOUEp43gRmBqwt6oN9bPJcLiloi2OG/2ED0N5z62Cuza6FDrlm3SJHQAXYlXqLE0HLdEE5WcUkOzg==} @@ -1477,6 +1484,10 @@ packages: '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} + '@vlasky/mysql@2.18.6': + resolution: {integrity: sha512-c+qz/zzqecteQLchoje0E0rjLla935d6hHPpMKmfyQJnHlycLpR49ekS6s/zUAt8w0Um5hFglKXm4+PeJTVhaQ==} + engines: {node: '>= 0.6'} + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1642,6 +1653,13 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + big-integer@1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + + bignumber.js@9.1.1: + resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -3178,6 +3196,9 @@ packages: readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + readable-stream@2.3.7: + resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -4777,6 +4798,12 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + '@powersync/mysql-zongji@0.0.0-dev-20241008105633': + dependencies: + '@vlasky/mysql': 2.18.6 + big-integer: 1.6.51 + iconv-lite: 0.6.3 + '@prisma/instrumentation@5.15.0': dependencies: '@opentelemetry/api': 1.8.0 @@ -5151,6 +5178,13 @@ snapshots: loupe: 3.1.1 tinyrainbow: 1.2.0 + '@vlasky/mysql@2.18.6': + dependencies: + bignumber.js: 9.1.1 + readable-stream: 2.3.7 + safe-buffer: 5.2.1 + sqlstring: 2.3.3 + abbrev@1.1.1: {} abort-controller@3.0.0: @@ -5288,6 +5322,10 @@ snapshots: dependencies: is-windows: 1.0.2 + big-integer@1.6.51: {} + + bignumber.js@9.1.1: {} + binary-extensions@2.3.0: {} bl@4.1.0: @@ -6972,6 +7010,16 @@ snapshots: isarray: 0.0.1 string_decoder: 0.10.31 + readable-stream@2.3.7: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 From d2ece1ba9b7885cf4fe022fa51459792ad193d5f Mon Sep 17 00:00:00 2001 From: stevensJourney <51082125+stevensJourney@users.noreply.github.com> Date: Mon, 14 Oct 2024 08:50:05 +0200 Subject: [PATCH 238/298] [Modules] Feat: Replication Events (#105) --- .changeset/orange-eagles-tap.md | 5 + .changeset/popular-snails-cough.md | 6 + .changeset/sour-turkeys-collect.md | 7 + libs/lib-services/src/utils/BaseObserver.ts | 6 +- .../src/utils/DisposableObserver.ts | 37 + libs/lib-services/src/utils/utils-index.ts | 1 + .../test/src/DisposeableObserver.test.ts | 58 + .../src/module/PostgresModule.ts | 17 +- .../src/replication/WalStream.ts | 25 +- .../replication/WalStreamReplicationJob.ts | 2 +- .../src/replication/WalStreamReplicator.ts | 4 +- .../test/src/slow_tests.test.ts | 10 +- modules/module-postgres/test/src/util.ts | 10 +- .../test/src/wal_stream_utils.ts | 21 +- packages/service-core/src/api/diagnostics.ts | 2 +- .../src/entry/commands/compact-action.ts | 6 +- ...099539247-custom-write-checkpoint-index.ts | 37 + .../src/replication/AbstractReplicationJob.ts | 4 +- .../src/replication/AbstractReplicator.ts | 9 +- .../src/replication/ReplicationEngine.ts | 2 +- .../src/replication/replication-index.ts | 2 +- .../src/routes/endpoints/checkpointing.ts | 7 +- packages/service-core/src/runner/teardown.ts | 6 +- .../service-core/src/storage/BucketStorage.ts | 41 +- .../src/storage/MongoBucketStorage.ts | 99 +- .../src/storage/ReplicationEventPayload.ts | 16 + .../service-core/src/storage/SourceTable.ts | 11 +- .../service-core/src/storage/StorageEngine.ts | 39 +- .../src/storage/StorageProvider.ts | 12 +- .../src/storage/mongo/MongoBucketBatch.ts | 111 +- .../src/storage/mongo/MongoStorageProvider.ts | 7 +- .../storage/mongo/MongoSyncBucketStorage.ts | 32 +- .../storage/mongo/MongoWriteCheckpointAPI.ts | 136 ++ packages/service-core/src/storage/mongo/db.ts | 5 +- .../service-core/src/storage/mongo/models.ts | 9 +- .../service-core/src/storage/storage-index.ts | 2 + .../src/storage/write-checkpoint.ts | 67 + .../test/src/data_storage.test.ts | 52 +- packages/service-core/test/src/util.ts | 3 +- packages/sync-rules/.gitignore | 1 + packages/sync-rules/package.json | 5 +- packages/sync-rules/scripts/compile-schema.js | 11 + packages/sync-rules/src/BaseSqlDataQuery.ts | 125 ++ packages/sync-rules/src/SqlDataQuery.ts | 138 +- packages/sync-rules/src/SqlSyncRules.ts | 65 +- .../src/events/SqlEventDescriptor.ts | 65 + .../src/events/SqlEventSourceQuery.ts | 142 ++ packages/sync-rules/src/index.ts | 24 +- packages/sync-rules/src/json_schema.ts | 29 + packages/sync-rules/src/sql_filters.ts | 10 +- packages/sync-rules/src/sql_functions.ts | 14 +- packages/sync-rules/src/sql_support.ts | 24 +- .../sync-rules/test/src/sync_rules.test.ts | 9 +- pnpm-lock.yaml | 1483 +++++++---------- service/src/runners/server.ts | 1 + 55 files changed, 1853 insertions(+), 1219 deletions(-) create mode 100644 .changeset/orange-eagles-tap.md create mode 100644 .changeset/popular-snails-cough.md create mode 100644 .changeset/sour-turkeys-collect.md create mode 100644 libs/lib-services/src/utils/DisposableObserver.ts create mode 100644 libs/lib-services/test/src/DisposeableObserver.test.ts create mode 100644 packages/service-core/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts create mode 100644 packages/service-core/src/storage/ReplicationEventPayload.ts create mode 100644 packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts create mode 100644 packages/service-core/src/storage/write-checkpoint.ts create mode 100644 packages/sync-rules/.gitignore create mode 100644 packages/sync-rules/scripts/compile-schema.js create mode 100644 packages/sync-rules/src/BaseSqlDataQuery.ts create mode 100644 packages/sync-rules/src/events/SqlEventDescriptor.ts create mode 100644 packages/sync-rules/src/events/SqlEventSourceQuery.ts diff --git a/.changeset/orange-eagles-tap.md b/.changeset/orange-eagles-tap.md new file mode 100644 index 000000000..7a21eec33 --- /dev/null +++ b/.changeset/orange-eagles-tap.md @@ -0,0 +1,5 @@ +--- +'@powersync/lib-services-framework': minor +--- + +Added disposable listeners and observers diff --git a/.changeset/popular-snails-cough.md b/.changeset/popular-snails-cough.md new file mode 100644 index 000000000..5dc9e2d4d --- /dev/null +++ b/.changeset/popular-snails-cough.md @@ -0,0 +1,6 @@ +--- +'@powersync/service-core': minor +'@powersync/service-sync-rules': minor +--- + +Added ability to emit data replication events diff --git a/.changeset/sour-turkeys-collect.md b/.changeset/sour-turkeys-collect.md new file mode 100644 index 000000000..d9bc279fb --- /dev/null +++ b/.changeset/sour-turkeys-collect.md @@ -0,0 +1,7 @@ +--- +'@powersync/service-module-postgres': patch +'@powersync/service-rsocket-router': patch +'@powersync/service-types': patch +--- + +Updates from Replication events changes diff --git a/libs/lib-services/src/utils/BaseObserver.ts b/libs/lib-services/src/utils/BaseObserver.ts index 4c5b689ce..937fde59a 100644 --- a/libs/lib-services/src/utils/BaseObserver.ts +++ b/libs/lib-services/src/utils/BaseObserver.ts @@ -1,6 +1,10 @@ import { v4 as uuid } from 'uuid'; -export class BaseObserver { +export interface ObserverClient { + registerListener(listener: Partial): () => void; +} + +export class BaseObserver implements ObserverClient { protected listeners: { [id: string]: Partial }; constructor() { diff --git a/libs/lib-services/src/utils/DisposableObserver.ts b/libs/lib-services/src/utils/DisposableObserver.ts new file mode 100644 index 000000000..1440d57e7 --- /dev/null +++ b/libs/lib-services/src/utils/DisposableObserver.ts @@ -0,0 +1,37 @@ +import { BaseObserver, ObserverClient } from './BaseObserver.js'; + +export interface DisposableListener { + /** + * Event which is fired when the `[Symbol.disposed]` method is called. + */ + disposed: () => void; +} + +export interface DisposableObserverClient extends ObserverClient, Disposable { + /** + * Registers a listener that is automatically disposed when the parent is disposed. + * This is useful for disposing nested listeners. + */ + registerManagedListener: (parent: DisposableObserverClient, cb: Partial) => () => void; +} + +export class DisposableObserver + extends BaseObserver + implements DisposableObserverClient +{ + registerManagedListener(parent: DisposableObserverClient, cb: Partial) { + const disposer = this.registerListener(cb); + parent.registerListener({ + disposed: () => { + disposer(); + } + }); + return disposer; + } + + [Symbol.dispose]() { + this.iterateListeners((cb) => cb.disposed?.()); + // Delete all callbacks + Object.keys(this.listeners).forEach((key) => delete this.listeners[key]); + } +} diff --git a/libs/lib-services/src/utils/utils-index.ts b/libs/lib-services/src/utils/utils-index.ts index ee42d4057..59b89d274 100644 --- a/libs/lib-services/src/utils/utils-index.ts +++ b/libs/lib-services/src/utils/utils-index.ts @@ -1,2 +1,3 @@ export * from './BaseObserver.js'; +export * from './DisposableObserver.js'; export * from './environment-variables.js'; diff --git a/libs/lib-services/test/src/DisposeableObserver.test.ts b/libs/lib-services/test/src/DisposeableObserver.test.ts new file mode 100644 index 000000000..1cde6a58b --- /dev/null +++ b/libs/lib-services/test/src/DisposeableObserver.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, test } from 'vitest'; + +import { DisposableListener, DisposableObserver } from '../../src/utils/DisposableObserver.js'; + +describe('DisposableObserver', () => { + test('it should dispose all listeners on dispose', () => { + const listener = new DisposableObserver(); + + let wasDisposed = false; + listener.registerListener({ + disposed: () => { + wasDisposed = true; + } + }); + + listener[Symbol.dispose](); + + expect(wasDisposed).equals(true); + expect(Object.keys(listener['listeners']).length).equals(0); + }); + + test('it should dispose nested listeners for managed listeners', () => { + interface ParentListener extends DisposableListener { + childCreated: (child: DisposableObserver) => void; + } + class ParentObserver extends DisposableObserver { + createChild() { + const child = new DisposableObserver(); + this.iterateListeners((cb) => cb.childCreated?.(child)); + } + } + + const parent = new ParentObserver(); + let aChild: DisposableObserver | null = null; + + parent.registerListener({ + childCreated: (child) => { + aChild = child; + child.registerManagedListener(parent, { + test: () => { + // this does nothing + } + }); + } + }); + + parent.createChild(); + + // The managed listener should add a `disposed` listener + expect(Object.keys(parent['listeners']).length).equals(2); + expect(Object.keys(aChild!['listeners']).length).equals(1); + + parent[Symbol.dispose](); + expect(Object.keys(parent['listeners']).length).equals(0); + // The listener attached to the child should be disposed when the parent was disposed + expect(Object.keys(aChild!['listeners']).length).equals(0); + }); +}); diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 5dc0caf79..eabffca0a 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -1,20 +1,13 @@ -import { - api, - auth, - ConfigurationFileSyncRulesProvider, - replication, - system, - TearDownOptions -} from '@powersync/service-core'; +import { api, auth, ConfigurationFileSyncRulesProvider, modules, replication, system } from '@powersync/service-core'; import * as jpgwire from '@powersync/service-jpgwire'; -import * as types from '../types/types.js'; import { PostgresRouteAPIAdapter } from '../api/PostgresRouteAPIAdapter.js'; import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; -import { WalStreamReplicator } from '../replication/WalStreamReplicator.js'; import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js'; +import { PgManager } from '../replication/PgManager.js'; import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js'; import { cleanUpReplicationSlot } from '../replication/replication-utils.js'; -import { PgManager } from '../replication/PgManager.js'; +import { WalStreamReplicator } from '../replication/WalStreamReplicator.js'; +import * as types from '../types/types.js'; export class PostgresModule extends replication.ReplicationModule { constructor() { @@ -70,7 +63,7 @@ export class PostgresModule extends replication.ReplicationModule { + async teardown(options: modules.TearDownOptions): Promise { const normalisedConfig = this.resolveConfig(this.decodedConfig!); const connectionManager = new PgManager(normalisedConfig, { idleTimeout: 30_000, diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index cd856c2c8..80b11df0e 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -1,11 +1,11 @@ -import * as pgwire from '@powersync/service-jpgwire'; -import * as util from '../utils/pgwire_utils.js'; import { container, errors, logger } from '@powersync/lib-services-framework'; +import { getUuidReplicaIdentityBson, Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; +import * as pg_utils from '../utils/pgwire_utils.js'; +import { PgManager } from './PgManager.js'; import { getPgOutputRelation, getRelId } from './PgRelation.js'; -import { getUuidReplicaIdentityBson, Metrics, SourceEntityDescriptor, storage } from '@powersync/service-core'; import { checkSourceConfiguration, getReplicationIdentityColumns } from './replication-utils.js'; -import { PgManager } from './PgManager.js'; export const ZERO_LSN = '00000000/00000000'; export const PUBLICATION_NAME = 'powersync'; @@ -60,7 +60,7 @@ export class WalStream { // Ping to speed up cancellation of streaming replication // We're not using pg_snapshot here, since it could be in the middle of // an initial replication transaction. - const promise = util.retriedQuery( + const promise = pg_utils.retriedQuery( this.connections.pool, `SELECT * FROM pg_logical_emit_message(false, 'powersync', 'ping')` ); @@ -347,7 +347,6 @@ WHERE oid = $1::regclass`, for (let table of tables) { await this.snapshotTable(batch, db, table); await batch.markSnapshotDone([table], lsn); - await touch(); } } @@ -395,7 +394,7 @@ WHERE oid = $1::regclass`, throw new Error(`Aborted initial replication of ${this.slot_name}`); } - for (let record of WalStream.getQueryData(rows)) { + for (const record of WalStream.getQueryData(rows)) { // This auto-flushes when the batch reaches its size limit await batch.save({ tag: 'insert', @@ -406,6 +405,7 @@ WHERE oid = $1::regclass`, afterReplicaId: getUuidReplicaIdentityBson(record, table.replicaIdColumns) }); } + at += rows.length; Metrics.getInstance().rows_replicated_total.add(rows.length); @@ -495,7 +495,7 @@ WHERE oid = $1::regclass`, if (msg.tag == 'insert') { Metrics.getInstance().rows_replicated_total.add(1); - const baseRecord = util.constructAfterRecord(msg); + const baseRecord = pg_utils.constructAfterRecord(msg); return await batch.save({ tag: 'insert', sourceTable: table, @@ -508,8 +508,8 @@ WHERE oid = $1::regclass`, Metrics.getInstance().rows_replicated_total.add(1); // "before" may be null if the replica id columns are unchanged // It's fine to treat that the same as an insert. - const before = util.constructBeforeRecord(msg); - const after = util.constructAfterRecord(msg); + const before = pg_utils.constructBeforeRecord(msg); + const after = pg_utils.constructAfterRecord(msg); return await batch.save({ tag: 'update', sourceTable: table, @@ -520,7 +520,7 @@ WHERE oid = $1::regclass`, }); } else if (msg.tag == 'delete') { Metrics.getInstance().rows_replicated_total.add(1); - const before = util.constructBeforeRecord(msg)!; + const before = pg_utils.constructBeforeRecord(msg)!; return await batch.save({ tag: 'delete', @@ -592,7 +592,6 @@ WHERE oid = $1::regclass`, // chunkLastLsn may come from normal messages in the chunk, // or from a PrimaryKeepalive message. const { messages, lastLsn: chunkLastLsn } = chunk; - for (const msg of messages) { if (msg.tag == 'relation') { await this.handleRelation(batch, getPgOutputRelation(msg), true); @@ -609,7 +608,7 @@ WHERE oid = $1::regclass`, } count += 1; - const result = await this.writeChange(batch, msg); + await this.writeChange(batch, msg); } } diff --git a/modules/module-postgres/src/replication/WalStreamReplicationJob.ts b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts index 8d82840d6..40247452a 100644 --- a/modules/module-postgres/src/replication/WalStreamReplicationJob.ts +++ b/modules/module-postgres/src/replication/WalStreamReplicationJob.ts @@ -1,6 +1,6 @@ -import { MissingReplicationSlotError, WalStream } from './WalStream.js'; import { container } from '@powersync/lib-services-framework'; import { PgManager } from './PgManager.js'; +import { MissingReplicationSlotError, WalStream } from './WalStream.js'; import { replication } from '@powersync/service-core'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; diff --git a/modules/module-postgres/src/replication/WalStreamReplicator.ts b/modules/module-postgres/src/replication/WalStreamReplicator.ts index fb068ba14..14a21725e 100644 --- a/modules/module-postgres/src/replication/WalStreamReplicator.ts +++ b/modules/module-postgres/src/replication/WalStreamReplicator.ts @@ -1,7 +1,7 @@ -import { storage, replication } from '@powersync/service-core'; -import { WalStreamReplicationJob } from './WalStreamReplicationJob.js'; +import { replication, storage } from '@powersync/service-core'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; import { cleanUpReplicationSlot } from './replication-utils.js'; +import { WalStreamReplicationJob } from './WalStreamReplicationJob.js'; export interface WalStreamReplicatorOptions extends replication.AbstractReplicatorOptions { connectionFactory: ConnectionManagerFactory; diff --git a/modules/module-postgres/test/src/slow_tests.test.ts b/modules/module-postgres/test/src/slow_tests.test.ts index ad7c6753d..7c5bad017 100644 --- a/modules/module-postgres/test/src/slow_tests.test.ts +++ b/modules/module-postgres/test/src/slow_tests.test.ts @@ -2,16 +2,16 @@ import * as bson from 'bson'; import { afterEach, describe, expect, test } from 'vitest'; import { WalStream, WalStreamOptions } from '../../src/replication/WalStream.js'; import { env } from './env.js'; -import { TEST_CONNECTION_OPTIONS, clearTestDb, connectPgPool, getClientCheckpoint } from './util.js'; +import { clearTestDb, connectPgPool, getClientCheckpoint, TEST_CONNECTION_OPTIONS } from './util.js'; import * as pgwire from '@powersync/service-jpgwire'; import { SqliteRow } from '@powersync/service-sync-rules'; import { mapOpEntry, MongoBucketStorage } from '@/storage/storage-index.js'; -import * as timers from 'node:timers/promises'; +import { reduceBucket, validateCompactedBucket } from '@core-tests/bucket_validation.js'; import { MONGO_STORAGE_FACTORY, StorageFactory } from '@core-tests/util.js'; import { PgManager } from '@module/replication/PgManager.js'; -import { reduceBucket, validateCompactedBucket } from '@core-tests/bucket_validation.js'; +import * as timers from 'node:timers/promises'; describe('slow tests - mongodb', function () { // These are slow, inconsistent tests. @@ -82,7 +82,7 @@ bucket_definitions: - SELECT * FROM "test_data" `; const syncRules = await f.updateSyncRules({ content: syncRuleContent }); - const storage = f.getInstance(syncRules); + using storage = f.getInstance(syncRules); abortController = new AbortController(); const options: WalStreamOptions = { abort_signal: abortController.signal, @@ -234,7 +234,7 @@ bucket_definitions: - SELECT id, description FROM "test_data" `; const syncRules = await f.updateSyncRules({ content: syncRuleContent }); - const storage = f.getInstance(syncRules); + using storage = f.getInstance(syncRules); // 1. Setup some base data that will be replicated in initial replication await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`); diff --git a/modules/module-postgres/test/src/util.ts b/modules/module-postgres/test/src/util.ts index d4e2b7170..c8142739d 100644 --- a/modules/module-postgres/test/src/util.ts +++ b/modules/module-postgres/test/src/util.ts @@ -1,11 +1,11 @@ +import { connectMongo } from '@core-tests/util.js'; import * as types from '@module/types/types.js'; import * as pg_utils from '@module/utils/pgwire_utils.js'; +import { logger } from '@powersync/lib-services-framework'; import { BucketStorageFactory, Metrics, MongoBucketStorage, OpId } from '@powersync/service-core'; import * as pgwire from '@powersync/service-jpgwire'; -import { env } from './env.js'; import { pgwireRows } from '@powersync/service-jpgwire'; -import { logger } from '@powersync/lib-services-framework'; -import { connectMongo } from '@core-tests/util.js'; +import { env } from './env.js'; // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -35,7 +35,9 @@ export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { await db.clear(); - return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); + return new MongoBucketStorage(db, { + slot_name_prefix: 'test_' + }); }; export async function clearTestDb(db: pgwire.PgClient) { diff --git a/modules/module-postgres/test/src/wal_stream_utils.ts b/modules/module-postgres/test/src/wal_stream_utils.ts index 8af286baa..23eced2e7 100644 --- a/modules/module-postgres/test/src/wal_stream_utils.ts +++ b/modules/module-postgres/test/src/wal_stream_utils.ts @@ -1,9 +1,9 @@ -import { BucketStorageFactory, SyncRulesBucketStorage } from '@powersync/service-core'; -import * as pgwire from '@powersync/service-jpgwire'; -import { TEST_CONNECTION_OPTIONS, clearTestDb, getClientCheckpoint } from './util.js'; -import { WalStream, WalStreamOptions, PUBLICATION_NAME } from '@module/replication/WalStream.js'; import { fromAsync } from '@core-tests/stream_utils.js'; import { PgManager } from '@module/replication/PgManager.js'; +import { PUBLICATION_NAME, WalStream, WalStreamOptions } from '@module/replication/WalStream.js'; +import { BucketStorageFactory, SyncRulesBucketStorage } from '@powersync/service-core'; +import * as pgwire from '@powersync/service-jpgwire'; +import { clearTestDb, getClientCheckpoint, TEST_CONNECTION_OPTIONS } from './util.js'; /** * Tests operating on the wal stream need to configure the stream and manage asynchronous @@ -20,16 +20,12 @@ export function walStreamTest( const connectionManager = new PgManager(TEST_CONNECTION_OPTIONS, {}); await clearTestDb(connectionManager.pool); - const context = new WalStreamTestContext(f, connectionManager); - try { - await test(context); - } finally { - await context.dispose(); - } + await using context = new WalStreamTestContext(f, connectionManager); + await test(context); }; } -export class WalStreamTestContext { +export class WalStreamTestContext implements AsyncDisposable { private _walStream?: WalStream; private abortController = new AbortController(); private streamPromise?: Promise; @@ -41,10 +37,11 @@ export class WalStreamTestContext { public connectionManager: PgManager ) {} - async dispose() { + async [Symbol.asyncDispose]() { this.abortController.abort(); await this.streamPromise; await this.connectionManager.destroy(); + this.storage?.[Symbol.dispose](); } get pool() { diff --git a/packages/service-core/src/api/diagnostics.ts b/packages/service-core/src/api/diagnostics.ts index 2ebf5ada5..d323fcf81 100644 --- a/packages/service-core/src/api/diagnostics.ts +++ b/packages/service-core/src/api/diagnostics.ts @@ -57,7 +57,7 @@ export async function getSyncRulesStatus( // This method can run under some situations if no connection is configured yet. // It will return a default tag in such a case. This default tag is not module specific. const tag = sourceConfig.tag ?? DEFAULT_TAG; - const systemStorage = live_status ? bucketStorage.getInstance(sync_rules) : undefined; + using systemStorage = live_status ? bucketStorage.getInstance(sync_rules) : undefined; const status = await systemStorage?.getStatus(); let replication_lag_bytes: number | undefined = undefined; diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index e5f34ea9c..128c54841 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -36,13 +36,15 @@ export function registerCompactAction(program: Command) { const client = psdb.client; await client.connect(); try { - const bucketStorage = new storage.MongoBucketStorage(psdb, { slot_name_prefix: configuration.slot_name_prefix }); + const bucketStorage = new storage.MongoBucketStorage(psdb, { + slot_name_prefix: configuration.slot_name_prefix + }); const active = await bucketStorage.getActiveSyncRulesContent(); if (active == null) { logger.info('No active instance to compact'); return; } - const p = bucketStorage.getInstance(active); + using p = bucketStorage.getInstance(active); logger.info('Performing compaction...'); await p.compact({ memoryLimitMB: COMPACT_MEMORY_LIMIT_MB }); logger.info('Successfully compacted storage.'); diff --git a/packages/service-core/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts b/packages/service-core/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts new file mode 100644 index 000000000..2bac37fcc --- /dev/null +++ b/packages/service-core/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts @@ -0,0 +1,37 @@ +import * as storage from '../../../storage/storage-index.js'; +import * as utils from '../../../util/util-index.js'; + +const INDEX_NAME = 'user_sync_rule_unique'; + +export const up = async (context: utils.MigrationContext) => { + const { runner_config } = context; + const config = await utils.loadConfig(runner_config); + const db = storage.createPowerSyncMongo(config.storage); + + try { + await db.custom_write_checkpoints.createIndex( + { + user_id: 1, + sync_rules_id: 1 + }, + { name: INDEX_NAME, unique: true } + ); + } finally { + await db.client.close(); + } +}; + +export const down = async (context: utils.MigrationContext) => { + const { runner_config } = context; + const config = await utils.loadConfig(runner_config); + + const db = storage.createPowerSyncMongo(config.storage); + + try { + if (await db.custom_write_checkpoints.indexExists(INDEX_NAME)) { + await db.custom_write_checkpoints.dropIndex(INDEX_NAME); + } + } finally { + await db.client.close(); + } +}; diff --git a/packages/service-core/src/replication/AbstractReplicationJob.ts b/packages/service-core/src/replication/AbstractReplicationJob.ts index 913146b83..dc27c2eda 100644 --- a/packages/service-core/src/replication/AbstractReplicationJob.ts +++ b/packages/service-core/src/replication/AbstractReplicationJob.ts @@ -1,7 +1,7 @@ -import * as storage from '../storage/storage-index.js'; -import { ErrorRateLimiter } from './ErrorRateLimiter.js'; import { container, logger } from '@powersync/lib-services-framework'; import winston from 'winston'; +import * as storage from '../storage/storage-index.js'; +import { ErrorRateLimiter } from './ErrorRateLimiter.js'; export interface AbstractReplicationJobOptions { id: string; diff --git a/packages/service-core/src/replication/AbstractReplicator.ts b/packages/service-core/src/replication/AbstractReplicator.ts index 6c5a2a934..fcc3fa0ec 100644 --- a/packages/service-core/src/replication/AbstractReplicator.ts +++ b/packages/service-core/src/replication/AbstractReplicator.ts @@ -1,10 +1,10 @@ +import { container, logger } from '@powersync/lib-services-framework'; import { hrtime } from 'node:process'; +import winston from 'winston'; import * as storage from '../storage/storage-index.js'; -import { container, logger } from '@powersync/lib-services-framework'; +import { StorageEngine } from '../storage/storage-index.js'; import { SyncRulesProvider } from '../util/config/sync-rules/sync-rules-provider.js'; -import winston from 'winston'; import { AbstractReplicationJob } from './AbstractReplicationJob.js'; -import { StorageEngine } from '../storage/storage-index.js'; import { ErrorRateLimiter } from './ErrorRateLimiter.js'; // 5 minutes @@ -192,6 +192,7 @@ export abstract class AbstractReplicator = new Map(); diff --git a/packages/service-core/src/replication/replication-index.ts b/packages/service-core/src/replication/replication-index.ts index 455ca913b..0b37534c9 100644 --- a/packages/service-core/src/replication/replication-index.ts +++ b/packages/service-core/src/replication/replication-index.ts @@ -1,5 +1,5 @@ -export * from './ErrorRateLimiter.js'; export * from './AbstractReplicationJob.js'; export * from './AbstractReplicator.js'; +export * from './ErrorRateLimiter.js'; export * from './ReplicationEngine.js'; export * from './ReplicationModule.js'; diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index c60c3b233..0cfd2dc12 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -1,5 +1,5 @@ -import * as t from 'ts-codec'; import { logger, router, schema } from '@powersync/lib-services-framework'; +import * as t from 'ts-codec'; import * as util from '../../util/util-index.js'; import { authUser } from '../auth.js'; @@ -63,7 +63,10 @@ export const writeCheckpoint2 = routeDefinition({ storageEngine: { activeBucketStorage } } = service_context; - const writeCheckpoint = await activeBucketStorage.createWriteCheckpoint(full_user_id, { '1': currentCheckpoint }); + const writeCheckpoint = await activeBucketStorage.createManagedWriteCheckpoint({ + user_id: full_user_id, + heads: { '1': currentCheckpoint } + }); logger.info(`Write checkpoint 2: ${JSON.stringify({ currentCheckpoint, id: String(full_user_id) })}`); return { diff --git a/packages/service-core/src/runner/teardown.ts b/packages/service-core/src/runner/teardown.ts index 583635642..b6b7bb5ac 100644 --- a/packages/service-core/src/runner/teardown.ts +++ b/packages/service-core/src/runner/teardown.ts @@ -4,11 +4,11 @@ // 2. Delete the storage import { container, logger } from '@powersync/lib-services-framework'; +import timers from 'timers/promises'; import * as modules from '../modules/modules-index.js'; -import * as system from '../system/system-index.js'; import * as storage from '../storage/storage-index.js'; +import * as system from '../system/system-index.js'; import * as utils from '../util/util-index.js'; -import timers from 'timers/promises'; export async function teardown(runnerConfig: utils.RunnerConfig) { try { @@ -51,7 +51,7 @@ async function terminateSyncRules(storageFactory: storage.BucketStorageFactory, // Mark the sync rules as terminated for (let syncRules of combinedSyncRules) { - const syncRulesStorage = storageFactory.getInstance(syncRules); + using syncRulesStorage = storageFactory.getInstance(syncRules); // The storage will be dropped at the end of the teardown, so we don't need to clear it here await syncRulesStorage.terminate({ clearStorage: false }); } diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index f2117f673..a7b8a716d 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -1,3 +1,4 @@ +import { DisposableListener, DisposableObserverClient } from '@powersync/lib-services-framework'; import { EvaluatedParameters, EvaluatedRow, @@ -8,11 +9,19 @@ import { ToastableSqliteRow } from '@powersync/service-sync-rules'; import * as util from '../util/util-index.js'; -import { SourceTable } from './SourceTable.js'; +import { ReplicationEventPayload } from './ReplicationEventPayload.js'; import { SourceEntityDescriptor } from './SourceEntity.js'; -import { ReplicaId } from './storage-index.js'; +import { SourceTable } from './SourceTable.js'; +import { BatchedCustomWriteCheckpointOptions, ReplicaId, WriteCheckpointAPI } from './storage-index.js'; + +export interface BucketStorageFactoryListener extends DisposableListener { + syncStorageCreated: (storage: SyncRulesBucketStorage) => void; + replicationEvent: (event: ReplicationEventPayload) => void; +} -export interface BucketStorageFactory { +export interface BucketStorageFactory + extends DisposableObserverClient, + WriteCheckpointAPI { /** * Update sync rules from configuration, if changed. */ @@ -81,10 +90,9 @@ export interface BucketStorageFactory { */ getActiveCheckpoint(): Promise; - createWriteCheckpoint(user_id: string, lsns: Record): Promise; - - lastWriteCheckpoint(user_id: string, lsn: string): Promise; - + /** + * Yields the latest user write checkpoint whenever the sync checkpoint updates. + */ watchWriteCheckpoint(user_id: string, signal: AbortSignal): AsyncIterable; /** @@ -194,7 +202,11 @@ export interface StartBatchOptions extends ParseSyncRulesOptions { zeroLSN: string; } -export interface SyncRulesBucketStorage { +export interface SyncRulesBucketStorageListener extends DisposableListener { + batchStarted: (batch: BucketStorageBatch) => void; +} + +export interface SyncRulesBucketStorage extends DisposableObserverClient { readonly group_id: number; readonly slot_name: string; @@ -293,7 +305,11 @@ export interface FlushedResult { flushed_op: string; } -export interface BucketStorageBatch { +export interface BucketBatchStorageListener extends DisposableListener { + replicationEvent: (payload: ReplicationEventPayload) => void; +} + +export interface BucketStorageBatch extends DisposableObserverClient { /** * Save an op, and potentially flush. * @@ -340,6 +356,11 @@ export interface BucketStorageBatch { keepalive(lsn: string): Promise; markSnapshotDone(tables: SourceTable[], no_checkpoint_before_lsn: string): Promise; + + /** + * Queues the creation of a custom Write Checkpoint. This will be persisted after operations are flushed. + */ + addCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): void; } export interface SaveParameterData { @@ -357,6 +378,8 @@ export interface SaveBucketData { evaluated: EvaluatedRow[]; } +export type SaveOp = 'insert' | 'update' | 'delete'; + export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete; export interface SaveInsert { diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index c3e59d0b7..c0254da71 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -8,11 +8,12 @@ import * as locks from '../locks/locks-index.js'; import * as sync from '../sync/sync-index.js'; import * as util from '../util/util-index.js'; -import { logger } from '@powersync/lib-services-framework'; +import { DisposableObserver, logger } from '@powersync/lib-services-framework'; import { v4 as uuid } from 'uuid'; import { ActiveCheckpoint, BucketStorageFactory, + BucketStorageFactoryListener, ParseSyncRulesOptions, PersistedSyncRules, PersistedSyncRulesContent, @@ -20,20 +21,36 @@ import { UpdateSyncRulesOptions, WriteCheckpoint } from './BucketStorage.js'; -import { MongoPersistedSyncRulesContent } from './mongo/MongoPersistedSyncRulesContent.js'; -import { MongoSyncBucketStorage } from './mongo/MongoSyncBucketStorage.js'; import { PowerSyncMongo, PowerSyncMongoOptions } from './mongo/db.js'; import { SyncRuleDocument, SyncRuleState } from './mongo/models.js'; +import { MongoPersistedSyncRulesContent } from './mongo/MongoPersistedSyncRulesContent.js'; +import { MongoSyncBucketStorage } from './mongo/MongoSyncBucketStorage.js'; +import { MongoWriteCheckpointAPI } from './mongo/MongoWriteCheckpointAPI.js'; import { generateSlotName } from './mongo/util.js'; +import { + CustomWriteCheckpointOptions, + DEFAULT_WRITE_CHECKPOINT_MODE, + LastWriteCheckpointFilters, + ManagedWriteCheckpointOptions, + WriteCheckpointAPI, + WriteCheckpointMode +} from './write-checkpoint.js'; export interface MongoBucketStorageOptions extends PowerSyncMongoOptions {} -export class MongoBucketStorage implements BucketStorageFactory { +export class MongoBucketStorage + extends DisposableObserver + implements BucketStorageFactory +{ private readonly client: mongo.MongoClient; private readonly session: mongo.ClientSession; // TODO: This is still Postgres specific and needs to be reworked public readonly slot_name_prefix: string; + readonly write_checkpoint_mode: WriteCheckpointMode; + + protected readonly writeCheckpointAPI: WriteCheckpointAPI; + private readonly storageCache = new LRUCache({ max: 3, fetchMethod: async (id) => { @@ -49,16 +66,31 @@ export class MongoBucketStorage implements BucketStorageFactory { } const rules = new MongoPersistedSyncRulesContent(this.db, doc2); return this.getInstance(rules); + }, + dispose: (storage) => { + storage[Symbol.dispose](); } }); public readonly db: PowerSyncMongo; - constructor(db: PowerSyncMongo, options: { slot_name_prefix: string }) { + constructor( + db: PowerSyncMongo, + options: { + slot_name_prefix: string; + write_checkpoint_mode?: WriteCheckpointMode; + } + ) { + super(); this.client = db.client; this.db = db; this.session = this.client.startSession(); this.slot_name_prefix = options.slot_name_prefix; + this.write_checkpoint_mode = options.write_checkpoint_mode ?? DEFAULT_WRITE_CHECKPOINT_MODE; + this.writeCheckpointAPI = new MongoWriteCheckpointAPI({ + db, + mode: this.write_checkpoint_mode + }); } getInstance(options: PersistedSyncRulesContent): MongoSyncBucketStorage { @@ -66,7 +98,17 @@ export class MongoBucketStorage implements BucketStorageFactory { if ((typeof id as any) == 'bigint') { id = Number(id); } - return new MongoSyncBucketStorage(this, id, options, slot_name); + const storage = new MongoSyncBucketStorage(this, id, options, slot_name); + this.iterateListeners((cb) => cb.syncStorageCreated?.(storage)); + storage.registerListener({ + batchStarted: (batch) => { + // This nested listener will be automatically disposed when the storage is disposed + batch.registerManagedListener(storage, { + replicationEvent: (payload) => this.iterateListeners((cb) => cb.replicationEvent?.(payload)) + }); + } + }); + return storage; } async configureSyncRules(sync_rules: string, options?: { lock?: boolean }) { @@ -257,30 +299,20 @@ export class MongoBucketStorage implements BucketStorageFactory { }); } - async createWriteCheckpoint(user_id: string, lsns: Record): Promise { - const doc = await this.db.write_checkpoints.findOneAndUpdate( - { - user_id: user_id - }, - { - $set: { - lsns: lsns - }, - $inc: { - client_id: 1n - } - }, - { upsert: true, returnDocument: 'after' } - ); - return doc!.client_id; + async batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise { + return this.writeCheckpointAPI.batchCreateCustomWriteCheckpoints(checkpoints); } - async lastWriteCheckpoint(user_id: string, lsn: string): Promise { - const lastWriteCheckpoint = await this.db.write_checkpoints.findOne({ - user_id: user_id, - 'lsns.1': { $lte: lsn } - }); - return lastWriteCheckpoint?.client_id ?? null; + async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise { + return this.writeCheckpointAPI.createCustomWriteCheckpoint(options); + } + + async createManagedWriteCheckpoint(options: ManagedWriteCheckpointOptions): Promise { + return this.writeCheckpointAPI.createManagedWriteCheckpoint(options); + } + + async lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise { + return this.writeCheckpointAPI.lastWriteCheckpoint(filters); } async getActiveCheckpoint(): Promise { @@ -496,8 +528,17 @@ export class MongoBucketStorage implements BucketStorageFactory { // What is important is: // 1. checkpoint (op_id) changes. // 2. write checkpoint changes for the specific user + const bucketStorage = await cp.getBucketStorage(); - const currentWriteCheckpoint = await this.lastWriteCheckpoint(user_id, lsn ?? ''); + const lsnFilters: Record = lsn ? { 1: lsn } : {}; + + const currentWriteCheckpoint = await this.lastWriteCheckpoint({ + user_id, + sync_rules_id: bucketStorage?.group_id, + heads: { + ...lsnFilters + } + }); if (currentWriteCheckpoint == lastWriteCheckpoint && checkpoint == lastCheckpoint) { // No change - wait for next one diff --git a/packages/service-core/src/storage/ReplicationEventPayload.ts b/packages/service-core/src/storage/ReplicationEventPayload.ts new file mode 100644 index 000000000..c2fe0aa84 --- /dev/null +++ b/packages/service-core/src/storage/ReplicationEventPayload.ts @@ -0,0 +1,16 @@ +import * as sync_rules from '@powersync/service-sync-rules'; +import { BucketStorageBatch, SaveOp } from './BucketStorage.js'; +import { SourceTable } from './SourceTable.js'; + +export type EventData = { + op: SaveOp; + before?: sync_rules.SqliteRow; + after?: sync_rules.SqliteRow; +}; + +export type ReplicationEventPayload = { + batch: BucketStorageBatch; + data: EventData; + event: sync_rules.SqlEventDescriptor; + table: SourceTable; +}; diff --git a/packages/service-core/src/storage/SourceTable.ts b/packages/service-core/src/storage/SourceTable.ts index 2f5364633..f514f9081 100644 --- a/packages/service-core/src/storage/SourceTable.ts +++ b/packages/service-core/src/storage/SourceTable.ts @@ -23,6 +23,15 @@ export class SourceTable { */ public syncParameters = true; + /** + * True if the table is used in sync rules for events. + * + * This value is resolved externally, and cached here. + * + * Defaults to true for tests. + */ + public syncEvent = true; + constructor( public readonly id: any, public readonly connectionTag: string, @@ -53,6 +62,6 @@ export class SourceTable { } get syncAny() { - return this.syncData || this.syncParameters; + return this.syncData || this.syncParameters || this.syncEvent; } } diff --git a/packages/service-core/src/storage/StorageEngine.ts b/packages/service-core/src/storage/StorageEngine.ts index bc4d23db4..bdafeb240 100644 --- a/packages/service-core/src/storage/StorageEngine.ts +++ b/packages/service-core/src/storage/StorageEngine.ts @@ -1,18 +1,31 @@ +import { DisposableListener, DisposableObserver, logger } from '@powersync/lib-services-framework'; import { ResolvedPowerSyncConfig } from '../util/util-index.js'; import { BucketStorageFactory } from './BucketStorage.js'; -import { BucketStorageProvider, ActiveStorage } from './StorageProvider.js'; -import { logger } from '@powersync/lib-services-framework'; +import { ActiveStorage, BucketStorageProvider, StorageSettings } from './StorageProvider.js'; +import { DEFAULT_WRITE_CHECKPOINT_MODE } from './write-checkpoint.js'; export type StorageEngineOptions = { configuration: ResolvedPowerSyncConfig; }; -export class StorageEngine { +export const DEFAULT_STORAGE_SETTINGS: StorageSettings = { + writeCheckpointMode: DEFAULT_WRITE_CHECKPOINT_MODE +}; + +export interface StorageEngineListener extends DisposableListener { + storageActivated: (storage: BucketStorageFactory) => void; +} + +export class StorageEngine extends DisposableObserver { // TODO: This will need to revisited when we actually support multiple storage providers. private storageProviders: Map = new Map(); private currentActiveStorage: ActiveStorage | null = null; + private _activeSettings: StorageSettings; - constructor(private options: StorageEngineOptions) {} + constructor(private options: StorageEngineOptions) { + super(); + this._activeSettings = DEFAULT_STORAGE_SETTINGS; + } get activeBucketStorage(): BucketStorageFactory { return this.activeStorage.storage; @@ -26,6 +39,20 @@ export class StorageEngine { return this.currentActiveStorage; } + get activeSettings(): StorageSettings { + return { ...this._activeSettings }; + } + + updateSettings(settings: Partial) { + if (this.currentActiveStorage) { + throw new Error(`Storage is already active, settings cannot be modified.`); + } + this._activeSettings = { + ...this._activeSettings, + ...settings + }; + } + /** * Register a provider which generates a {@link BucketStorageFactory} * given the matching config specified in the loaded {@link ResolvedPowerSyncConfig} @@ -38,8 +65,10 @@ export class StorageEngine { logger.info('Starting Storage Engine...'); const { configuration } = this.options; this.currentActiveStorage = await this.storageProviders.get(configuration.storage.type)!.getStorage({ - resolvedConfig: configuration + resolvedConfig: configuration, + ...this.activeSettings }); + this.iterateListeners((cb) => cb.storageActivated?.(this.activeBucketStorage)); logger.info(`Successfully activated storage: ${configuration.storage.type}.`); logger.info('Successfully started Storage Engine.'); } diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts index 9e6078874..7c730fb4b 100644 --- a/packages/service-core/src/storage/StorageProvider.ts +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -1,5 +1,6 @@ -import { BucketStorageFactory } from './BucketStorage.js'; import * as util from '../util/util-index.js'; +import { BucketStorageFactory } from './BucketStorage.js'; +import { WriteCheckpointMode } from './write-checkpoint.js'; export interface ActiveStorage { storage: BucketStorageFactory; @@ -11,7 +12,14 @@ export interface ActiveStorage { tearDown(): Promise; } -export interface GetStorageOptions { +/** + * Settings which can be modified by various modules in their initialization. + */ +export interface StorageSettings { + writeCheckpointMode: WriteCheckpointMode; +} + +export interface GetStorageOptions extends StorageSettings { // TODO: This should just be the storage config. Update once the slot name prefix coupling has been removed from the storage resolvedConfig: util.ResolvedPowerSyncConfig; } diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index 81c03e3e5..30f9121bf 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -1,14 +1,22 @@ -import { SqliteRow, SqlSyncRules } from '@powersync/service-sync-rules'; +import { SqlEventDescriptor, SqliteRow, SqlSyncRules } from '@powersync/service-sync-rules'; import * as bson from 'bson'; import * as mongo from 'mongodb'; -import { container, errors, logger } from '@powersync/lib-services-framework'; +import { container, DisposableObserver, errors, logger } from '@powersync/lib-services-framework'; import * as util from '../../util/util-index.js'; -import { BucketStorageBatch, FlushedResult, mergeToast, SaveOptions } from '../BucketStorage.js'; +import { + BucketBatchStorageListener, + BucketStorageBatch, + FlushedResult, + mergeToast, + SaveOptions +} from '../BucketStorage.js'; import { SourceTable } from '../SourceTable.js'; +import { CustomWriteCheckpointOptions } from '../write-checkpoint.js'; import { PowerSyncMongo } from './db.js'; -import { CurrentBucket, CurrentDataDocument, SourceKey } from './models.js'; +import { CurrentBucket, CurrentDataDocument, SourceKey, SyncRuleDocument } from './models.js'; import { MongoIdSequence } from './MongoIdSequence.js'; +import { batchCreateCustomWriteCheckpoints } from './MongoWriteCheckpointAPI.js'; import { cacheKey, OperationBatch, RecordOperation } from './OperationBatch.js'; import { PersistedBatch } from './PersistedBatch.js'; import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, replicaIdEquals, serializeLookup } from './util.js'; @@ -25,7 +33,7 @@ const MAX_ROW_SIZE = 15 * 1024 * 1024; // In the future, we can investigate allowing multiple replication streams operating independently. const replicationMutex = new util.Mutex(); -export class MongoBucketBatch implements BucketStorageBatch { +export class MongoBucketBatch extends DisposableObserver implements BucketStorageBatch { private readonly client: mongo.MongoClient; public readonly db: PowerSyncMongo; public readonly session: mongo.ClientSession; @@ -36,6 +44,7 @@ export class MongoBucketBatch implements BucketStorageBatch { private readonly slot_name: string; private batch: OperationBatch | null = null; + private write_checkpoint_batch: CustomWriteCheckpointOptions[] = []; /** * Last LSN received associated with a checkpoint. @@ -63,14 +72,22 @@ export class MongoBucketBatch implements BucketStorageBatch { last_checkpoint_lsn: string | null, no_checkpoint_before_lsn: string ) { - this.db = db; + super(); this.client = db.client; - this.sync_rules = sync_rules; + this.db = db; this.group_id = group_id; - this.slot_name = slot_name; - this.session = this.client.startSession(); this.last_checkpoint_lsn = last_checkpoint_lsn; this.no_checkpoint_before_lsn = no_checkpoint_before_lsn; + this.session = this.client.startSession(); + this.slot_name = slot_name; + this.sync_rules = sync_rules; + } + + addCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): void { + this.write_checkpoint_batch.push({ + ...checkpoint, + sync_rules_id: this.group_id + }); } async flush(): Promise { @@ -83,6 +100,8 @@ export class MongoBucketBatch implements BucketStorageBatch { result = r; } } + await batchCreateCustomWriteCheckpoints(this.db, this.write_checkpoint_batch); + this.write_checkpoint_batch = []; return result; } @@ -528,8 +547,9 @@ export class MongoBucketBatch implements BucketStorageBatch { }); } - async abort() { + async [Symbol.asyncDispose]() { await this.session.endSession(); + super[Symbol.dispose](); } async commit(lsn: string): Promise { @@ -546,26 +566,29 @@ export class MongoBucketBatch implements BucketStorageBatch { return false; } + const now = new Date(); + const update: Partial = { + last_checkpoint_lsn: lsn, + last_checkpoint_ts: now, + last_keepalive_ts: now, + snapshot_done: true, + last_fatal_error: null + }; + if (this.persisted_op != null) { - const now = new Date(); - await this.db.sync_rules.updateOne( - { - _id: this.group_id - }, - { - $set: { - last_checkpoint: this.persisted_op, - last_checkpoint_lsn: lsn, - last_checkpoint_ts: now, - last_keepalive_ts: now, - snapshot_done: true, - last_fatal_error: null - } - }, - { session: this.session } - ); - this.persisted_op = null; + update.last_checkpoint = this.persisted_op; } + + await this.db.sync_rules.updateOne( + { + _id: this.group_id + }, + { + $set: update + }, + { session: this.session } + ); + this.persisted_op = null; this.last_checkpoint_lsn = lsn; return true; } @@ -606,6 +629,29 @@ export class MongoBucketBatch implements BucketStorageBatch { } async save(record: SaveOptions): Promise { + const { after, before, sourceTable, tag } = record; + for (const event of this.getTableEvents(sourceTable)) { + this.iterateListeners((cb) => + cb.replicationEvent?.({ + batch: this, + table: sourceTable, + data: { + op: tag, + after: after && util.isCompleteRow(after) ? after : undefined, + before: before && util.isCompleteRow(before) ? before : undefined + }, + event + }) + ); + } + + /** + * Return if the table is just an event table + */ + if (!sourceTable.syncData && !sourceTable.syncParameters) { + return null; + } + logger.debug(`Saving ${record.tag}:${record.before?.id}/${record.after?.id}`); this.batch ??= new OperationBatch(); @@ -754,6 +800,15 @@ export class MongoBucketBatch implements BucketStorageBatch { return copy; }); } + + /** + * Gets relevant {@link SqlEventDescriptor}s for the given {@link SourceTable} + */ + protected getTableEvents(table: SourceTable): SqlEventDescriptor[] { + return this.sync_rules.event_descriptors.filter((evt) => + [...evt.getSourceTables()].some((sourceTable) => sourceTable.matches(table)) + ); + } } export function currentBucketKey(b: CurrentBucket) { diff --git a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts index e5af38922..ef16900eb 100644 --- a/packages/service-core/src/storage/mongo/MongoStorageProvider.ts +++ b/packages/service-core/src/storage/mongo/MongoStorageProvider.ts @@ -1,8 +1,8 @@ +import { logger } from '@powersync/lib-services-framework'; import * as db from '../../db/db-index.js'; import { MongoBucketStorage } from '../MongoBucketStorage.js'; -import { BucketStorageProvider, ActiveStorage, GetStorageOptions } from '../StorageProvider.js'; +import { ActiveStorage, BucketStorageProvider, GetStorageOptions } from '../StorageProvider.js'; import { PowerSyncMongo } from './db.js'; -import { logger } from '@powersync/lib-services-framework'; export class MongoStorageProvider implements BucketStorageProvider { get type() { @@ -19,7 +19,8 @@ export class MongoStorageProvider implements BucketStorageProvider { return { storage: new MongoBucketStorage(database, { // TODO currently need the entire resolved config due to this - slot_name_prefix: resolvedConfig.slot_name_prefix + slot_name_prefix: resolvedConfig.slot_name_prefix, + write_checkpoint_mode: options.writeCheckpointMode }), shutDown: () => client.close(), tearDown: () => { diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 3844f5b22..71ab4a531 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -2,6 +2,7 @@ import { SqliteJsonRow, SqliteJsonValue, SqlSyncRules } from '@powersync/service import * as bson from 'bson'; import * as mongo from 'mongodb'; +import { DisposableObserver } from '@powersync/lib-services-framework'; import * as db from '../../db/db-index.js'; import * as util from '../../util/util-index.js'; import { @@ -12,13 +13,13 @@ import { DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES, FlushedResult, ParseSyncRulesOptions, - PersistedSyncRules, PersistedSyncRulesContent, ResolveTableOptions, ResolveTableResult, StartBatchOptions, SyncBucketDataBatch, SyncRulesBucketStorage, + SyncRulesBucketStorageListener, SyncRuleStatus, TerminateOptions } from '../BucketStorage.js'; @@ -31,7 +32,10 @@ import { MongoBucketBatch } from './MongoBucketBatch.js'; import { MongoCompactor } from './MongoCompactor.js'; import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js'; -export class MongoSyncBucketStorage implements SyncRulesBucketStorage { +export class MongoSyncBucketStorage + extends DisposableObserver + implements SyncRulesBucketStorage +{ private readonly db: PowerSyncMongo; private checksumCache = new ChecksumCache({ fetchChecksums: (batch) => { @@ -47,6 +51,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { private readonly sync_rules: PersistedSyncRulesContent, public readonly slot_name: string ) { + super(); this.db = factory.db; } @@ -79,7 +84,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { ); const checkpoint_lsn = doc?.last_checkpoint_lsn ?? null; - const batch = new MongoBucketBatch( + await using batch = new MongoBucketBatch( this.db, this.sync_rules.parsed(options).sync_rules, this.group_id, @@ -87,18 +92,14 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { checkpoint_lsn, doc?.no_checkpoint_before ?? options.zeroLSN ); - try { - await callback(batch); - await batch.flush(); - await batch.abort(); - if (batch.last_flushed_op) { - return { flushed_op: String(batch.last_flushed_op) }; - } else { - return null; - } - } catch (e) { - await batch.abort(); - throw e; + this.iterateListeners((cb) => cb.batchStarted?.(batch)); + + await callback(batch); + await batch.flush(); + if (batch.last_flushed_op) { + return { flushed_op: String(batch.last_flushed_op) }; + } else { + return null; } } @@ -150,6 +151,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage { replicationColumns, doc.snapshot_done ?? true ); + sourceTable.syncEvent = options.sync_rules.tableTriggersEvent(sourceTable); sourceTable.syncData = options.sync_rules.tableSyncsData(sourceTable); sourceTable.syncParameters = options.sync_rules.tableSyncsParameters(sourceTable); diff --git a/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts b/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts new file mode 100644 index 000000000..230db3153 --- /dev/null +++ b/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts @@ -0,0 +1,136 @@ +import * as framework from '@powersync/lib-services-framework'; +import { + CustomWriteCheckpointFilters, + CustomWriteCheckpointOptions, + LastWriteCheckpointFilters, + ManagedWriteCheckpointFilters, + ManagedWriteCheckpointOptions, + WriteCheckpointAPI, + WriteCheckpointMode +} from '../write-checkpoint.js'; +import { PowerSyncMongo } from './db.js'; + +export type MongoCheckpointAPIOptions = { + db: PowerSyncMongo; + mode: WriteCheckpointMode; +}; + +export class MongoWriteCheckpointAPI implements WriteCheckpointAPI { + readonly db: PowerSyncMongo; + readonly mode: WriteCheckpointMode; + + constructor(options: MongoCheckpointAPIOptions) { + this.db = options.db; + this.mode = options.mode; + } + + async batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise { + return batchCreateCustomWriteCheckpoints(this.db, checkpoints); + } + + async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise { + if (this.mode !== WriteCheckpointMode.CUSTOM) { + throw new framework.errors.ValidationError( + `Creating a custom Write Checkpoint when the current Write Checkpoint mode is set to "${this.mode}"` + ); + } + + const { checkpoint, user_id, sync_rules_id } = options; + const doc = await this.db.custom_write_checkpoints.findOneAndUpdate( + { + user_id: user_id, + sync_rules_id + }, + { + $set: { + checkpoint + } + }, + { upsert: true, returnDocument: 'after' } + ); + return doc!.checkpoint; + } + + async createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise { + if (this.mode !== WriteCheckpointMode.MANAGED) { + throw new framework.errors.ValidationError( + `Creating a managed Write Checkpoint when the current Write Checkpoint mode is set to "${this.mode}"` + ); + } + + const { user_id, heads: lsns } = checkpoint; + const doc = await this.db.write_checkpoints.findOneAndUpdate( + { + user_id: user_id + }, + { + $set: { + lsns + }, + $inc: { + client_id: 1n + } + }, + { upsert: true, returnDocument: 'after' } + ); + return doc!.client_id; + } + + async lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise { + switch (this.mode) { + case WriteCheckpointMode.CUSTOM: + if (false == 'sync_rules_id' in filters) { + throw new framework.errors.ValidationError(`Sync rules ID is required for custom Write Checkpoint filtering`); + } + return this.lastCustomWriteCheckpoint(filters); + case WriteCheckpointMode.MANAGED: + if (false == 'heads' in filters) { + throw new framework.errors.ValidationError( + `Replication HEAD is required for managed Write Checkpoint filtering` + ); + } + return this.lastManagedWriteCheckpoint(filters); + } + } + + protected async lastCustomWriteCheckpoint(filters: CustomWriteCheckpointFilters) { + const { user_id, sync_rules_id } = filters; + const lastWriteCheckpoint = await this.db.custom_write_checkpoints.findOne({ + user_id, + sync_rules_id + }); + return lastWriteCheckpoint?.checkpoint ?? null; + } + + protected async lastManagedWriteCheckpoint(filters: ManagedWriteCheckpointFilters) { + const { user_id } = filters; + const lastWriteCheckpoint = await this.db.write_checkpoints.findOne({ + user_id: user_id + }); + return lastWriteCheckpoint?.client_id ?? null; + } +} + +export async function batchCreateCustomWriteCheckpoints( + db: PowerSyncMongo, + checkpoints: CustomWriteCheckpointOptions[] +): Promise { + if (!checkpoints.length) { + return; + } + + await db.custom_write_checkpoints.bulkWrite( + checkpoints.map((checkpointOptions) => ({ + updateOne: { + filter: { user_id: checkpointOptions.user_id, sync_rules_id: checkpointOptions.sync_rules_id }, + update: { + $set: { + checkpoint: checkpointOptions.checkpoint, + sync_rules_id: checkpointOptions.sync_rules_id + } + }, + upsert: true + } + })) + ); +} diff --git a/packages/service-core/src/storage/mongo/db.ts b/packages/service-core/src/storage/mongo/db.ts index 1cc3f8471..dddfdf918 100644 --- a/packages/service-core/src/storage/mongo/db.ts +++ b/packages/service-core/src/storage/mongo/db.ts @@ -1,11 +1,13 @@ import * as mongo from 'mongodb'; +import { configFile } from '@powersync/service-types'; import * as db from '../../db/db-index.js'; import * as locks from '../../locks/locks-index.js'; import { BucketDataDocument, BucketParameterDocument, CurrentDataDocument, + CustomWriteCheckpointDocument, IdSequenceDocument, InstanceDocument, SourceTableDocument, @@ -13,7 +15,6 @@ import { WriteCheckpointDocument } from './models.js'; import { BSON_DESERIALIZE_OPTIONS } from './util.js'; -import { configFile } from '@powersync/service-types'; export interface PowerSyncMongoOptions { /** @@ -33,6 +34,7 @@ export class PowerSyncMongo { readonly op_id_sequence: mongo.Collection; readonly sync_rules: mongo.Collection; readonly source_tables: mongo.Collection; + readonly custom_write_checkpoints: mongo.Collection; readonly write_checkpoints: mongo.Collection; readonly instance: mongo.Collection; readonly locks: mongo.Collection; @@ -54,6 +56,7 @@ export class PowerSyncMongo { this.op_id_sequence = db.collection('op_id_sequence'); this.sync_rules = db.collection('sync_rules'); this.source_tables = db.collection('source_tables'); + this.custom_write_checkpoints = db.collection('custom_write_checkpoints'); this.write_checkpoints = db.collection('write_checkpoints'); this.instance = db.collection('instance'); this.locks = this.db.collection('locks'); diff --git a/packages/service-core/src/storage/mongo/models.ts b/packages/service-core/src/storage/mongo/models.ts index fa52a37da..a85886c4e 100644 --- a/packages/service-core/src/storage/mongo/models.ts +++ b/packages/service-core/src/storage/mongo/models.ts @@ -1,5 +1,5 @@ -import * as bson from 'bson'; import { SqliteJsonValue } from '@powersync/service-sync-rules'; +import * as bson from 'bson'; /** * Replica id uniquely identifying a row on the source database. @@ -159,6 +159,13 @@ export interface SyncRuleDocument { content: string; } +export interface CustomWriteCheckpointDocument { + _id: bson.ObjectId; + user_id: string; + checkpoint: bigint; + sync_rules_id: number; +} + export interface WriteCheckpointDocument { _id: bson.ObjectId; user_id: string; diff --git a/packages/service-core/src/storage/storage-index.ts b/packages/service-core/src/storage/storage-index.ts index 3c137b8d1..076248882 100644 --- a/packages/service-core/src/storage/storage-index.ts +++ b/packages/service-core/src/storage/storage-index.ts @@ -1,5 +1,6 @@ export * from './BucketStorage.js'; export * from './MongoBucketStorage.js'; +export * from './ReplicationEventPayload.js'; export * from './SourceEntity.js'; export * from './SourceTable.js'; export * from './StorageEngine.js'; @@ -16,3 +17,4 @@ export * from './mongo/MongoSyncRulesLock.js'; export * from './mongo/OperationBatch.js'; export * from './mongo/PersistedBatch.js'; export * from './mongo/util.js'; +export * from './write-checkpoint.js'; diff --git a/packages/service-core/src/storage/write-checkpoint.ts b/packages/service-core/src/storage/write-checkpoint.ts new file mode 100644 index 000000000..0b61fe0c1 --- /dev/null +++ b/packages/service-core/src/storage/write-checkpoint.ts @@ -0,0 +1,67 @@ +export enum WriteCheckpointMode { + /** + * Raw mappings of `user_id` to `write_checkpoint`s should + * be supplied for each set of sync rules. + */ + CUSTOM = 'manual', + /** + * Write checkpoints are stored as a mapping of `user_id` plus + * replication HEAD (lsn in Postgres) to an automatically generated + * incrementing `write_checkpoint` (stored as`client_id`). + */ + MANAGED = 'managed' +} + +export interface BaseWriteCheckpointIdentifier { + /** + * Identifier for User's account. + */ + user_id: string; +} + +export interface CustomWriteCheckpointFilters extends BaseWriteCheckpointIdentifier { + /** + * Sync rules which were active when this checkpoint was created. + */ + sync_rules_id: number; +} + +export interface CustomWriteCheckpointOptions extends CustomWriteCheckpointFilters { + /** + * A supplied incrementing Write Checkpoint number + */ + checkpoint: bigint; +} + +/** + * Options for creating a custom Write Checkpoint in a batch. + * A {@link BucketStorageBatch} is already associated with a Sync Rules instance. + * The `sync_rules_id` is not required here. + */ +export type BatchedCustomWriteCheckpointOptions = Omit; + +/** + * Managed Write Checkpoints are a mapping of User ID to replication HEAD + */ +export interface ManagedWriteCheckpointFilters extends BaseWriteCheckpointIdentifier { + /** + * Replication HEAD(s) at the creation of the checkpoint. + */ + heads: Record; +} + +export type ManagedWriteCheckpointOptions = ManagedWriteCheckpointFilters; + +export type LastWriteCheckpointFilters = CustomWriteCheckpointFilters | ManagedWriteCheckpointFilters; + +export interface WriteCheckpointAPI { + batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise; + + createCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): Promise; + + createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise; + + lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise; +} + +export const DEFAULT_WRITE_CHECKPOINT_MODE = WriteCheckpointMode.MANAGED; diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index 103ff4b4d..8eceacf40 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -1,10 +1,6 @@ -import { - BucketDataBatchOptions, - ParseSyncRulesOptions, - PersistedSyncRulesContent, - StartBatchOptions -} from '@/storage/BucketStorage.js'; -import { RequestParameters, SqlSyncRules } from '@powersync/service-sync-rules'; +import { BucketDataBatchOptions } from '@/storage/BucketStorage.js'; +import { getUuidReplicaIdentityBson } from '@/util/util-index.js'; +import { RequestParameters } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { fromAsync, oneFromAsync } from './stream_utils.js'; import { @@ -16,10 +12,8 @@ import { PARSE_OPTIONS, rid, StorageFactory, - testRules, - ZERO_LSN + testRules } from './util.js'; -import { getUuidReplicaIdentityBson } from '@/util/util-index.js'; const TEST_TABLE = makeTestTable('test', ['id']); @@ -1406,4 +1400,42 @@ bucket_definitions: expect(getBatchMeta(batch3)).toEqual(null); }); + + test('batch should be disposed automatically', async () => { + const sync_rules = testRules(` + bucket_definitions: + global: + data: [] + `); + + const storage = (await factory()).getInstance(sync_rules); + + let isDisposed = false; + await storage.startBatch(BATCH_OPTIONS, async (batch) => { + batch.registerListener({ + disposed: () => { + isDisposed = true; + } + }); + }); + expect(isDisposed).true; + + isDisposed = false; + let errorCaught = false; + try { + await storage.startBatch(BATCH_OPTIONS, async (batch) => { + batch.registerListener({ + disposed: () => { + isDisposed = true; + } + }); + throw new Error(`Testing exceptions`); + }); + } catch (ex) { + errorCaught = true; + expect(ex.message.includes('Testing')).true; + } + expect(errorCaught).true; + expect(isDisposed).true; + }); } diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 5acdd2f2e..cd8c06f2c 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -11,11 +11,10 @@ import { SourceTable } from '@/storage/SourceTable.js'; import { PowerSyncMongo } from '@/storage/mongo/db.js'; import { SyncBucketData } from '@/util/protocol-types.js'; import { getUuidReplicaIdentityBson, hashData } from '@/util/utils.js'; +import { SqlSyncRules } from '@powersync/service-sync-rules'; import * as bson from 'bson'; import * as mongo from 'mongodb'; import { env } from './env.js'; -import { SqlSyncRules } from '@powersync/service-sync-rules'; -import { ReplicaId } from '@/storage/storage-index.js'; // The metrics need to be initialised before they can be used await Metrics.initialise({ diff --git a/packages/sync-rules/.gitignore b/packages/sync-rules/.gitignore new file mode 100644 index 000000000..b73759d7b --- /dev/null +++ b/packages/sync-rules/.gitignore @@ -0,0 +1 @@ +schema/ \ No newline at end of file diff --git a/packages/sync-rules/package.json b/packages/sync-rules/package.json index de6e1f4de..ffe53c975 100644 --- a/packages/sync-rules/package.json +++ b/packages/sync-rules/package.json @@ -9,13 +9,16 @@ "access": "public" }, "files": [ - "dist/**/*" + "dist/**/*", + "schema/*" ], "type": "module", "scripts": { "clean": "rm -r ./dist && tsc -b --clean", "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", + "compile:schema": "pnpm build && node scripts/compile-schema.js", + "postversion": "pnpm compile:schema", "test": "vitest" }, "dependencies": { diff --git a/packages/sync-rules/scripts/compile-schema.js b/packages/sync-rules/scripts/compile-schema.js new file mode 100644 index 000000000..267de50d5 --- /dev/null +++ b/packages/sync-rules/scripts/compile-schema.js @@ -0,0 +1,11 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { syncRulesSchema } from '../dist/json_schema.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const schemaDir = path.join(__dirname, '../schema'); + +fs.mkdirSync(schemaDir, { recursive: true }); + +fs.writeFileSync(path.join(schemaDir, 'sync_rules.json'), JSON.stringify(syncRulesSchema, null, '\t')); diff --git a/packages/sync-rules/src/BaseSqlDataQuery.ts b/packages/sync-rules/src/BaseSqlDataQuery.ts new file mode 100644 index 000000000..342314d9b --- /dev/null +++ b/packages/sync-rules/src/BaseSqlDataQuery.ts @@ -0,0 +1,125 @@ +import { SelectedColumn } from 'pgsql-ast-parser'; +import { SqlRuleError } from './errors.js'; +import { ColumnDefinition } from './ExpressionType.js'; +import { SourceTableInterface } from './SourceTableInterface.js'; +import { SqlTools } from './sql_filters.js'; +import { TablePattern } from './TablePattern.js'; +import { QueryParameters, QuerySchema, SourceSchema, SourceSchemaTable, SqliteJsonRow, SqliteRow } from './types.js'; +import { filterJsonRow } from './utils.js'; + +export interface RowValueExtractor { + extract(tables: QueryParameters, into: SqliteRow): void; + getTypes(schema: QuerySchema, into: Record): void; +} + +export class BaseSqlDataQuery { + sourceTable?: TablePattern; + table?: string; + sql?: string; + columns?: SelectedColumn[]; + extractors: RowValueExtractor[] = []; + descriptor_name?: string; + bucket_parameters?: string[]; + tools?: SqlTools; + + ruleId?: string; + + errors: SqlRuleError[] = []; + + constructor() {} + + applies(table: SourceTableInterface) { + return this.sourceTable?.matches(table); + } + + addSpecialParameters(table: SourceTableInterface, row: SqliteRow) { + if (this.sourceTable!.isWildcard) { + return { + ...row, + _table_suffix: this.sourceTable!.suffix(table.table) + }; + } else { + return row; + } + } + + getOutputName(sourceTable: string) { + if (this.isUnaliasedWildcard()) { + // Wildcard without alias - use source + return sourceTable; + } else { + return this.table!; + } + } + + isUnaliasedWildcard() { + return this.sourceTable!.isWildcard && this.table == this.sourceTable!.tablePattern; + } + + columnOutputNames(): string[] { + return this.columns!.map((c) => { + return this.tools!.getOutputName(c); + }); + } + + getColumnOutputs(schema: SourceSchema): { name: string; columns: ColumnDefinition[] }[] { + let result: { name: string; columns: ColumnDefinition[] }[] = []; + + if (this.isUnaliasedWildcard()) { + // Separate results + for (let schemaTable of schema.getTables(this.sourceTable!)) { + let output: Record = {}; + + this.getColumnOutputsFor(schemaTable, output); + + result.push({ + name: this.getOutputName(schemaTable.table), + columns: Object.values(output) + }); + } + } else { + // Merged results + let output: Record = {}; + for (let schemaTable of schema.getTables(this.sourceTable!)) { + this.getColumnOutputsFor(schemaTable, output); + } + result.push({ + name: this.table!, + columns: Object.values(output) + }); + } + + return result; + } + + protected transformRow(tables: QueryParameters): SqliteJsonRow { + let result: SqliteRow = {}; + for (let extractor of this.extractors) { + extractor.extract(tables, result); + } + return filterJsonRow(result); + } + + protected getColumnOutputsFor(schemaTable: SourceSchemaTable, output: Record) { + const querySchema: QuerySchema = { + getColumn: (table, column) => { + if (table == this.table!) { + return schemaTable.getColumn(column); + } else { + // TODO: bucket parameters? + return undefined; + } + }, + getColumns: (table) => { + if (table == this.table!) { + return schemaTable.getColumns(); + } else { + return []; + } + } + }; + for (let extractor of this.extractors) { + extractor.getTypes(querySchema, output); + } + } +} diff --git a/packages/sync-rules/src/SqlDataQuery.ts b/packages/sync-rules/src/SqlDataQuery.ts index 9f15d00db..6f082a96a 100644 --- a/packages/sync-rules/src/SqlDataQuery.ts +++ b/packages/sync-rules/src/SqlDataQuery.ts @@ -1,32 +1,21 @@ import { JSONBig } from '@powersync/service-jsonbig'; -import { parse, SelectedColumn } from 'pgsql-ast-parser'; +import { parse } from 'pgsql-ast-parser'; +import { BaseSqlDataQuery } from './BaseSqlDataQuery.js'; import { SqlRuleError } from './errors.js'; -import { ColumnDefinition, ExpressionType } from './ExpressionType.js'; +import { ExpressionType } from './ExpressionType.js'; import { SourceTableInterface } from './SourceTableInterface.js'; import { SqlTools } from './sql_filters.js'; import { castAsText } from './sql_functions.js'; import { checkUnsupportedFeatures, isClauseError } from './sql_support.js'; +import { SyncRulesOptions } from './SqlSyncRules.js'; import { TablePattern } from './TablePattern.js'; -import { - EvaluationResult, - ParameterMatchClause, - QueryParameters, - QuerySchema, - SourceSchema, - SourceSchemaTable, - SqliteJsonRow, - SqliteRow -} from './types.js'; -import { filterJsonRow, getBucketId, isSelectStatement } from './utils.js'; import { TableQuerySchema } from './TableQuerySchema.js'; -import { SyncRulesOptions } from './SqlSyncRules.js'; +import { EvaluationResult, ParameterMatchClause, QuerySchema, SqliteRow } from './types.js'; +import { getBucketId, isSelectStatement } from './utils.js'; -interface RowValueExtractor { - extract(tables: QueryParameters, into: SqliteRow): void; - getTypes(schema: QuerySchema, into: Record): void; -} +export class SqlDataQuery extends BaseSqlDataQuery { + filter?: ParameterMatchClause; -export class SqlDataQuery { static fromSql(descriptor_name: string, bucket_parameters: string[], sql: string, options: SyncRulesOptions) { const parsed = parse(sql, { locationTracking: true }); const rows = new SqlDataQuery(); @@ -174,50 +163,6 @@ export class SqlDataQuery { return rows; } - sourceTable?: TablePattern; - table?: string; - sql?: string; - columns?: SelectedColumn[]; - extractors: RowValueExtractor[] = []; - filter?: ParameterMatchClause; - descriptor_name?: string; - bucket_parameters?: string[]; - tools?: SqlTools; - - ruleId?: string; - - errors: SqlRuleError[] = []; - - constructor() {} - - applies(table: SourceTableInterface) { - return this.sourceTable?.matches(table); - } - - addSpecialParameters(table: SourceTableInterface, row: SqliteRow) { - if (this.sourceTable!.isWildcard) { - return { - ...row, - _table_suffix: this.sourceTable!.suffix(table.table) - }; - } else { - return row; - } - } - - getOutputName(sourceTable: string) { - if (this.isUnaliasedWildcard()) { - // Wildcard without alias - use source - return sourceTable; - } else { - return this.table!; - } - } - - isUnaliasedWildcard() { - return this.sourceTable!.isWildcard && this.table == this.sourceTable!.tablePattern; - } - evaluateRow(table: SourceTableInterface, row: SqliteRow): EvaluationResult[] { try { const tables = { [this.table!]: this.addSpecialParameters(table, row) }; @@ -252,71 +197,4 @@ export class SqlDataQuery { return [{ error: e.message ?? `Evaluating data query failed` }]; } } - - private transformRow(tables: QueryParameters): SqliteJsonRow { - let result: SqliteRow = {}; - for (let extractor of this.extractors) { - extractor.extract(tables, result); - } - return filterJsonRow(result); - } - - columnOutputNames(): string[] { - return this.columns!.map((c) => { - return this.tools!.getOutputName(c); - }); - } - - getColumnOutputs(schema: SourceSchema): { name: string; columns: ColumnDefinition[] }[] { - let result: { name: string; columns: ColumnDefinition[] }[] = []; - - if (this.isUnaliasedWildcard()) { - // Separate results - for (let schemaTable of schema.getTables(this.sourceTable!)) { - let output: Record = {}; - - this.getColumnOutputsFor(schemaTable, output); - - result.push({ - name: this.getOutputName(schemaTable.table), - columns: Object.values(output) - }); - } - } else { - // Merged results - let output: Record = {}; - for (let schemaTable of schema.getTables(this.sourceTable!)) { - this.getColumnOutputsFor(schemaTable, output); - } - result.push({ - name: this.table!, - columns: Object.values(output) - }); - } - - return result; - } - - private getColumnOutputsFor(schemaTable: SourceSchemaTable, output: Record) { - const querySchema: QuerySchema = { - getColumn: (table, column) => { - if (table == this.table!) { - return schemaTable.getColumn(column); - } else { - // TODO: bucket parameters? - return undefined; - } - }, - getColumns: (table) => { - if (table == this.table!) { - return schemaTable.getColumns(); - } else { - return []; - } - } - }; - for (let extractor of this.extractors) { - extractor.getTypes(querySchema, output); - } - } } diff --git a/packages/sync-rules/src/SqlSyncRules.ts b/packages/sync-rules/src/SqlSyncRules.ts index 10c0de3b4..ca5f05d85 100644 --- a/packages/sync-rules/src/SqlSyncRules.ts +++ b/packages/sync-rules/src/SqlSyncRules.ts @@ -1,5 +1,6 @@ -import { LineCounter, parseDocument, Scalar, YAMLMap, YAMLSeq } from 'yaml'; +import { isScalar, LineCounter, parseDocument, Scalar, YAMLMap, YAMLSeq } from 'yaml'; import { SqlRuleError, SyncRulesErrors, YamlError } from './errors.js'; +import { SqlEventDescriptor } from './events/SqlEventDescriptor.js'; import { IdSequence } from './IdSequence.js'; import { validateSyncRulesSchema } from './json_schema.js'; import { SourceTableInterface } from './SourceTableInterface.js'; @@ -39,6 +40,7 @@ export interface SyncRulesOptions { export class SqlSyncRules implements SyncRules { bucket_descriptors: SqlBucketDescriptor[] = []; + event_descriptors: SqlEventDescriptor[] = []; idSequence = new IdSequence(); content: string; @@ -145,6 +147,35 @@ export class SqlSyncRules implements SyncRules { rules.bucket_descriptors.push(descriptor); } + const eventMap = parsed.get('event_definitions') as YAMLMap; + for (const event of eventMap?.items ?? []) { + const { key, value } = event as { key: Scalar; value: YAMLSeq }; + + if (false == value instanceof YAMLMap) { + rules.errors.push(new YamlError(new Error(`Event definitions must be objects.`))); + continue; + } + + const payloads = value.get('payloads') as YAMLSeq; + if (false == payloads instanceof YAMLSeq) { + rules.errors.push(new YamlError(new Error(`Event definition payloads must be an array.`))); + continue; + } + + const eventDescriptor = new SqlEventDescriptor(key.toString(), rules.idSequence); + for (let item of payloads.items) { + if (!isScalar(item)) { + rules.errors.push(new YamlError(new Error(`Payload queries for events must be scalar.`))); + continue; + } + rules.withScalar(item, (q) => { + return eventDescriptor.addSourceQuery(q, options); + }); + } + + rules.event_descriptors.push(eventDescriptor); + } + // Validate that there are no additional properties. // Since these errors don't contain line numbers, do this last. const valid = validateSyncRulesSchema(parsed.toJSON()); @@ -290,18 +321,42 @@ export class SqlSyncRules implements SyncRules { } getSourceTables(): TablePattern[] { - let sourceTables = new Map(); - for (let bucket of this.bucket_descriptors) { - for (let r of bucket.getSourceTables()) { + const sourceTables = new Map(); + for (const bucket of this.bucket_descriptors) { + for (const r of bucket.getSourceTables()) { const key = `${r.connectionTag}.${r.schema}.${r.tablePattern}`; sourceTables.set(key, r); } } + + for (const event of this.event_descriptors) { + for (const r of event.getSourceTables()) { + const key = `${r.connectionTag}.${r.schema}.${r.tablePattern}`; + sourceTables.set(key, r); + } + } + return [...sourceTables.values()]; } + getEventTables(): TablePattern[] { + const eventTables = new Map(); + + for (const event of this.event_descriptors) { + for (const r of event.getSourceTables()) { + const key = `${r.connectionTag}.${r.schema}.${r.tablePattern}`; + eventTables.set(key, r); + } + } + return [...eventTables.values()]; + } + + tableTriggersEvent(table: SourceTableInterface): boolean { + return this.event_descriptors.some((bucket) => bucket.tableTriggersEvent(table)); + } + tableSyncsData(table: SourceTableInterface): boolean { - for (let bucket of this.bucket_descriptors) { + for (const bucket of this.bucket_descriptors) { if (bucket.tableSyncsData(table)) { return true; } diff --git a/packages/sync-rules/src/events/SqlEventDescriptor.ts b/packages/sync-rules/src/events/SqlEventDescriptor.ts new file mode 100644 index 000000000..a02cd42ef --- /dev/null +++ b/packages/sync-rules/src/events/SqlEventDescriptor.ts @@ -0,0 +1,65 @@ +import { SqlRuleError } from '../errors.js'; +import { IdSequence } from '../IdSequence.js'; +import { SourceTableInterface } from '../SourceTableInterface.js'; +import { QueryParseResult } from '../SqlBucketDescriptor.js'; +import { SyncRulesOptions } from '../SqlSyncRules.js'; +import { TablePattern } from '../TablePattern.js'; +import { EvaluateRowOptions } from '../types.js'; +import { EvaluatedEventRowWithErrors, SqlEventSourceQuery } from './SqlEventSourceQuery.js'; + +/** + * A sync rules event which is triggered from a SQL table change. + */ +export class SqlEventDescriptor { + name: string; + source_queries: SqlEventSourceQuery[] = []; + + constructor(name: string, public idSequence: IdSequence) { + this.name = name; + } + + addSourceQuery(sql: string, options: SyncRulesOptions): QueryParseResult { + const source = SqlEventSourceQuery.fromSql(this.name, sql, options); + + // Each source query should be for a unique table + const existingSourceQuery = this.source_queries.find((q) => q.table == source.table); + if (existingSourceQuery) { + return { + parsed: false, + errors: [new SqlRuleError('Each payload query should query a unique table', sql)] + }; + } + + source.ruleId = this.idSequence.nextId(); + this.source_queries.push(source); + + return { + parsed: true, + errors: source.errors + }; + } + + evaluateRowWithErrors(options: EvaluateRowOptions): EvaluatedEventRowWithErrors { + // There should only be 1 payload result per source query + const matchingQuery = this.source_queries.find((q) => q.applies(options.sourceTable)); + if (!matchingQuery) { + return { + errors: [{ error: `No marching source query found for table ${options.sourceTable.table}` }] + }; + } + + return matchingQuery.evaluateRowWithErrors(options.sourceTable, options.record); + } + + getSourceTables(): Set { + let result = new Set(); + for (let query of this.source_queries) { + result.add(query.sourceTable!); + } + return result; + } + + tableTriggersEvent(table: SourceTableInterface): boolean { + return this.source_queries.some((query) => query.applies(table)); + } +} diff --git a/packages/sync-rules/src/events/SqlEventSourceQuery.ts b/packages/sync-rules/src/events/SqlEventSourceQuery.ts new file mode 100644 index 000000000..c5ca1e1ab --- /dev/null +++ b/packages/sync-rules/src/events/SqlEventSourceQuery.ts @@ -0,0 +1,142 @@ +import { parse } from 'pgsql-ast-parser'; +import { BaseSqlDataQuery } from '../BaseSqlDataQuery.js'; +import { SqlRuleError } from '../errors.js'; +import { ExpressionType } from '../ExpressionType.js'; +import { SourceTableInterface } from '../SourceTableInterface.js'; +import { SqlTools } from '../sql_filters.js'; +import { checkUnsupportedFeatures, isClauseError } from '../sql_support.js'; +import { SyncRulesOptions } from '../SqlSyncRules.js'; +import { TablePattern } from '../TablePattern.js'; +import { TableQuerySchema } from '../TableQuerySchema.js'; +import { EvaluationError, QuerySchema, SqliteJsonRow, SqliteRow } from '../types.js'; +import { isSelectStatement } from '../utils.js'; + +export type EvaluatedEventSourceRow = { + data: SqliteJsonRow; + ruleId?: string; +}; + +export type EvaluatedEventRowWithErrors = { + result?: EvaluatedEventSourceRow; + errors: EvaluationError[]; +}; + +/** + * Defines how a Replicated Row is mapped to source parameters for events. + */ +export class SqlEventSourceQuery extends BaseSqlDataQuery { + static fromSql(descriptor_name: string, sql: string, options: SyncRulesOptions) { + const parsed = parse(sql, { locationTracking: true }); + const rows = new SqlEventSourceQuery(); + const schema = options.schema; + + if (parsed.length > 1) { + throw new SqlRuleError('Only a single SELECT statement is supported', sql, parsed[1]?._location); + } + const q = parsed[0]; + if (!isSelectStatement(q)) { + throw new SqlRuleError('Only SELECT statements are supported', sql, q._location); + } + + rows.errors.push(...checkUnsupportedFeatures(sql, q)); + + if (q.from == null || q.from.length != 1 || q.from[0].type != 'table') { + throw new SqlRuleError('Must SELECT from a single table', sql, q.from?.[0]._location); + } + + const tableRef = q.from?.[0].name; + if (tableRef?.name == null) { + throw new SqlRuleError('Must SELECT from a single table', sql, q.from?.[0]._location); + } + const alias: string = tableRef.alias ?? tableRef.name; + + const sourceTable = new TablePattern(tableRef.schema ?? options.defaultSchema, tableRef.name); + let querySchema: QuerySchema | undefined = undefined; + if (schema) { + const tables = schema.getTables(sourceTable); + if (tables.length == 0) { + const e = new SqlRuleError( + `Table ${sourceTable.schema}.${sourceTable.tablePattern} not found`, + sql, + q.from?.[0]?._location + ); + e.type = 'warning'; + + rows.errors.push(e); + } else { + querySchema = new TableQuerySchema(tables, alias); + } + } + + const tools = new SqlTools({ + table: alias, + parameter_tables: [], + value_tables: [alias], + sql, + schema: querySchema + }); + + rows.sourceTable = sourceTable; + rows.table = alias; + rows.sql = sql; + rows.descriptor_name = descriptor_name; + rows.columns = q.columns ?? []; + rows.tools = tools; + + for (let column of q.columns ?? []) { + const name = tools.getOutputName(column); + if (name != '*') { + const clause = tools.compileRowValueExtractor(column.expr); + if (isClauseError(clause)) { + // Error logged already + continue; + } + rows.extractors.push({ + extract: (tables, output) => { + output[name] = clause.evaluate(tables); + }, + getTypes(schema, into) { + const def = clause.getColumnDefinition(schema); + into[name] = { name, type: def?.type ?? ExpressionType.NONE, originalType: def?.originalType }; + } + }); + } else { + rows.extractors.push({ + extract: (tables, output) => { + const row = tables[alias]; + for (let key in row) { + if (key.startsWith('_')) { + continue; + } + output[key] ??= row[key]; + } + }, + getTypes(schema, into) { + for (let column of schema.getColumns(alias)) { + into[column.name] ??= column; + } + } + }); + } + } + rows.errors.push(...tools.errors); + return rows; + } + + evaluateRowWithErrors(table: SourceTableInterface, row: SqliteRow): EvaluatedEventRowWithErrors { + try { + const tables = { [this.table!]: this.addSpecialParameters(table, row) }; + + const data = this.transformRow(tables); + return { + result: { + data, + ruleId: this.ruleId + }, + errors: [] + }; + } catch (e) { + return { errors: [e.message ?? `Evaluating data query failed`] }; + } + } +} diff --git a/packages/sync-rules/src/index.ts b/packages/sync-rules/src/index.ts index d72a1e58c..877d52c37 100644 --- a/packages/sync-rules/src/index.ts +++ b/packages/sync-rules/src/index.ts @@ -1,20 +1,22 @@ +export * from './DartSchemaGenerator.js'; export * from './errors.js'; +export * from './events/SqlEventDescriptor.js'; +export * from './events/SqlEventSourceQuery.js'; +export * from './ExpressionType.js'; +export * from './generators.js'; export * from './IdSequence.js'; +export * from './JsLegacySchemaGenerator.js'; +export * from './json_schema.js'; +export * from './request_functions.js'; +export * from './SchemaGenerator.js'; export * from './SourceTableInterface.js'; export * from './sql_filters.js'; export * from './sql_functions.js'; +export * from './SqlDataQuery.js'; +export * from './SqlParameterQuery.js'; export * from './SqlSyncRules.js'; +export * from './StaticSchema.js'; export * from './TablePattern.js'; +export * from './TsSchemaGenerator.js'; export * from './types.js'; export * from './utils.js'; -export * from './SqlParameterQuery.js'; -export * from './json_schema.js'; -export * from './StaticSchema.js'; -export * from './ExpressionType.js'; -export * from './SchemaGenerator.js'; -export * from './DartSchemaGenerator.js'; -export * from './JsLegacySchemaGenerator.js'; -export * from './TsSchemaGenerator.js'; -export * from './generators.js'; -export * from './SqlDataQuery.js'; -export * from './request_functions.js'; diff --git a/packages/sync-rules/src/json_schema.ts b/packages/sync-rules/src/json_schema.ts index dfe614825..b2f9367a9 100644 --- a/packages/sync-rules/src/json_schema.ts +++ b/packages/sync-rules/src/json_schema.ts @@ -44,6 +44,35 @@ export const syncRulesSchema: ajvModule.Schema = { additionalProperties: false } } + }, + event_definitions: { + type: 'object', + description: 'Record of sync replication event definitions', + examples: [ + { + write_checkpoints: { + payloads: ['select user_id, client_id, checkpoint from checkpoints'] + } + } + ], + patternProperties: { + '.*': { + type: ['object'], + required: ['payloads'], + examples: [{ payloads: ['select user_id, client_id, checkpoint from checkpoints'] }], + properties: { + payloads: { + description: 'Queries which extract event payload fields from replicated table rows.', + type: 'array', + items: { + type: 'string' + } + }, + additionalProperties: false, + uniqueItems: true + } + } + } } }, required: ['bucket_definitions'], diff --git a/packages/sync-rules/src/sql_filters.ts b/packages/sync-rules/src/sql_filters.ts index 9313ffd28..5cf9961f3 100644 --- a/packages/sync-rules/src/sql_filters.ts +++ b/packages/sync-rules/src/sql_filters.ts @@ -1,7 +1,9 @@ -import { Expr, ExprRef, Name, NodeLocation, QName, QNameAliased, SelectedColumn, parse } from 'pgsql-ast-parser'; +import { JSONBig } from '@powersync/service-jsonbig'; +import { Expr, ExprRef, Name, NodeLocation, QName, QNameAliased, SelectedColumn } from 'pgsql-ast-parser'; import { nil } from 'pgsql-ast-parser/src/utils.js'; -import { ExpressionType, TYPE_NONE } from './ExpressionType.js'; +import { ExpressionType } from './ExpressionType.js'; import { SqlRuleError } from './errors.js'; +import { REQUEST_FUNCTIONS } from './request_functions.js'; import { BASIC_OPERATORS, OPERATOR_IN, @@ -13,6 +15,7 @@ import { SQL_FUNCTIONS, SqlFunction, castOperator, + getOperatorFunction, sqliteTypeOf } from './sql_functions.js'; import { @@ -20,7 +23,6 @@ import { SQLITE_TRUE, andFilters, compileStaticOperator, - getOperatorFunction, isClauseError, isParameterMatchClause, isParameterValueClause, @@ -44,8 +46,6 @@ import { TrueIfParametersMatch } from './types.js'; import { isJsonValue } from './utils.js'; -import { JSONBig } from '@powersync/service-jsonbig'; -import { REQUEST_FUNCTIONS } from './request_functions.js'; export const MATCH_CONST_FALSE: TrueIfParametersMatch = []; export const MATCH_CONST_TRUE: TrueIfParametersMatch = [{}]; diff --git a/packages/sync-rules/src/sql_functions.ts b/packages/sync-rules/src/sql_functions.ts index 715f1494f..ce419d49a 100644 --- a/packages/sync-rules/src/sql_functions.ts +++ b/packages/sync-rules/src/sql_functions.ts @@ -1,5 +1,5 @@ import { JSONBig } from '@powersync/service-jsonbig'; -import { getOperatorFunction, SQLITE_FALSE, SQLITE_TRUE, sqliteBool, sqliteNot } from './sql_support.js'; +import { SQLITE_FALSE, SQLITE_TRUE, sqliteBool, sqliteNot } from './sql_support.js'; import { SqliteValue } from './types.js'; import { jsonValueToSqlite } from './utils.js'; // Declares @syncpoint/wkx module @@ -44,6 +44,18 @@ export interface DocumentedSqlFunction extends SqlFunction { documentation?: string; } +export function getOperatorFunction(op: string): SqlFunction { + return { + debugName: `operator${op}`, + call(...args: SqliteValue[]) { + return evaluateOperator(op, args[0], args[1]); + }, + getReturnType(args) { + return getOperatorReturnType(op, args[0], args[1]); + } + }; +} + const upper: DocumentedSqlFunction = { debugName: 'upper', call(value: SqliteValue) { diff --git a/packages/sync-rules/src/sql_support.ts b/packages/sync-rules/src/sql_support.ts index e2b074c55..b64aacd4f 100644 --- a/packages/sync-rules/src/sql_support.ts +++ b/packages/sync-rules/src/sql_support.ts @@ -1,3 +1,8 @@ +import { SelectFromStatement } from 'pgsql-ast-parser'; +import { SqlRuleError } from './errors.js'; +import { ExpressionType } from './ExpressionType.js'; +import { MATCH_CONST_FALSE, MATCH_CONST_TRUE } from './sql_filters.js'; +import { evaluateOperator, getOperatorReturnType } from './sql_functions.js'; import { ClauseError, CompiledClause, @@ -6,16 +11,11 @@ import { ParameterMatchClause, ParameterValueClause, QueryParameters, - SqliteValue, RowValueClause, + SqliteValue, StaticValueClause, TrueIfParametersMatch } from './types.js'; -import { MATCH_CONST_FALSE, MATCH_CONST_TRUE } from './sql_filters.js'; -import { SqlFunction, evaluateOperator, getOperatorReturnType } from './sql_functions.js'; -import { SelectFromStatement } from 'pgsql-ast-parser'; -import { SqlRuleError } from './errors.js'; -import { ExpressionType } from './ExpressionType.js'; export function isParameterMatchClause(clause: CompiledClause): clause is ParameterMatchClause { return Array.isArray((clause as ParameterMatchClause).inputParameters); @@ -78,18 +78,6 @@ export function compileStaticOperator(op: string, left: RowValueClause, right: R }; } -export function getOperatorFunction(op: string): SqlFunction { - return { - debugName: `operator${op}`, - call(...args: SqliteValue[]) { - return evaluateOperator(op, args[0], args[1]); - }, - getReturnType(args) { - return getOperatorReturnType(op, args[0], args[1]); - } - }; -} - export function andFilters(a: CompiledClause, b: CompiledClause): CompiledClause { if (isRowValueClause(a) && isRowValueClause(b)) { // Optimization diff --git a/packages/sync-rules/test/src/sync_rules.test.ts b/packages/sync-rules/test/src/sync_rules.test.ts index 26ff3adb3..54d507440 100644 --- a/packages/sync-rules/test/src/sync_rules.test.ts +++ b/packages/sync-rules/test/src/sync_rules.test.ts @@ -1,12 +1,5 @@ import { describe, expect, test } from 'vitest'; -import { - DEFAULT_TAG, - DartSchemaGenerator, - JsLegacySchemaGenerator, - SqlSyncRules, - StaticSchema, - TsSchemaGenerator -} from '../../src/index.js'; +import { SqlSyncRules } from '../../src/index.js'; import { ASSETS, BASIC_SCHEMA, PARSE_OPTIONS, TestSourceTable, USERS, normalizeTokenParameters } from './util.js'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index beb4d92be..eac450a03 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,16 +19,16 @@ importers: version: 3.2.5 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 concurrently: specifier: ^8.2.2 version: 8.2.2 inquirer: specifier: ^9.2.7 - version: 9.2.22 + version: 9.3.5 npm-check-updates: specifier: ^17.1.2 - version: 17.1.2 + version: 17.1.3 prettier: specifier: ^3.3.3 version: 3.3.3 @@ -52,19 +52,19 @@ importers: version: 5.6.2 ws: specifier: ^8.2.3 - version: 8.2.3 + version: 8.18.0 libs/lib-services: dependencies: ajv: specifier: ^8.12.0 - version: 8.14.0 + version: 8.16.0 better-ajv-errors: specifier: ^1.2.0 - version: 1.2.0(ajv@8.14.0) + version: 1.2.0(ajv@8.16.0) bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -79,14 +79,14 @@ importers: version: 9.0.1 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 zod: specifier: ^3.23.8 version: 3.23.8 devDependencies: '@types/lodash': specifier: ^4.17.5 - version: 4.17.5 + version: 4.17.6 '@types/uuid': specifier: ^9.0.4 version: 9.0.8 @@ -116,7 +116,7 @@ importers: version: link:../../packages/types jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 @@ -138,7 +138,7 @@ importers: version: 5.6.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) + version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) vitest: specifier: ^2.1.1 version: 2.1.1(@types/node@22.5.5) @@ -183,7 +183,7 @@ importers: version: 9.0.1 ws: specifier: ^8.17.0 - version: 8.17.0 + version: 8.18.0 devDependencies: '@types/uuid': specifier: ^9.0.4 @@ -193,7 +193,7 @@ importers: version: 8.2.3 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 rsocket-websocket-client: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 @@ -220,7 +220,7 @@ importers: version: 0.51.1(@opentelemetry/api@1.8.0) '@opentelemetry/resources': specifier: ^1.24.1 - version: 1.24.1(@opentelemetry/api@1.8.0) + version: 1.25.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': specifier: 1.24.1 version: 1.24.1(@opentelemetry/api@1.8.0) @@ -247,7 +247,7 @@ importers: version: 0.5.0 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 commander: specifier: ^12.0.0 version: 12.1.0 @@ -262,16 +262,16 @@ importers: version: 5.0.0 jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 lodash: specifier: ^4.17.21 version: 4.17.21 lru-cache: specifier: ^10.2.2 - version: 10.2.2 + version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) + version: 6.8.0(socks@2.8.3) node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -283,17 +283,17 @@ importers: version: 9.0.1 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 yaml: specifier: ^2.3.2 - version: 2.4.2 + version: 2.4.5 devDependencies: '@types/async': specifier: ^3.2.24 version: 3.2.24 '@types/lodash': specifier: ^4.17.5 - version: 4.17.5 + version: 4.17.6 '@types/uuid': specifier: ^9.0.4 version: 9.0.8 @@ -308,7 +308,7 @@ importers: version: 5.6.2 vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)) + version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) vitest: specifier: ^2.1.1 version: 2.1.1(@types/node@22.5.5) @@ -323,13 +323,13 @@ importers: version: 0.5.2 ajv: specifier: ^8.12.0 - version: 8.14.0 + version: 8.16.0 pgsql-ast-parser: specifier: ^11.1.0 version: 11.2.0 yaml: specifier: ^2.3.1 - version: 2.4.2 + version: 2.4.5 devDependencies: '@types/node': specifier: ^22.5.5 @@ -387,13 +387,13 @@ importers: version: link:../packages/types '@sentry/node': specifier: ^8.9.2 - version: 8.9.2 + version: 8.17.0 async-mutex: specifier: ^0.5.0 version: 0.5.0 bson: specifier: ^6.6.0 - version: 6.7.0 + version: 6.8.0 commander: specifier: ^12.0.0 version: 12.1.0 @@ -411,13 +411,13 @@ importers: version: 5.0.0 jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 lru-cache: specifier: ^10.0.1 - version: 10.2.2 + version: 10.4.3 mongodb: specifier: ^6.7.0 - version: 6.7.0(socks@2.8.3) + version: 6.8.0(socks@2.8.3) node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -432,14 +432,14 @@ importers: version: 9.0.1 winston: specifier: ^3.13.0 - version: 3.13.0 + version: 3.13.1 yaml: specifier: ^2.3.2 - version: 2.4.2 + version: 2.4.5 devDependencies: '@sentry/types': specifier: ^8.9.2 - version: 8.9.2 + version: 8.17.0 '@types/uuid': specifier: ^9.0.4 version: 9.0.8 @@ -448,7 +448,7 @@ importers: version: 2.4.1 nodemon: specifier: ^3.0.1 - version: 3.1.1 + version: 3.1.4 npm-check-updates: specifier: ^16.14.4 version: 16.14.20 @@ -472,7 +472,7 @@ importers: version: 12.1.0 jose: specifier: ^4.15.1 - version: 4.15.5 + version: 4.15.9 yaml: specifier: ^2.5.0 version: 2.5.0 @@ -486,20 +486,20 @@ importers: packages: - '@babel/code-frame@7.24.6': - resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==} + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.24.6': - resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==} + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.6': - resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==} + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.24.6': - resolution: {integrity: sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==} + '@babel/runtime@7.24.8': + resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} '@changesets/apply-release-plan@7.0.5': @@ -572,146 +572,146 @@ packages: '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] - '@fastify/ajv-compiler@3.5.0': - resolution: {integrity: sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==} + '@fastify/ajv-compiler@3.6.0': + resolution: {integrity: sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==} '@fastify/cors@8.4.1': resolution: {integrity: sha512-iYQJtrY3pFiDS5mo5zRaudzg2OcUdJ96PD6xfkKOOEilly5nnrFZx/W6Sce2T79xxlEn2qpU3t5+qS2phS369w==} @@ -732,8 +732,8 @@ packages: resolution: {integrity: sha512-RE815I4arJFtt+FVeU1Tgp9/Xvecacji8w/V6XtXsWWH/wz/eNkNbhb+ny/+PlVZjV0rxQpRSQKNKE3lcktHEA==} engines: {node: '>=10.10.0'} - '@inquirer/figures@1.0.2': - resolution: {integrity: sha512-4F1MBwVr3c/m4bAUef6LgkvBfSjzwH+OfldgHqcuacWwSUetFebM2wi58WfG9uk1rR98U6GwLed4asLJbwdV5w==} + '@inquirer/figures@1.0.3': + resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} engines: {node: '>=18'} '@isaacs/cliui@8.0.2': @@ -744,9 +744,6 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} @@ -756,10 +753,6 @@ packages: '@js-sdsl/ordered-set@4.4.2': resolution: {integrity: sha512-ieYQ8WlBPKYzEo81H3q0DFbd8WtFRXXABb4+vRCF0AO3WWtJZFxYvRGdipUXGrd6tlSySmqhcPuO3J6SCodCxg==} - '@ljharb/through@2.3.13': - resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} - engines: {node: '>= 0.4'} - '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -819,8 +812,8 @@ packages: resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==} engines: {node: '>=14'} - '@opentelemetry/api-logs@0.52.0': - resolution: {integrity: sha512-HxjD7xH9iAE4OyhNaaSec65i1H6QZYBWSwWkowFfsc5YAcDvJG30/J1sRKXEQqdmUcKTXEAnA66UciqZha/4+Q==} + '@opentelemetry/api-logs@0.52.1': + resolution: {integrity: sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==} engines: {node: '>=14'} '@opentelemetry/api@1.6.0': @@ -835,8 +828,8 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-async-hooks@1.25.0': - resolution: {integrity: sha512-sBW313mnMyFg0cp/40BRzrZBWG+581s2j5gIsa5fgGadswyILk4mNFATsqrCOpAx945RDuZ2B7ThQLgor9OpfA==} + '@opentelemetry/context-async-hooks@1.25.1': + resolution: {integrity: sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -853,8 +846,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.9.0' - '@opentelemetry/core@1.25.0': - resolution: {integrity: sha512-n0B3s8rrqGrasTgNkXLKXzN0fXo+6IYP7M5b7AMsrZM33f/y6DS6kJ0Btd7SespASWq8bgL3taLo0oe0vB52IQ==} + '@opentelemetry/core@1.25.1': + resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -877,110 +870,104 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-connect@0.37.0': - resolution: {integrity: sha512-SeQktDIH5rNzjiEiazWiJAIXkmnLOnNV7wwHpahrqE0Ph+Z3heqMfxRtoMtbdJSIYLfcNZYO51AjxZ00IXufdw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-express@0.40.1': - resolution: {integrity: sha512-+RKMvVe2zw3kIXRup9c1jFu3T4d0fs5aKy015TpiMyoCKX1UMu3Z0lfgYtuyiSTANvg5hZnDbWmQmqSPj9VTvg==} + '@opentelemetry/instrumentation-connect@0.38.0': + resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-fastify@0.37.0': - resolution: {integrity: sha512-WRjwzNZgupSzbEYvo9s+QuHJRqZJjVdNxSEpGBwWK8RKLlHGwGVAu0gcc2gPamJWUJsGqPGvahAPWM18ZkWj6A==} + '@opentelemetry/instrumentation-express@0.41.0': + resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-graphql@0.41.0': - resolution: {integrity: sha512-R/gXeljgIhaRDKquVkKYT5QHPnFouM8ooyePZEP0kqyaVAedtR1V7NfAUJbxfTG5fBQa5wdmLjvu63+tzRXZCA==} + '@opentelemetry/instrumentation-fastify@0.38.0': + resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-hapi@0.39.0': - resolution: {integrity: sha512-ik2nA9Yj2s2ay+aNY+tJsKCsEx6Tsc2g/MK0iWBW5tibwrWKTy1pdVt5sB3kd5Gkimqj23UV5+FH2JFcQLeKug==} + '@opentelemetry/instrumentation-graphql@0.42.0': + resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-http@0.52.0': - resolution: {integrity: sha512-E6ywZuxTa4LnVXZGwL1oj3e2Eog1yIaNqa8KjKXoGkDNKte9/SjQnePXOmhQYI0A9nf0UyFbP9aKd+yHrkJXUA==} + '@opentelemetry/instrumentation-hapi@0.40.0': + resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-ioredis@0.41.0': - resolution: {integrity: sha512-rxiLloU8VyeJGm5j2fZS8ShVdB82n7VNP8wTwfUQqDwRfHCnkzGr+buKoxuhGD91gtwJ91RHkjHA1Eg6RqsUTg==} + '@opentelemetry/instrumentation-http@0.52.1': + resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-koa@0.41.0': - resolution: {integrity: sha512-mbPnDt7ELvpM2S0vixYUsde7122lgegLOJQxx8iJQbB8YHal/xnTh9v7IfArSVzIDo+E+080hxZyUZD4boOWkw==} + '@opentelemetry/instrumentation-ioredis@0.42.0': + resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongodb@0.45.0': - resolution: {integrity: sha512-xnZP9+ayeB1JJyNE9cIiwhOJTzNEsRhXVdLgfzmrs48Chhhk026mQdM5CITfyXSCfN73FGAIB8d91+pflJEfWQ==} + '@opentelemetry/instrumentation-koa@0.42.0': + resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongoose@0.39.0': - resolution: {integrity: sha512-J1r66A7zJklPPhMtrFOO7/Ud2p0Pv5u8+r23Cd1JUH6fYPmftNJVsLp2urAt6PHK4jVqpP/YegN8wzjJ2mZNPQ==} + '@opentelemetry/instrumentation-mongodb@0.46.0': + resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql2@0.39.0': - resolution: {integrity: sha512-Iypuq2z6TCfriAXCIZjRq8GTFCKhQv5SpXbmI+e60rYdXw8NHtMH4NXcGF0eKTuoCsC59IYSTUvDQYDKReaszA==} + '@opentelemetry/instrumentation-mongoose@0.40.0': + resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql@0.39.0': - resolution: {integrity: sha512-8snHPh83rhrDf31v9Kq0Nf+ts8hdr7NguuszRqZomZBHgE0+UyXZSkXHAAFZoBPPRMGyM68uaFE5hVtFl+wOcA==} + '@opentelemetry/instrumentation-mysql2@0.40.0': + resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-nestjs-core@0.38.0': - resolution: {integrity: sha512-M381Df1dM8aqihZz2yK+ugvMFK5vlHG/835dc67Sx2hH4pQEQYDA2PpFPTgc9AYYOydQaj7ClFQunESimjXDgg==} + '@opentelemetry/instrumentation-mysql@0.40.0': + resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-pg@0.42.0': - resolution: {integrity: sha512-sjgcM8CswYy8zxHgXv4RAZ09DlYhQ+9TdlourUs63Df/ek5RrB1ZbjznqW7PB6c3TyJJmX6AVtPTjAsROovEjA==} + '@opentelemetry/instrumentation-nestjs-core@0.39.0': + resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-redis-4@0.40.0': - resolution: {integrity: sha512-0ieQYJb6yl35kXA75LQUPhHtGjtQU9L85KlWa7d4ohBbk/iQKZ3X3CFl5jC5vNMq/GGPB3+w3IxNvALlHtrp7A==} + '@opentelemetry/instrumentation-pg@0.43.0': + resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.43.0': - resolution: {integrity: sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ==} + '@opentelemetry/instrumentation-redis-4@0.41.0': + resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.51.1': - resolution: {integrity: sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==} + '@opentelemetry/instrumentation@0.46.0': + resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.52.0': - resolution: {integrity: sha512-LPwSIrw+60cheWaXsfGL8stBap/AppKQJFE+qqRvzYrgttXFH2ofoIMxWadeqPTq4BYOXM/C7Bdh/T+B60xnlQ==} + '@opentelemetry/instrumentation@0.52.1': + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -1013,8 +1000,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.9.0' - '@opentelemetry/resources@1.25.0': - resolution: {integrity: sha512-iHjydPMYJ+Li1auveJCq2rp5U2h6Mhq8BidiyE0jfVlDTFyR1ny8AfJHfmFzJ/RAM8vT8L7T21kcmGybxZC7lQ==} + '@opentelemetry/resources@1.25.1': + resolution: {integrity: sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -1044,8 +1031,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.9.0' - '@opentelemetry/sdk-trace-base@1.25.0': - resolution: {integrity: sha512-6+g2fiRQUG39guCsKVeY8ToeuUf3YUnPkN6DXRA1qDmFLprlLvZm9cS6+chgbW70cZJ406FTtSCDnJwxDC5sGQ==} + '@opentelemetry/sdk-trace-base@1.25.1': + resolution: {integrity: sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -1058,8 +1045,8 @@ packages: resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==} engines: {node: '>=14'} - '@opentelemetry/semantic-conventions@1.25.0': - resolution: {integrity: sha512-M+kkXKRAIAiAP6qYyesfrC5TOmDpDVtsxuGfPcqd9B/iBrac+E14jYwrgm0yZBUIbIP2OnqC3j+UgkXLm1vxUQ==} + '@opentelemetry/semantic-conventions@1.25.1': + resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} engines: {node: '>=14'} '@opentelemetry/sql-common@0.40.1': @@ -1084,113 +1071,113 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@prisma/instrumentation@5.15.0': - resolution: {integrity: sha512-fCWOOOajTKOUEp43gRmBqwt6oN9bPJcLiloi2OG/2ED0N5z62Cuza6FDrlm3SJHQAXYlXqLE0HLdEE5WcUkOzg==} + '@prisma/instrumentation@5.16.1': + resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} - '@rollup/rollup-android-arm-eabi@4.18.0': - resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} + '@rollup/rollup-android-arm-eabi@4.18.1': + resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.18.0': - resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} + '@rollup/rollup-android-arm64@4.18.1': + resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.18.0': - resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} + '@rollup/rollup-darwin-arm64@4.18.1': + resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.18.0': - resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} + '@rollup/rollup-darwin-x64@4.18.1': + resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': + resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} + '@rollup/rollup-linux-arm-musleabihf@4.18.1': + resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.18.0': - resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} + '@rollup/rollup-linux-arm64-gnu@4.18.1': + resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.18.0': - resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} + '@rollup/rollup-linux-arm64-musl@4.18.1': + resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': + resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} + '@rollup/rollup-linux-riscv64-gnu@4.18.1': + resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.18.0': - resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} + '@rollup/rollup-linux-s390x-gnu@4.18.1': + resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.18.0': - resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} + '@rollup/rollup-linux-x64-gnu@4.18.1': + resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.18.0': - resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} + '@rollup/rollup-linux-x64-musl@4.18.1': + resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.18.0': - resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} + '@rollup/rollup-win32-arm64-msvc@4.18.1': + resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.18.0': - resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} + '@rollup/rollup-win32-ia32-msvc@4.18.1': + resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.18.0': - resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} + '@rollup/rollup-win32-x64-msvc@4.18.1': + resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} cpu: [x64] os: [win32] - '@sentry/core@8.9.2': - resolution: {integrity: sha512-ixm8NISFlPlEo3FjSaqmq4nnd13BRHoafwJ5MG+okCz6BKGZ1SexEggP42/QpGvDprUUHnfncG6WUMgcarr1zA==} + '@sentry/core@8.17.0': + resolution: {integrity: sha512-s62O0Re6WcvaVbH1IEeAWmj/ca8UhaRoFaDnc5TR68reOycBrgnqCNq3qHxBsELOA6NJowoK+T29DDGs9QVXhQ==} engines: {node: '>=14.18'} - '@sentry/node@8.9.2': - resolution: {integrity: sha512-Q+JBpR4yx3eUyyhwgugucfRtPg65gYvzJGEmjzcnDJXJqX8ms4HPpNv9o2Om7A4014JxIibUdrQ+p5idcT7SZA==} + '@sentry/node@8.17.0': + resolution: {integrity: sha512-HJ7B/zlpGMOIN+TnLzp6gbOpOzTk3Co19N39Y17T9MrR+5Z4eHdgEKWORFyE0Wy2KYKkVRwJ5zZJbfldc0EsEA==} engines: {node: '>=14.18'} - '@sentry/opentelemetry@8.9.2': - resolution: {integrity: sha512-Q6SHDQhrsBPcMi7ejqVdNTkt6SCTIhpGsFN8QR7daH3uvM0X2O7ciCuO9gRNRTEkflEINV4SBZEjANYH7BkRAg==} + '@sentry/opentelemetry@8.17.0': + resolution: {integrity: sha512-SKHfvHECIs7kqcXVRypXC6bQ7AQ4TTILamamZS5Ro1FP+i+yT8qEIoVWljoFZUIyO4J42mAP98THa1lCPK4BXA==} engines: {node: '>=14.18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/core': ^1.25.0 - '@opentelemetry/instrumentation': ^0.52.0 - '@opentelemetry/sdk-trace-base': ^1.25.0 - '@opentelemetry/semantic-conventions': ^1.25.0 + '@opentelemetry/core': ^1.25.1 + '@opentelemetry/instrumentation': ^0.52.1 + '@opentelemetry/sdk-trace-base': ^1.25.1 + '@opentelemetry/semantic-conventions': ^1.25.1 - '@sentry/types@8.9.2': - resolution: {integrity: sha512-+LFOyQGl+zk5SZRGZD2MEURf7i5RHgP/mt3s85Rza+vz8M211WJ0YsjkIGUJFSY842nged5QLx4JysLaBlLymg==} + '@sentry/types@8.17.0': + resolution: {integrity: sha512-v0nI0+ajiGTijhF1W/ryn2+zFVFr6VPn6lao3W4qKj9MlltIHa4/uuGzTaiCFwoPw7g5bZ1Q09SStpDXVMkz2A==} engines: {node: '>=14.18'} - '@sentry/utils@8.9.2': - resolution: {integrity: sha512-A4srR9mEBFdVXwSEKjQ94msUbVkMr8JeFiEj9ouOFORw/Y/ux/WV2bWVD/ZI9wq0TcTNK8L1wBgU8UMS5lIq3A==} + '@sentry/utils@8.17.0': + resolution: {integrity: sha512-HHtAPLOlvzhwgfYzxtuPnLUoGRMtMrFvopkii74zmx/1ZD4VN4PYPB2E5KFf3c18pTovw+kxF0ux6VrGiyAHsw==} engines: {node: '>=14.18'} '@sigstore/bundle@1.1.0': @@ -1244,62 +1231,20 @@ packages: resolution: {integrity: sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - '@types/accepts@1.3.7': - resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} - '@types/async@3.2.24': resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - '@types/body-parser@1.19.5': - resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} - '@types/connect@3.4.36': resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/content-disposition@0.5.8': - resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} - - '@types/cookies@0.9.0': - resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} - '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@types/express-serve-static-core@4.19.1': - resolution: {integrity: sha512-ej0phymbFLoCB26dbbq5PGScsf2JAJ4IJHjG10LalgUV36XKTmA4GdA+PVllKvRk0sEKt64X8975qFnkSi0hqA==} - - '@types/express@4.17.21': - resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} - - '@types/http-assert@1.5.5': - resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} - '@types/http-cache-semantics@4.0.4': resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} - '@types/http-errors@2.0.4': - resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - - '@types/keygrip@1.0.6': - resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} - - '@types/koa-compose@3.2.8': - resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} - - '@types/koa@2.14.0': - resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==} - - '@types/koa__router@12.0.3': - resolution: {integrity: sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==} - - '@types/lodash@4.17.5': - resolution: {integrity: sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==} - - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + '@types/lodash@4.17.6': + resolution: {integrity: sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==} '@types/mysql@2.15.22': resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} @@ -1322,26 +1267,14 @@ packages: '@types/pg@8.6.1': resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} - '@types/qs@6.9.15': - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/semver-utils@1.1.3': resolution: {integrity: sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==} '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - '@types/send@0.17.4': - resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} - - '@types/serve-static@1.15.7': - resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - - '@types/shimmer@1.0.5': - resolution: {integrity: sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==} + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} '@types/strip-bom@3.0.0': resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} @@ -1414,12 +1347,12 @@ packages: peerDependencies: acorn: ^8 - acorn-walk@8.3.2: - resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} + acorn-walk@8.3.3: + resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} engines: {node: '>=0.4.0'} - acorn@8.11.3: - resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==} + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1451,8 +1384,8 @@ packages: ajv: optional: true - ajv@8.14.0: - resolution: {integrity: sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==} + ajv@8.16.0: + resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -1564,8 +1497,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - bson@6.7.0: - resolution: {integrity: sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==} + bson@6.8.0: + resolution: {integrity: sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==} engines: {node: '>=16.20.1'} buffer-from@1.1.2: @@ -1597,10 +1530,6 @@ packages: resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} engines: {node: '>=14.16'} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - camelcase@7.0.1: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} @@ -1773,8 +1702,8 @@ packages: date-fns@3.6.0: resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + debug@4.3.5: + resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1810,10 +1739,6 @@ packages: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} @@ -1872,16 +1797,8 @@ packages: err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true @@ -1939,8 +1856,8 @@ packages: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} - fast-json-stringify@5.16.0: - resolution: {integrity: sha512-A4bg6E15QrkuVO3f0SwIASgzMzR6XC4qTyTqhf3hYXy0iazbAdZKwkE+ox4WgzKyzM6ygvbdq3r134UjOaaAnA==} + fast-json-stringify@5.16.1: + resolution: {integrity: sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==} fast-memoize@2.5.2: resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} @@ -1952,8 +1869,8 @@ packages: resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} engines: {node: '>=6'} - fast-uri@2.3.0: - resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} + fast-uri@2.4.0: + resolution: {integrity: sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==} fastify-plugin@4.5.1: resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} @@ -1990,8 +1907,8 @@ packages: fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} - foreground-child@3.1.1: - resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + foreground-child@3.2.1: + resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} form-data-encoder@2.1.4: @@ -2052,10 +1969,6 @@ packages: get-func-name@2.0.2: resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - get-stdin@8.0.0: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} engines: {node: '>=10'} @@ -2068,9 +1981,8 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob@10.4.1: - resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==} - engines: {node: '>=16 || 14 >=14.18'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true glob@7.2.3: @@ -2093,9 +2005,6 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - got@12.6.1: resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} engines: {node: '>=14.16'} @@ -2114,17 +2023,6 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - has-unicode@2.0.1: resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} @@ -2187,14 +2085,11 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} - import-in-the-middle@1.4.2: - resolution: {integrity: sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==} + import-in-the-middle@1.7.1: + resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} - import-in-the-middle@1.7.4: - resolution: {integrity: sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==} - - import-in-the-middle@1.8.0: - resolution: {integrity: sha512-/xQjze8szLNnJ5rvHSzn+dcVXqCAU6Plbk4P24U/jwPmg1wy7IIp9OjKIO5tYue8GSPhDpPDiApQjvBUmWwhsQ==} + import-in-the-middle@1.9.0: + resolution: {integrity: sha512-Ng1SJINJDBzyUEkx9Mj32XD8G0TQCUb5TMoL9V91CTn6F3wYZLygLuhNFrv0cNMBZaeptnL1zecV6XrIdHJ+xQ==} import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} @@ -2229,8 +2124,8 @@ packages: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - inquirer@9.2.22: - resolution: {integrity: sha512-SqLLa/Oe5rZUagTR9z+Zd6izyatHglbmbvVofo1KzuVB54YHleWzeHNLoR7FOICGOeQSqeLh1cordb3MzhGcEw==} + inquirer@9.3.5: + resolution: {integrity: sha512-SVRCRovA7KaT6nqWB2mCNpTvU4cuZ0hOXo5KPyiyOcNNUIZwq/JKtvXuDJNaxfuJKabBYRu1ecHze0YEwDYoRQ==} engines: {node: '>=18'} ip-address@9.0.5: @@ -2256,8 +2151,9 @@ packages: resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true - is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} + is-core-module@2.14.0: + resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} + engines: {node: '>= 0.4'} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} @@ -2333,15 +2229,14 @@ packages: ix@5.0.0: resolution: {integrity: sha512-6LyyrHnvNrSy5pKtW/KA+KKusHrB223aBJCJlIGPN7QBfDkEEtNrAkAz9lLLShIcdJntq6BiPCHuKaCM/9wwXw==} - jackspeak@3.1.2: - resolution: {integrity: sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==} - engines: {node: '>=14'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - jose@4.15.5: - resolution: {integrity: sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==} + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2434,8 +2329,8 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} - logform@2.6.0: - resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==} + logform@2.6.1: + resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} engines: {node: '>= 12.0.0'} lossless-json@2.0.11: @@ -2448,9 +2343,8 @@ packages: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lru-cache@10.2.2: - resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} - engines: {node: 14 || >=16.14} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -2506,8 +2400,8 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} - minimatch@9.0.4: - resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -2570,8 +2464,8 @@ packages: mongodb-connection-string-url@3.0.1: resolution: {integrity: sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==} - mongodb@6.7.0: - resolution: {integrity: sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==} + mongodb@6.8.0: + resolution: {integrity: sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==} engines: {node: '>=16.20.1'} peerDependencies: '@aws-sdk/credential-providers': ^3.188.0 @@ -2643,8 +2537,8 @@ packages: engines: {node: ^12.13 || ^14.13 || >=16} hasBin: true - nodemon@3.1.1: - resolution: {integrity: sha512-k43xGaDtaDIcufn0Fc6fTtsdKSkV/hQzoQFigNH//GaKta28yoKVYXCnV+KXRqfT/YzsFaQU9VdeEG+HEyxr6A==} + nodemon@3.1.4: + resolution: {integrity: sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==} engines: {node: '>=10'} hasBin: true @@ -2677,8 +2571,8 @@ packages: engines: {node: '>=14.14'} hasBin: true - npm-check-updates@17.1.2: - resolution: {integrity: sha512-k3osAbCNXIXqC7QAuF2uRHsKtTUS50KhOW1VAojRHlLdZRh/5EYfduvnVPGDWsbQXFakbSrSbWDdV8qIvDSUtA==} + npm-check-updates@17.1.3: + resolution: {integrity: sha512-4uDLBWPuDHT5KLieIJ20FoAB8yqJejmupI42wPyfObgQOBbPAikQSwT73afDwREvhuxYrRDqlRvxTMSfvO+L8A==} engines: {node: ^18.18.0 || >=20.0.0, npm: '>=8.12.1'} hasBin: true @@ -2732,9 +2626,11 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - opentelemetry-instrumentation-fetch-node@1.2.0: - resolution: {integrity: sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA==} + opentelemetry-instrumentation-fetch-node@1.2.3: + resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} engines: {node: '>18.0.0'} + peerDependencies: + '@opentelemetry/api': ^1.6.0 ora@5.4.1: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} @@ -2783,6 +2679,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + package-json@8.1.1: resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} engines: {node: '>=14.16'} @@ -2795,9 +2694,9 @@ packages: engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true - parse-github-url@1.0.2: - resolution: {integrity: sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==} - engines: {node: '>=0.10.0'} + parse-github-url@1.0.3: + resolution: {integrity: sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==} + engines: {node: '>= 0.10'} hasBin: true path-exists@4.0.0: @@ -2876,8 +2775,8 @@ packages: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + postcss@8.4.39: + resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} postgres-array@2.0.0: @@ -3090,8 +2989,8 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.3.1: - resolution: {integrity: sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} @@ -3103,13 +3002,13 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@5.0.7: - resolution: {integrity: sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==} - engines: {node: '>=14.18'} + rimraf@5.0.9: + resolution: {integrity: sha512-3i7b8OcswU6CpU8Ej89quJD4O98id7TtVM5U4Mybh84zQXdrFmDLouWBEEaD/QfO3gDDfH+AGFCGsR7kngzQnA==} + engines: {node: 14 >=14.20 || 16 >=16.20 || >=18} hasBin: true - rollup@4.18.0: - resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} + rollup@4.18.1: + resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3166,10 +3065,6 @@ packages: set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - shebang-command@1.2.0: resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} engines: {node: '>=0.10.0'} @@ -3396,8 +3291,8 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} - tinyspy@3.0.0: - resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} tmp@0.0.33: @@ -3463,8 +3358,8 @@ packages: peerDependencies: typescript: '*' - tsconfck@3.0.3: - resolution: {integrity: sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==} + tsconfck@3.1.1: + resolution: {integrity: sha512-00eoI6WY57SvZEVjm13stEVE90VkEdJAFGgpFLTsZbJyW/LwFQ7uQxJHWpZ2hzSWgCPKc9AnBnNP+0X7o3hAmQ==} engines: {node: ^18 || >=20} hasBin: true peerDependencies: @@ -3476,8 +3371,8 @@ packages: tsconfig@7.0.0: resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} - tslib@2.6.2: - resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tslib@2.6.3: + resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} tuf-js@1.1.7: resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} @@ -3578,8 +3473,8 @@ packages: vite: optional: true - vite@5.2.11: - resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==} + vite@5.3.3: + resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3672,12 +3567,12 @@ packages: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} - winston-transport@4.7.0: - resolution: {integrity: sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==} + winston-transport@4.7.1: + resolution: {integrity: sha512-wQCXXVgfv/wUPOfb2x0ruxzwkcZfxcktz6JIMUaPLmcNhO4bZTwA/WtDWK74xV3F2dKu8YadrFv0qhwYjVEwhA==} engines: {node: '>= 12.0.0'} - winston@3.13.0: - resolution: {integrity: sha512-rwidmA1w3SE4j0E5MuIufFhyJPBDG7Nu71RkZor1p2+qHvJSZ9GYDA81AyleQcZbh/+V6HjeBdfnTZJm9rSeQQ==} + winston@3.13.1: + resolution: {integrity: sha512-SvZit7VFNvXRzbqGHsv5KSmgbEYR5EiQfDAL9gxYkRqa934Hnk++zze0wANKtMHcy/gI4W/3xmSDwlhf865WGw==} engines: {node: '>= 12.0.0'} wrap-ansi@6.2.0: @@ -3698,8 +3593,8 @@ packages: write-file-atomic@3.0.3: resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} - ws@8.17.0: - resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3710,18 +3605,6 @@ packages: utf-8-validate: optional: true - ws@8.2.3: - resolution: {integrity: sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} @@ -3740,8 +3623,8 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml@2.4.2: - resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==} + yaml@2.4.5: + resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} engines: {node: '>= 14'} hasBin: true @@ -3774,26 +3657,30 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} snapshots: - '@babel/code-frame@7.24.6': + '@babel/code-frame@7.24.7': dependencies: - '@babel/highlight': 7.24.6 + '@babel/highlight': 7.24.7 picocolors: 1.0.1 - '@babel/helper-validator-identifier@7.24.6': {} + '@babel/helper-validator-identifier@7.24.7': {} - '@babel/highlight@7.24.6': + '@babel/highlight@7.24.7': dependencies: - '@babel/helper-validator-identifier': 7.24.6 + '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.0.1 - '@babel/runtime@7.24.6': + '@babel/runtime@7.24.8': dependencies: regenerator-runtime: 0.14.1 @@ -3956,80 +3843,80 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 - '@esbuild/aix-ppc64@0.20.2': + '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/android-arm64@0.20.2': + '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm@0.20.2': + '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-x64@0.20.2': + '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.20.2': + '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-x64@0.20.2': + '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.20.2': + '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.20.2': + '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/linux-arm64@0.20.2': + '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm@0.20.2': + '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-ia32@0.20.2': + '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-loong64@0.20.2': + '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-mips64el@0.20.2': + '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-ppc64@0.20.2': + '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.20.2': + '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-s390x@0.20.2': + '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-x64@0.20.2': + '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.20.2': + '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.20.2': + '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.20.2': + '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/win32-arm64@0.20.2': + '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-ia32@0.20.2': + '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-x64@0.20.2': + '@esbuild/win32-x64@0.21.5': optional: true - '@fastify/ajv-compiler@3.5.0': + '@fastify/ajv-compiler@3.6.0': dependencies: - ajv: 8.14.0 - ajv-formats: 2.1.1(ajv@8.14.0) - fast-uri: 2.3.0 + ajv: 8.16.0 + ajv-formats: 2.1.1(ajv@8.16.0) + fast-uri: 2.4.0 '@fastify/cors@8.4.1': dependencies: @@ -4040,7 +3927,7 @@ snapshots: '@fastify/fast-json-stringify-compiler@4.3.0': dependencies: - fast-json-stringify: 5.16.0 + fast-json-stringify: 5.16.1 '@fastify/merge-json-schemas@0.1.1': dependencies: @@ -4050,7 +3937,7 @@ snapshots: '@humanwhocodes/momoa@2.0.4': {} - '@inquirer/figures@1.0.2': {} + '@inquirer/figures@1.0.3': {} '@isaacs/cliui@8.0.2': dependencies: @@ -4063,31 +3950,25 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.4.15': {} - '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@js-sdsl/ordered-set@4.4.2': {} - '@ljharb/through@2.3.13': - dependencies: - call-bind: 1.0.7 - '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.24.6 + '@babel/runtime': 7.24.8 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.24.6 + '@babel/runtime': 7.24.8 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -4163,9 +4044,9 @@ snapshots: dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/api-logs@0.52.0': + '@opentelemetry/api-logs@0.52.1': dependencies: - '@opentelemetry/api': 1.8.0 + '@opentelemetry/api': 1.6.0 '@opentelemetry/api@1.6.0': {} @@ -4173,7 +4054,7 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@opentelemetry/context-async-hooks@1.25.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-async-hooks@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -4197,15 +4078,15 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.24.1 - '@opentelemetry/core@1.25.0(@opentelemetry/api@1.8.0)': + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/exporter-metrics-otlp-http@0.51.1(@opentelemetry/api@1.8.0)': dependencies: @@ -4230,149 +4111,147 @@ snapshots: '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0) - '@opentelemetry/instrumentation-connect@0.37.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@types/connect': 3.4.36 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-express@0.40.1(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-fastify@0.37.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-graphql@0.41.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-hapi@0.39.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-http@0.52.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 semver: 7.6.2 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-ioredis@0.41.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-koa@0.41.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@types/koa': 2.14.0 - '@types/koa__router': 12.0.3 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongodb@0.45.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongoose@0.39.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql2@0.39.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql@0.39.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@types/mysql': 2.15.22 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-nestjs-core@0.38.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-pg@0.42.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) '@types/pg': 8.6.1 '@types/pg-pool': 2.0.4 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-redis-4@0.40.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.8.0)': + '@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0)': dependencies: - '@opentelemetry/api': 1.8.0 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.4.2 + '@opentelemetry/api': 1.9.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.7.1 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 @@ -4380,24 +4259,24 @@ snapshots: - supports-color optional: true - '@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0)': + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/api-logs': 0.51.1 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.7.4 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.52.0 - '@types/shimmer': 1.0.5 - import-in-the-middle: 1.8.0 + '@opentelemetry/api-logs': 0.52.1 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.9.0 require-in-the-middle: 7.3.0 semver: 7.6.2 shimmer: 1.2.1 @@ -4445,17 +4324,17 @@ snapshots: '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.24.1 - '@opentelemetry/resources@1.25.0(@opentelemetry/api@1.8.0)': + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/resources@1.25.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/resources@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/sdk-logs@0.51.1(@opentelemetry/api-logs@0.51.1)(@opentelemetry/api@1.8.0)': dependencies: @@ -4499,30 +4378,30 @@ snapshots: '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0) '@opentelemetry/semantic-conventions': 1.24.1 - '@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.8.0)': + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.8.0)': dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.8.0) + '@opentelemetry/semantic-conventions': 1.25.1 - '@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 '@opentelemetry/semantic-conventions@1.17.0': {} '@opentelemetry/semantic-conventions@1.24.1': {} - '@opentelemetry/semantic-conventions@1.25.0': {} + '@opentelemetry/semantic-conventions@1.25.1': {} '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) '@pkgjs/parseargs@0.11.0': optional: true @@ -4539,117 +4418,117 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@prisma/instrumentation@5.15.0': + '@prisma/instrumentation@5.16.1': dependencies: '@opentelemetry/api': 1.8.0 - '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.8.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.8.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.8.0) transitivePeerDependencies: - supports-color - '@rollup/rollup-android-arm-eabi@4.18.0': + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true - '@rollup/rollup-android-arm64@4.18.0': + '@rollup/rollup-android-arm64@4.18.1': optional: true - '@rollup/rollup-darwin-arm64@4.18.0': + '@rollup/rollup-darwin-arm64@4.18.1': optional: true - '@rollup/rollup-darwin-x64@4.18.0': + '@rollup/rollup-darwin-x64@4.18.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + '@rollup/rollup-linux-arm-gnueabihf@4.18.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.18.0': + '@rollup/rollup-linux-arm-musleabihf@4.18.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.18.0': + '@rollup/rollup-linux-arm64-gnu@4.18.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.18.0': + '@rollup/rollup-linux-arm64-musl@4.18.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.18.0': + '@rollup/rollup-linux-riscv64-gnu@4.18.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.18.0': + '@rollup/rollup-linux-s390x-gnu@4.18.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.18.0': + '@rollup/rollup-linux-x64-gnu@4.18.1': optional: true - '@rollup/rollup-linux-x64-musl@4.18.0': + '@rollup/rollup-linux-x64-musl@4.18.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.18.0': + '@rollup/rollup-win32-arm64-msvc@4.18.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.18.0': + '@rollup/rollup-win32-ia32-msvc@4.18.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.18.0': + '@rollup/rollup-win32-x64-msvc@4.18.1': optional: true - '@sentry/core@8.9.2': + '@sentry/core@8.17.0': dependencies: - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 - '@sentry/node@8.9.2': + '@sentry/node@8.17.0': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.37.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.40.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fastify': 0.37.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.45.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-nestjs-core': 0.38.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.42.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis-4': 0.40.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@prisma/instrumentation': 5.15.0 - '@sentry/core': 8.9.2 - '@sentry/opentelemetry': 8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0) - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 + '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.39.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@prisma/instrumentation': 5.16.1 + '@sentry/core': 8.17.0 + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 optionalDependencies: - opentelemetry-instrumentation-fetch-node: 1.2.0 + opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.9.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.52.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.0)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.25.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.0 - '@sentry/core': 8.9.2 - '@sentry/types': 8.9.2 - '@sentry/utils': 8.9.2 + '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 + '@sentry/core': 8.17.0 + '@sentry/types': 8.17.0 + '@sentry/utils': 8.17.0 - '@sentry/types@8.9.2': {} + '@sentry/types@8.17.0': {} - '@sentry/utils@8.9.2': + '@sentry/utils@8.17.0': dependencies: - '@sentry/types': 8.9.2 + '@sentry/types': 8.17.0 '@sigstore/bundle@1.1.0': dependencies: @@ -4697,82 +4576,19 @@ snapshots: '@tufjs/models@1.0.4': dependencies: '@tufjs/canonical-json': 1.0.0 - minimatch: 9.0.4 - - '@types/accepts@1.3.7': - dependencies: - '@types/node': 22.5.5 + minimatch: 9.0.5 '@types/async@3.2.24': {} - '@types/body-parser@1.19.5': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 22.5.5 - '@types/connect@3.4.36': dependencies: '@types/node': 22.5.5 - '@types/connect@3.4.38': - dependencies: - '@types/node': 22.5.5 - - '@types/content-disposition@0.5.8': {} - - '@types/cookies@0.9.0': - dependencies: - '@types/connect': 3.4.38 - '@types/express': 4.17.21 - '@types/keygrip': 1.0.6 - '@types/node': 22.5.5 - '@types/estree@1.0.5': {} - '@types/express-serve-static-core@4.19.1': - dependencies: - '@types/node': 22.5.5 - '@types/qs': 6.9.15 - '@types/range-parser': 1.2.7 - '@types/send': 0.17.4 - - '@types/express@4.17.21': - dependencies: - '@types/body-parser': 1.19.5 - '@types/express-serve-static-core': 4.19.1 - '@types/qs': 6.9.15 - '@types/serve-static': 1.15.7 - - '@types/http-assert@1.5.5': {} - '@types/http-cache-semantics@4.0.4': {} - '@types/http-errors@2.0.4': {} - - '@types/keygrip@1.0.6': {} - - '@types/koa-compose@3.2.8': - dependencies: - '@types/koa': 2.14.0 - - '@types/koa@2.14.0': - dependencies: - '@types/accepts': 1.3.7 - '@types/content-disposition': 0.5.8 - '@types/cookies': 0.9.0 - '@types/http-assert': 1.5.5 - '@types/http-errors': 2.0.4 - '@types/keygrip': 1.0.6 - '@types/koa-compose': 3.2.8 - '@types/node': 22.5.5 - - '@types/koa__router@12.0.3': - dependencies: - '@types/koa': 2.14.0 - - '@types/lodash@4.17.5': {} - - '@types/mime@1.3.5': {} + '@types/lodash@4.17.6': {} '@types/mysql@2.15.22': dependencies: @@ -4798,26 +4614,11 @@ snapshots: pg-protocol: 1.6.1 pg-types: 2.2.0 - '@types/qs@6.9.15': {} - - '@types/range-parser@1.2.7': {} - '@types/semver-utils@1.1.3': {} '@types/semver@7.5.8': {} - '@types/send@0.17.4': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 22.5.5 - - '@types/serve-static@1.15.7': - dependencies: - '@types/http-errors': 2.0.4 - '@types/node': 22.5.5 - '@types/send': 0.17.4 - - '@types/shimmer@1.0.5': {} + '@types/shimmer@1.2.0': {} '@types/strip-bom@3.0.0': {} @@ -4844,13 +4645,13 @@ snapshots: chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.2.11(@types/node@22.5.5))': + '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.3.3(@types/node@22.5.5))': dependencies: '@vitest/spy': 2.1.1 estree-walker: 3.0.3 magic-string: 0.30.11 optionalDependencies: - vite: 5.2.11(@types/node@22.5.5) + vite: 5.3.3(@types/node@22.5.5) '@vitest/pretty-format@2.1.1': dependencies: @@ -4869,7 +4670,7 @@ snapshots: '@vitest/spy@2.1.1': dependencies: - tinyspy: 3.0.0 + tinyspy: 3.0.2 '@vitest/utils@2.1.1': dependencies: @@ -4885,22 +4686,24 @@ snapshots: abstract-logging@2.0.1: {} - acorn-import-assertions@1.9.0(acorn@8.11.3): + acorn-import-assertions@1.9.0(acorn@8.12.1): dependencies: - acorn: 8.11.3 + acorn: 8.12.1 optional: true - acorn-import-attributes@1.9.5(acorn@8.11.3): + acorn-import-attributes@1.9.5(acorn@8.12.1): dependencies: - acorn: 8.11.3 + acorn: 8.12.1 - acorn-walk@8.3.2: {} + acorn-walk@8.3.3: + dependencies: + acorn: 8.12.1 - acorn@8.11.3: {} + acorn@8.12.1: {} agent-base@6.0.2: dependencies: - debug: 4.3.7 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -4913,15 +4716,15 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ajv-formats@2.1.1(ajv@8.14.0): + ajv-formats@2.1.1(ajv@8.16.0): optionalDependencies: - ajv: 8.14.0 + ajv: 8.16.0 - ajv-formats@3.0.1(ajv@8.14.0): + ajv-formats@3.0.1(ajv@8.16.0): optionalDependencies: - ajv: 8.14.0 + ajv: 8.16.0 - ajv@8.14.0: + ajv@8.16.0: dependencies: fast-deep-equal: 3.1.3 json-schema-traverse: 1.0.0 @@ -4978,7 +4781,7 @@ snapshots: async-mutex@0.5.0: dependencies: - tslib: 2.6.2 + tslib: 2.6.3 async@3.2.5: {} @@ -4993,11 +4796,11 @@ snapshots: base64-js@1.5.1: {} - better-ajv-errors@1.2.0(ajv@8.14.0): + better-ajv-errors@1.2.0(ajv@8.16.0): dependencies: - '@babel/code-frame': 7.24.6 + '@babel/code-frame': 7.24.7 '@humanwhocodes/momoa': 2.0.4 - ajv: 8.14.0 + ajv: 8.16.0 chalk: 4.1.2 jsonpointer: 5.0.1 leven: 3.1.0 @@ -5038,7 +4841,7 @@ snapshots: dependencies: fill-range: 7.1.1 - bson@6.7.0: {} + bson@6.8.0: {} buffer-from@1.1.2: {} @@ -5081,7 +4884,7 @@ snapshots: dependencies: '@npmcli/fs': 3.1.1 fs-minipass: 3.0.3 - glob: 10.4.1 + glob: 10.4.5 lru-cache: 7.18.3 minipass: 7.1.2 minipass-collect: 1.0.2 @@ -5104,14 +4907,6 @@ snapshots: normalize-url: 8.0.1 responselike: 3.0.0 - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - camelcase@7.0.1: {} chai@5.1.1: @@ -5294,11 +5089,11 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.24.6 + '@babel/runtime': 7.24.8 date-fns@3.6.0: {} - debug@4.3.4(supports-color@5.5.0): + debug@4.3.5(supports-color@5.5.0): dependencies: ms: 2.1.2 optionalDependencies: @@ -5322,12 +5117,6 @@ snapshots: defer-to-connect@2.0.1: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - delegates@1.0.0: {} detect-indent@6.1.0: {} @@ -5374,37 +5163,31 @@ snapshots: err-code@2.0.3: {} - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-errors@1.3.0: {} - - esbuild@0.20.2: + esbuild@0.21.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 escalade@3.1.2: {} @@ -5456,15 +5239,15 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.7 - fast-json-stringify@5.16.0: + fast-json-stringify@5.16.1: dependencies: '@fastify/merge-json-schemas': 0.1.1 - ajv: 8.14.0 - ajv-formats: 3.0.1(ajv@8.14.0) + ajv: 8.16.0 + ajv-formats: 3.0.1(ajv@8.16.0) fast-deep-equal: 3.1.3 - fast-uri: 2.3.0 + fast-uri: 2.4.0 json-schema-ref-resolver: 1.0.1 - rfdc: 1.3.1 + rfdc: 1.4.1 fast-memoize@2.5.2: {} @@ -5474,25 +5257,25 @@ snapshots: fast-redact@3.5.0: {} - fast-uri@2.3.0: {} + fast-uri@2.4.0: {} fastify-plugin@4.5.1: {} fastify@4.23.2: dependencies: - '@fastify/ajv-compiler': 3.5.0 + '@fastify/ajv-compiler': 3.6.0 '@fastify/error': 3.4.1 '@fastify/fast-json-stringify-compiler': 4.3.0 abstract-logging: 2.0.1 avvio: 8.3.2 fast-content-type-parse: 1.1.0 - fast-json-stringify: 5.16.0 + fast-json-stringify: 5.16.1 find-my-way: 7.7.0 light-my-request: 5.13.0 pino: 8.21.0 process-warning: 2.3.2 proxy-addr: 2.0.7 - rfdc: 1.3.1 + rfdc: 1.4.1 secure-json-parse: 2.7.0 semver: 7.6.2 toad-cache: 3.7.0 @@ -5530,7 +5313,7 @@ snapshots: fn.name@1.1.0: {} - foreground-child@3.1.1: + foreground-child@3.2.1: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 @@ -5589,14 +5372,6 @@ snapshots: get-func-name@2.0.2: {} - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - get-stdin@8.0.0: {} get-stream@6.0.1: {} @@ -5605,12 +5380,13 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.1: + glob@10.4.5: dependencies: - foreground-child: 3.1.1 - jackspeak: 3.1.2 - minimatch: 9.0.4 + foreground-child: 3.2.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 minipass: 7.1.2 + package-json-from-dist: 1.0.0 path-scurry: 1.11.1 glob@7.2.3: @@ -5645,10 +5421,6 @@ snapshots: globrex@0.1.2: {} - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 - got@12.6.1: dependencies: '@sindresorhus/is': 5.6.0 @@ -5671,14 +5443,6 @@ snapshots: has-flag@4.0.0: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 - - has-proto@1.0.3: {} - - has-symbols@1.0.3: {} - has-unicode@2.0.1: {} has-yarn@3.0.0: {} @@ -5701,7 +5465,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -5713,7 +5477,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -5738,29 +5502,22 @@ snapshots: ignore-walk@6.0.5: dependencies: - minimatch: 9.0.4 + minimatch: 9.0.5 ignore@5.3.1: {} - import-in-the-middle@1.4.2: + import-in-the-middle@1.7.1: dependencies: - acorn: 8.11.3 - acorn-import-assertions: 1.9.0(acorn@8.11.3) + acorn: 8.12.1 + acorn-import-assertions: 1.9.0(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 optional: true - import-in-the-middle@1.7.4: + import-in-the-middle@1.9.0: dependencies: - acorn: 8.11.3 - acorn-import-attributes: 1.9.5(acorn@8.11.3) - cjs-module-lexer: 1.3.1 - module-details-from-path: 1.0.3 - - import-in-the-middle@1.8.0: - dependencies: - acorn: 8.11.3 - acorn-import-attributes: 1.9.5(acorn@8.11.3) + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 @@ -5785,16 +5542,12 @@ snapshots: ini@4.1.3: {} - inquirer@9.2.22: + inquirer@9.3.5: dependencies: - '@inquirer/figures': 1.0.2 - '@ljharb/through': 2.3.13 + '@inquirer/figures': 1.0.3 ansi-escapes: 4.3.2 - chalk: 5.3.0 - cli-cursor: 3.1.0 cli-width: 4.1.0 external-editor: 3.1.0 - lodash: 4.17.21 mute-stream: 1.0.0 ora: 5.4.1 run-async: 3.0.0 @@ -5802,6 +5555,7 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 ip-address@9.0.5: dependencies: @@ -5822,7 +5576,7 @@ snapshots: dependencies: ci-info: 3.9.0 - is-core-module@2.13.1: + is-core-module@2.14.0: dependencies: hasown: 2.0.2 @@ -5874,9 +5628,9 @@ snapshots: ix@5.0.0: dependencies: '@types/node': 13.13.52 - tslib: 2.6.2 + tslib: 2.6.3 - jackspeak@3.1.2: + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: @@ -5884,7 +5638,7 @@ snapshots: jju@1.4.0: {} - jose@4.15.5: {} + jose@4.15.9: {} js-tokens@4.0.0: {} @@ -5964,7 +5718,7 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 - logform@2.6.0: + logform@2.6.1: dependencies: '@colors/colors': 1.6.0 '@types/triple-beam': 1.3.5 @@ -5981,7 +5735,7 @@ snapshots: lowercase-keys@3.0.0: {} - lru-cache@10.2.2: {} + lru-cache@10.4.3: {} lru-cache@4.1.5: dependencies: @@ -6063,7 +5817,7 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.4: + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -6132,10 +5886,10 @@ snapshots: '@types/whatwg-url': 11.0.5 whatwg-url: 13.0.0 - mongodb@6.7.0(socks@2.8.3): + mongodb@6.8.0(socks@2.8.3): dependencies: '@mongodb-js/saslprep': 1.1.7 - bson: 6.7.0 + bson: 6.8.0 mongodb-connection-string-url: 3.0.1 optionalDependencies: socks: 2.8.3 @@ -6188,10 +5942,10 @@ snapshots: - bluebird - supports-color - nodemon@3.1.1: + nodemon@3.1.4: dependencies: chokidar: 3.6.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) ignore-by-default: 1.0.1 minimatch: 3.1.2 pstree.remy: 1.1.8 @@ -6213,7 +5967,7 @@ snapshots: normalize-package-data@5.0.0: dependencies: hosted-git-info: 6.1.1 - is-core-module: 2.13.1 + is-core-module: 2.14.0 semver: 7.6.2 validate-npm-package-license: 3.0.4 @@ -6243,15 +5997,15 @@ snapshots: jsonlines: 0.1.1 lodash: 4.17.21 make-fetch-happen: 11.1.1 - minimatch: 9.0.4 + minimatch: 9.0.5 p-map: 4.0.0 pacote: 15.2.0 - parse-github-url: 1.0.2 + parse-github-url: 1.0.3 progress: 2.0.3 prompts-ncu: 3.0.0 rc-config-loader: 4.1.3 remote-git-tags: 3.0.0 - rimraf: 5.0.7 + rimraf: 5.0.9 semver: 7.6.2 semver-utils: 1.1.4 source-map-support: 0.5.21 @@ -6264,7 +6018,7 @@ snapshots: - bluebird - supports-color - npm-check-updates@17.1.2: {} + npm-check-updates@17.1.3: {} npm-install-checks@6.3.0: dependencies: @@ -6327,11 +6081,11 @@ snapshots: dependencies: mimic-fn: 2.1.0 - opentelemetry-instrumentation-fetch-node@1.2.0: + opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): dependencies: - '@opentelemetry/api': 1.8.0 - '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.8.0) - '@opentelemetry/semantic-conventions': 1.25.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.25.1 transitivePeerDependencies: - supports-color optional: true @@ -6382,6 +6136,8 @@ snapshots: p-try@2.2.0: {} + package-json-from-dist@1.0.0: {} + package-json@8.1.1: dependencies: got: 12.6.1 @@ -6415,7 +6171,7 @@ snapshots: - bluebird - supports-color - parse-github-url@1.0.2: {} + parse-github-url@1.0.3: {} path-exists@4.0.0: {} @@ -6427,7 +6183,7 @@ snapshots: path-scurry@1.11.1: dependencies: - lru-cache: 10.2.2 + lru-cache: 10.4.3 minipass: 7.1.2 path-type@4.0.0: {} @@ -6488,7 +6244,7 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 - postcss@8.4.38: + postcss@8.4.39: dependencies: nanoid: 3.3.7 picocolors: 1.0.1 @@ -6568,7 +6324,7 @@ snapshots: rc-config-loader@4.1.3: dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.5(supports-color@5.5.0) js-yaml: 4.1.0 json5: 2.2.3 require-from-string: 2.0.2 @@ -6589,7 +6345,7 @@ snapshots: read-package-json@6.0.4: dependencies: - glob: 10.4.1 + glob: 10.4.5 json-parse-even-better-errors: 3.0.2 normalize-package-data: 5.0.0 npm-normalize-package-bin: 3.0.1 @@ -6656,7 +6412,7 @@ snapshots: require-in-the-middle@7.3.0: dependencies: - debug: 4.3.7 + debug: 4.3.5(supports-color@5.5.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -6668,7 +6424,7 @@ snapshots: resolve@1.22.8: dependencies: - is-core-module: 2.13.1 + is-core-module: 2.14.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -6689,7 +6445,7 @@ snapshots: reusify@1.0.4: {} - rfdc@1.3.1: {} + rfdc@1.4.1: {} rimraf@2.7.1: dependencies: @@ -6699,30 +6455,30 @@ snapshots: dependencies: glob: 7.2.3 - rimraf@5.0.7: + rimraf@5.0.9: dependencies: - glob: 10.4.1 + glob: 10.4.5 - rollup@4.18.0: + rollup@4.18.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.0 - '@rollup/rollup-android-arm64': 4.18.0 - '@rollup/rollup-darwin-arm64': 4.18.0 - '@rollup/rollup-darwin-x64': 4.18.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 - '@rollup/rollup-linux-arm-musleabihf': 4.18.0 - '@rollup/rollup-linux-arm64-gnu': 4.18.0 - '@rollup/rollup-linux-arm64-musl': 4.18.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 - '@rollup/rollup-linux-riscv64-gnu': 4.18.0 - '@rollup/rollup-linux-s390x-gnu': 4.18.0 - '@rollup/rollup-linux-x64-gnu': 4.18.0 - '@rollup/rollup-linux-x64-musl': 4.18.0 - '@rollup/rollup-win32-arm64-msvc': 4.18.0 - '@rollup/rollup-win32-ia32-msvc': 4.18.0 - '@rollup/rollup-win32-x64-msvc': 4.18.0 + '@rollup/rollup-android-arm-eabi': 4.18.1 + '@rollup/rollup-android-arm64': 4.18.1 + '@rollup/rollup-darwin-arm64': 4.18.1 + '@rollup/rollup-darwin-x64': 4.18.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.18.1 + '@rollup/rollup-linux-arm-musleabihf': 4.18.1 + '@rollup/rollup-linux-arm64-gnu': 4.18.1 + '@rollup/rollup-linux-arm64-musl': 4.18.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.18.1 + '@rollup/rollup-linux-riscv64-gnu': 4.18.1 + '@rollup/rollup-linux-s390x-gnu': 4.18.1 + '@rollup/rollup-linux-x64-gnu': 4.18.1 + '@rollup/rollup-linux-x64-musl': 4.18.1 + '@rollup/rollup-win32-arm64-msvc': 4.18.1 + '@rollup/rollup-win32-ia32-msvc': 4.18.1 + '@rollup/rollup-win32-x64-msvc': 4.18.1 fsevents: 2.3.3 rsocket-core@1.0.0-alpha.3: {} @@ -6739,7 +6495,7 @@ snapshots: rxjs@7.8.1: dependencies: - tslib: 2.6.2 + tslib: 2.6.3 safe-buffer@5.1.2: {} @@ -6767,15 +6523,6 @@ snapshots: set-cookie-parser@2.6.0: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 @@ -6825,7 +6572,7 @@ snapshots: socks-proxy-agent@7.0.0: dependencies: agent-base: 6.0.2 - debug: 4.3.7 + debug: 4.3.5(supports-color@5.5.0) socks: 2.8.3 transitivePeerDependencies: - supports-color @@ -6989,7 +6736,7 @@ snapshots: tinyrainbow@1.2.0: {} - tinyspy@3.0.0: {} + tinyspy@3.0.2: {} tmp@0.0.33: dependencies: @@ -7039,8 +6786,8 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 22.5.5 - acorn: 8.11.3 - acorn-walk: 8.3.2 + acorn: 8.12.1 + acorn-walk: 8.3.3 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 @@ -7057,7 +6804,7 @@ snapshots: string-argv: 0.3.2 typescript: 5.6.2 - tsconfck@3.0.3(typescript@5.6.2): + tsconfck@3.1.1(typescript@5.6.2): optionalDependencies: typescript: 5.6.2 @@ -7068,12 +6815,12 @@ snapshots: strip-bom: 3.0.0 strip-json-comments: 2.0.1 - tslib@2.6.2: {} + tslib@2.6.3: {} tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.7 + debug: 4.3.5(supports-color@5.5.0) make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color @@ -7159,7 +6906,7 @@ snapshots: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 - vite: 5.2.11(@types/node@22.5.5) + vite: 5.3.3(@types/node@22.5.5) transitivePeerDependencies: - '@types/node' - less @@ -7170,22 +6917,22 @@ snapshots: - supports-color - terser - vite-tsconfig-paths@4.3.2(typescript@5.6.2)(vite@5.2.11(@types/node@22.5.5)): + vite-tsconfig-paths@4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)): dependencies: - debug: 4.3.7 + debug: 4.3.5(supports-color@5.5.0) globrex: 0.1.2 - tsconfck: 3.0.3(typescript@5.6.2) + tsconfck: 3.1.1(typescript@5.6.2) optionalDependencies: - vite: 5.2.11(@types/node@22.5.5) + vite: 5.3.3(@types/node@22.5.5) transitivePeerDependencies: - supports-color - typescript - vite@5.2.11(@types/node@22.5.5): + vite@5.3.3(@types/node@22.5.5): dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 + esbuild: 0.21.5 + postcss: 8.4.39 + rollup: 4.18.1 optionalDependencies: '@types/node': 22.5.5 fsevents: 2.3.3 @@ -7193,7 +6940,7 @@ snapshots: vitest@2.1.1(@types/node@22.5.5): dependencies: '@vitest/expect': 2.1.1 - '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.2.11(@types/node@22.5.5)) + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.3.3(@types/node@22.5.5)) '@vitest/pretty-format': 2.1.1 '@vitest/runner': 2.1.1 '@vitest/snapshot': 2.1.1 @@ -7208,7 +6955,7 @@ snapshots: tinyexec: 0.3.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.2.11(@types/node@22.5.5) + vite: 5.3.3(@types/node@22.5.5) vite-node: 2.1.1(@types/node@22.5.5) why-is-node-running: 2.3.0 optionalDependencies: @@ -7261,25 +7008,25 @@ snapshots: dependencies: string-width: 5.1.2 - winston-transport@4.7.0: + winston-transport@4.7.1: dependencies: - logform: 2.6.0 + logform: 2.6.1 readable-stream: 3.6.2 triple-beam: 1.4.1 - winston@3.13.0: + winston@3.13.1: dependencies: '@colors/colors': 1.6.0 '@dabh/diagnostics': 2.0.3 async: 3.2.5 is-stream: 2.0.1 - logform: 2.6.0 + logform: 2.6.1 one-time: 1.0.0 readable-stream: 3.6.2 safe-stable-stringify: 2.4.3 stack-trace: 0.0.10 triple-beam: 1.4.1 - winston-transport: 4.7.0 + winston-transport: 4.7.1 wrap-ansi@6.2.0: dependencies: @@ -7308,9 +7055,7 @@ snapshots: signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 - ws@8.17.0: {} - - ws@8.2.3: {} + ws@8.18.0: {} xdg-basedir@5.1.0: {} @@ -7322,7 +7067,7 @@ snapshots: yallist@4.0.0: {} - yaml@2.4.2: {} + yaml@2.4.5: {} yaml@2.5.0: {} @@ -7354,4 +7099,6 @@ snapshots: yocto-queue@0.1.0: {} + yoctocolors-cjs@2.1.2: {} + zod@3.23.8: {} diff --git a/service/src/runners/server.ts b/service/src/runners/server.ts index c91c507be..28939464b 100644 --- a/service/src/runners/server.ts +++ b/service/src/runners/server.ts @@ -78,6 +78,7 @@ export async function startServer(runnerConfig: core.utils.RunnerConfig) { await moduleManager.initialize(serviceContext); logger.info('Starting service...'); + await serviceContext.lifeCycleEngine.start(); logger.info('Service started.'); From 0532cd887fcea57eba178fcae19411f8e57dc641 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 15 Oct 2024 09:32:56 +0200 Subject: [PATCH 239/298] Ignore auth errors on collections for mongodb schema. --- .../src/api/MongoRouteAPIAdapter.ts | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index e96cc8cb8..322915ba9 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -192,16 +192,25 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { let collections: mongo.CollectionInfo[]; try { collections = await this.client.db(db.name).listCollections().toArray(); - } catch (ex) { - return null; + } catch (e) { + if (e instanceof mongo.MongoServerError && e.codeName == 'Unauthorized') { + // Ignore databases we're not authorized to query + return null; + } + throw e; } - const filtered = collections.filter((c) => { - return !['_powersync_checkpoints'].includes(c.name); - }); - - const tables = await Promise.all( - filtered.map(async (collection) => { + let tables: service_types.TableSchema[] = []; + for (let collection of collections) { + if (['_powersync_checkpoints'].includes(collection.name)) { + continue; + } + if (collection.name.startsWith('system.')) { + // system.views, system.js, system.profile, system.buckets + // https://www.mongodb.com/docs/manual/reference/system-collections/ + continue; + } + try { const sampleDocuments = await this.db .collection(collection.name) .aggregate([{ $sample: { size: sampleSize } }]) @@ -210,20 +219,27 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { if (sampleDocuments.length > 0) { const columns = this.getColumnsFromDocuments(sampleDocuments); - return { + tables.push({ name: collection.name, // Since documents are sampled in a random order, we need to sort // to get a consistent order columns: columns.sort((a, b) => a.name.localeCompare(b.name)) - } satisfies service_types.TableSchema; + }); } else { - return { + tables.push({ name: collection.name, columns: [] - } satisfies service_types.TableSchema; + }); } - }) - ); + } catch (e) { + if (e instanceof mongo.MongoServerError && e.codeName == 'Unauthorized') { + // Ignore collections we're not authorized to query + continue; + } + throw e; + } + } + return { name: db.name, tables: tables From 04ddc985543d28348dba088607d68cf4d6781ccf Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 15 Oct 2024 09:39:00 +0200 Subject: [PATCH 240/298] Add changeset. --- .changeset/cold-items-explain.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/cold-items-explain.md diff --git a/.changeset/cold-items-explain.md b/.changeset/cold-items-explain.md new file mode 100644 index 000000000..78baa7bfc --- /dev/null +++ b/.changeset/cold-items-explain.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-module-mongodb': patch +--- + +Fix diagnostics schema authorization issues for MongoDB From bb367f4be5e2389471d99904b29d0b684702049f Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 15 Oct 2024 09:44:22 +0200 Subject: [PATCH 241/298] Filter out views. --- modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 322915ba9..d58d2cf58 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -210,6 +210,9 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { // https://www.mongodb.com/docs/manual/reference/system-collections/ continue; } + if (collection.type == 'view') { + continue; + } try { const sampleDocuments = await this.db .collection(collection.name) From 0932afbca23961da86ab4db28ca784a7ee331740 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 15 Oct 2024 20:43:24 +0200 Subject: [PATCH 242/298] Updated dev/test mysql docker compose Renamed MysqlBinLogStream Some naming cleanup Fixed connection usage in BinlogStream --- modules/module-mysql/dev/README.md | 2 +- .../dev/docker/mysql/init-scripts/my.cnf | 2 +- .../dev/docker/mysql/init-scripts/mysql.sql | 3 +- .../src/api/MySQLRouteAPIAdapter.ts | 2 +- .../src/common/check-source-configuration.ts | 9 ++ .../src/common/get-replication-columns.ts | 10 +- .../src/common/read-executed-gtid.ts | 11 +- .../src/replication/BinLogReplicationJob.ts | 4 +- .../{MysqlBinLogStream.ts => BinLogStream.ts} | 104 ++++++++++-------- .../src/replication/MySQLConnectionManager.ts | 10 +- modules/module-mysql/src/types/types.ts | 2 +- modules/module-mysql/src/utils/mysql_utils.ts | 2 +- 12 files changed, 90 insertions(+), 71 deletions(-) rename modules/module-mysql/src/replication/{MysqlBinLogStream.ts => BinLogStream.ts} (83%) diff --git a/modules/module-mysql/dev/README.md b/modules/module-mysql/dev/README.md index fa00d7d17..fe62ef533 100644 --- a/modules/module-mysql/dev/README.md +++ b/modules/module-mysql/dev/README.md @@ -3,7 +3,7 @@ This folder contains some helpers for developing with MySQL. - `./.env.template` contains basic settings to be applied to a root `.env` file -- `./config` contains YAML configuration files for a MySQL odo list application +- `./config` contains YAML configuration files for a MySQL todo list application - `./docker/mysql` contains a docker compose file for starting Mysql TODO this does not contain any auth or backend functionality. diff --git a/modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf b/modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf index 815c4b690..99f01c70a 100644 --- a/modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf +++ b/modules/module-mysql/dev/docker/mysql/init-scripts/my.cnf @@ -4,6 +4,6 @@ enforce-gtid-consistency = ON # Row format required for ZongJi binlog_format = row log_bin=mysql-bin -server-id=1 +server-id=1 binlog-do-db=mydatabase replicate-do-table=mydatabase.lists \ No newline at end of file diff --git a/modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql b/modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql index 480ae786c..8e5cb3538 100644 --- a/modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql +++ b/modules/module-mysql/dev/docker/mysql/init-scripts/mysql.sql @@ -2,7 +2,8 @@ CREATE USER 'repl_user'@'%' IDENTIFIED BY 'good_password'; -- Grant replication client privilege -GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl_user'@'%'; +GRANT REPLICATION SLAVE, REPLICATION CLIENT, RELOAD ON *.* TO 'repl_user'@'%'; +GRANT REPLICATION SLAVE, REPLICATION CLIENT, RELOAD ON *.* TO 'myuser'@'%'; -- Grant access to the specific database GRANT ALL PRIVILEGES ON mydatabase.* TO 'repl_user'@'%'; diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 44f115e8b..17e584c25 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -206,7 +206,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { try { connection = await this.pool.getConnection(); idColumnsResult = await common.getReplicationIdentityColumns({ - db: connection, + connection: connection, schema, table_name: tableName }); diff --git a/modules/module-mysql/src/common/check-source-configuration.ts b/modules/module-mysql/src/common/check-source-configuration.ts index bcc7a4fa3..a48c4184b 100644 --- a/modules/module-mysql/src/common/check-source-configuration.ts +++ b/modules/module-mysql/src/common/check-source-configuration.ts @@ -48,3 +48,12 @@ export async function checkSourceConfiguration(connection: mysqlPromise.Connecti return errors; } + +export async function getMySQLVersion(connection: mysqlPromise.Connection): Promise { + const [[versionResult]] = await mysql_utils.retriedQuery({ + connection, + query: `SELECT VERSION() as version` + }); + + return versionResult.version as string; +} diff --git a/modules/module-mysql/src/common/get-replication-columns.ts b/modules/module-mysql/src/common/get-replication-columns.ts index 2242ed6ef..19d5ca059 100644 --- a/modules/module-mysql/src/common/get-replication-columns.ts +++ b/modules/module-mysql/src/common/get-replication-columns.ts @@ -3,7 +3,7 @@ import mysqlPromise from 'mysql2/promise'; import * as mysql_utils from '../utils/mysql_utils.js'; export type GetReplicationColumnsOptions = { - db: mysqlPromise.Connection; + connection: mysqlPromise.Connection; schema: string; table_name: string; }; @@ -17,9 +17,9 @@ export type ReplicationIdentityColumnsResult = { export async function getReplicationIdentityColumns( options: GetReplicationColumnsOptions ): Promise { - const { db, schema, table_name } = options; + const { connection, schema, table_name } = options; const [primaryKeyColumns] = await mysql_utils.retriedQuery({ - connection: db, + connection: connection, query: ` SELECT s.COLUMN_NAME AS name, @@ -55,7 +55,7 @@ export async function getReplicationIdentityColumns( // TODO: test code with tables with unique keys, compound key etc. // No primary key, find the first valid unique key const [uniqueKeyColumns] = await mysql_utils.retriedQuery({ - connection: db, + connection: connection, query: ` SELECT s.INDEX_NAME, @@ -92,7 +92,7 @@ export async function getReplicationIdentityColumns( } const [allColumns] = await mysql_utils.retriedQuery({ - connection: db, + connection: connection, query: ` SELECT s.COLUMN_NAME AS name, diff --git a/modules/module-mysql/src/common/read-executed-gtid.ts b/modules/module-mysql/src/common/read-executed-gtid.ts index 2b52ffe2c..51ac7c97f 100644 --- a/modules/module-mysql/src/common/read-executed-gtid.ts +++ b/modules/module-mysql/src/common/read-executed-gtid.ts @@ -3,17 +3,14 @@ import * as mysql_utils from '../utils/mysql_utils.js'; import { gte } from 'semver'; import { ReplicatedGTID } from './ReplicatedGTID.js'; +import { getMySQLVersion } from './check-source-configuration.js'; +import { logger } from '@powersync/lib-services-framework'; /** * Gets the current master HEAD GTID */ export async function readExecutedGtid(connection: mysqlPromise.Connection): Promise { - const [[versionResult]] = await mysql_utils.retriedQuery({ - connection, - query: `SELECT VERSION() as version` - }); - - const version = versionResult.version as string; + const version = await getMySQLVersion(connection); let binlogStatus: mysqlPromise.RowDataPacket; if (gte(version, '8.4.0')) { // Get the BinLog status @@ -36,6 +33,8 @@ export async function readExecutedGtid(connection: mysqlPromise.Connection): Pro offset: parseInt(binlogStatus.Position) }; + logger.info('Succesfully read executed GTID', { position }); + return new ReplicatedGTID({ // The head always points to the next position to start replication from position, diff --git a/modules/module-mysql/src/replication/BinLogReplicationJob.ts b/modules/module-mysql/src/replication/BinLogReplicationJob.ts index 2350b727f..15e96fe17 100644 --- a/modules/module-mysql/src/replication/BinLogReplicationJob.ts +++ b/modules/module-mysql/src/replication/BinLogReplicationJob.ts @@ -1,6 +1,6 @@ import { container } from '@powersync/lib-services-framework'; import { replication } from '@powersync/service-core'; -import { MysqlBinLogStream } from './MysqlBinLogStream.js'; +import { BinLogStream } from './BinLogStream.js'; import { MySQLConnectionManagerFactory } from './MySQLConnectionManagerFactory.js'; export interface BinLogReplicationJobOptions extends replication.AbstractReplicationJobOptions { @@ -66,7 +66,7 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob { if (this.isStopped) { return; } - const stream = new MysqlBinLogStream({ + const stream = new BinLogStream({ abortSignal: this.abortController.signal, storage: this.options.storage, connections: connectionManager diff --git a/modules/module-mysql/src/replication/MysqlBinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts similarity index 83% rename from modules/module-mysql/src/replication/MysqlBinLogStream.ts rename to modules/module-mysql/src/replication/BinLogStream.ts index 100901023..741403c10 100644 --- a/modules/module-mysql/src/replication/MysqlBinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -2,7 +2,7 @@ import { logger } from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import async from 'async'; -import { framework, getUuidReplicaIdentityBson, storage } from '@powersync/service-core'; +import { framework, getUuidReplicaIdentityBson, Metrics, storage } from '@powersync/service-core'; import mysql from 'mysql2'; import { BinLogEvent } from '@powersync/mysql-zongji'; @@ -10,6 +10,7 @@ import * as common from '../common/common-index.js'; import * as zongji_utils from './zongji/zongji-utils.js'; import { MySQLConnectionManager } from './MySQLConnectionManager.js'; import { ReplicatedGTID } from '../common/common-index.js'; +import mysqlPromise from 'mysql2/promise'; export interface BinLogStreamOptions { connections: MySQLConnectionManager; @@ -22,6 +23,15 @@ interface MysqlRelId { name: string; } +interface WriteChangePayload { + type: storage.SaveOperationTag; + data: Data; + previous_data?: Data; + database: string; + table: string; + sourceTable: storage.SourceTable; +} + export type Data = Record; /** @@ -32,9 +42,9 @@ function getMysqlRelId(source: MysqlRelId): string { return `${source.schema}.${source.name}`; } -export class MysqlBinLogStream { - private readonly sync_rules: sync_rules.SqlSyncRules; - private readonly group_id: number; +export class BinLogStream { + private readonly syncRules: sync_rules.SqlSyncRules; + private readonly groupId: number; private readonly storage: storage.SyncRulesBucketStorage; @@ -42,13 +52,13 @@ export class MysqlBinLogStream { private abortSignal: AbortSignal; - private relation_cache = new Map(); + private tableCache = new Map(); constructor(protected options: BinLogStreamOptions) { this.storage = options.storage; this.connections = options.connections; - this.sync_rules = options.storage.getParsedSyncRules({ defaultSchema: this.defaultSchema }); - this.group_id = options.storage.group_id; + this.syncRules = options.storage.getParsedSyncRules({ defaultSchema: this.defaultSchema }); + this.groupId = options.storage.group_id; this.abortSignal = options.abortSignal; } @@ -71,13 +81,13 @@ export class MysqlBinLogStream { async handleRelation(batch: storage.BucketStorageBatch, entity: storage.SourceEntityDescriptor, snapshot: boolean) { const result = await this.storage.resolveTable({ - group_id: this.group_id, + group_id: this.groupId, connection_id: this.connectionId, connection_tag: this.connectionTag, entity_descriptor: entity, - sync_rules: this.sync_rules + sync_rules: this.syncRules }); - this.relation_cache.set(entity.objectId, result.table); + this.tableCache.set(entity.objectId, result.table); // Drop conflicting tables. This includes for example renamed tables. await batch.drop(result.dropTables); @@ -96,7 +106,7 @@ export class MysqlBinLogStream { // Start the snapshot inside a transaction. // We use a dedicated connection for this. const connection = await this.connections.getStreamingConnection(); - const promiseConnection = connection.connection.promise(); + const promiseConnection = (connection as mysql.Connection).promise(); try { await promiseConnection.query('BEGIN'); try { @@ -168,7 +178,7 @@ AND table_type = 'BASE TABLE';`, const connection = await this.connections.getConnection(); const replicationColumns = await common.getReplicationIdentityColumns({ - db: connection, + connection: connection, schema: tablePattern.schema, table_name: tablePattern.name }); @@ -212,21 +222,23 @@ AND table_type = 'BASE TABLE';`, await this.storage.clear(); // Replication will be performed in a single transaction on this connection const connection = await this.connections.getStreamingConnection(); - const promiseConnection = connection.connection.promise(); + const promiseConnection = (connection as mysql.Connection).promise(); const headGTID = await common.readExecutedGtid(promiseConnection); - logger.info(`Using GTID:: '${headGTID}'`); + logger.info(`Using snapshot checkpoint GTID:: '${headGTID}'`); try { logger.info(`Starting initial replication`); - await promiseConnection.query('START TRANSACTION'); - await promiseConnection.query('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ACCESS_MODE READ ONLY'); - const sourceTables = this.sync_rules.getSourceTables(); + await promiseConnection.query( + 'SET TRANSACTION ISOLATION LEVEL REPEATABLE READ, READ ONLY' + ); + await promiseConnection.query('START TRANSACTION'); + const sourceTables = this.syncRules.getSourceTables(); await this.storage.startBatch( { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, async (batch) => { for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, tablePattern); for (let table of tables) { - await this.snapshotTable(connection.connection, batch, table); + await this.snapshotTable(connection as mysql.Connection, batch, table); await batch.markSnapshotDone([table], headGTID.comparable); await framework.container.probes.touch(); } @@ -235,9 +247,9 @@ AND table_type = 'BASE TABLE';`, } ); logger.info(`Initial replication done`); - await connection.query('COMMIT'); + await promiseConnection.query('COMMIT'); } catch (e) { - await connection.query('ROLLBACK'); + await promiseConnection.query('ROLLBACK'); throw e; } finally { connection.release(); @@ -250,16 +262,17 @@ AND table_type = 'BASE TABLE';`, table: storage.SourceTable ) { logger.info(`Replicating ${table.qualifiedName}`); - // TODO count rows + // TODO count rows and log progress at certain batch sizes return new Promise((resolve, reject) => { // MAX_EXECUTION_TIME(0) hint disables execution timeout for this query connection .query(`SELECT /*+ MAX_EXECUTION_TIME(0) */ * FROM ${table.schema}.${table.table}`) + .stream() .on('error', (err) => { reject(err); }) - .on('result', async (row) => { + .on('data', async (row) => { connection.pause(); const record = common.toSQLiteRow(row); @@ -272,6 +285,8 @@ AND table_type = 'BASE TABLE';`, afterReplicaId: getUuidReplicaIdentityBson(record, table.replicaIdColumns) }); connection.resume(); + // TODO: These metrics can probably be reported in batches + Metrics.getInstance().rows_replicated_total.add(1); }) .on('end', async function () { await batch.flush(); @@ -304,7 +319,7 @@ AND table_type = 'BASE TABLE';`, } private getTable(tableId: string): storage.SourceTable { - const table = this.relation_cache.get(tableId); + const table = this.tableCache.get(tableId); if (table == null) { // We should always receive a replication message before the relation is used. // If we can't find it, it's a bug. @@ -319,6 +334,8 @@ AND table_type = 'BASE TABLE';`, const connection = await this.connections.getConnection(); const { checkpoint_lsn } = await this.storage.getStatus(); + logger.info(`Last known LSN from storage: ${checkpoint_lsn}`); + const fromGTID = checkpoint_lsn ? common.ReplicatedGTID.fromSerialized(checkpoint_lsn) : await common.readExecutedGtid(connection); @@ -402,6 +419,7 @@ AND table_type = 'BASE TABLE';`, }); break; case zongji_utils.eventIsXid(evt): + Metrics.getInstance().transactions_replicated_total.add(1); // Need to commit with a replicated GTID with updated next position await batch.commit( new common.ReplicatedGTID({ @@ -419,9 +437,11 @@ AND table_type = 'BASE TABLE';`, }, 1); zongji.on('binlog', (evt: BinLogEvent) => { + logger.info(`Pushing Binlog event ${evt.getEventName()}`); queue.push(evt); }); + logger.info(`Starting replication from ${binLogPositionState.filename}:${binLogPositionState.offset}`); zongji.start({ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'], excludeEvents: [], @@ -476,51 +496,47 @@ AND table_type = 'BASE TABLE';`, private async writeChange( batch: storage.BucketStorageBatch, - msg: { - type: storage.SaveOperationTag; - data: Data; - previous_data?: Data; - database: string; - table: string; - sourceTable: storage.SourceTable; - } + payload: WriteChangePayload ): Promise { - switch (msg.type) { + switch (payload.type) { case storage.SaveOperationTag.INSERT: - const record = common.toSQLiteRow(msg.data); + Metrics.getInstance().rows_replicated_total.add(1); + const record = common.toSQLiteRow(payload.data); return await batch.save({ tag: storage.SaveOperationTag.INSERT, - sourceTable: msg.sourceTable, + sourceTable: payload.sourceTable, before: undefined, beforeReplicaId: undefined, after: record, - afterReplicaId: getUuidReplicaIdentityBson(record, msg.sourceTable.replicaIdColumns) + afterReplicaId: getUuidReplicaIdentityBson(record, payload.sourceTable.replicaIdColumns) }); case storage.SaveOperationTag.UPDATE: + Metrics.getInstance().rows_replicated_total.add(1); // "before" may be null if the replica id columns are unchanged // It's fine to treat that the same as an insert. - const beforeUpdated = msg.previous_data ? common.toSQLiteRow(msg.previous_data) : undefined; - const after = common.toSQLiteRow(msg.data); + const beforeUpdated = payload.previous_data ? common.toSQLiteRow(payload.previous_data) : undefined; + const after = common.toSQLiteRow(payload.data); return await batch.save({ tag: storage.SaveOperationTag.UPDATE, - sourceTable: msg.sourceTable, + sourceTable: payload.sourceTable, before: beforeUpdated, beforeReplicaId: beforeUpdated - ? getUuidReplicaIdentityBson(beforeUpdated, msg.sourceTable.replicaIdColumns) + ? getUuidReplicaIdentityBson(beforeUpdated, payload.sourceTable.replicaIdColumns) : undefined, - after: common.toSQLiteRow(msg.data), - afterReplicaId: getUuidReplicaIdentityBson(after, msg.sourceTable.replicaIdColumns) + after: common.toSQLiteRow(payload.data), + afterReplicaId: getUuidReplicaIdentityBson(after, payload.sourceTable.replicaIdColumns) }); case storage.SaveOperationTag.DELETE: - const beforeDeleted = common.toSQLiteRow(msg.data); + Metrics.getInstance().rows_replicated_total.add(1); + const beforeDeleted = common.toSQLiteRow(payload.data); return await batch.save({ tag: storage.SaveOperationTag.DELETE, - sourceTable: msg.sourceTable, + sourceTable: payload.sourceTable, before: beforeDeleted, - beforeReplicaId: getUuidReplicaIdentityBson(beforeDeleted, msg.sourceTable.replicaIdColumns), + beforeReplicaId: getUuidReplicaIdentityBson(beforeDeleted, payload.sourceTable.replicaIdColumns), after: undefined, afterReplicaId: undefined }); diff --git a/modules/module-mysql/src/replication/MySQLConnectionManager.ts b/modules/module-mysql/src/replication/MySQLConnectionManager.ts index d673d47af..a2e88a722 100644 --- a/modules/module-mysql/src/replication/MySQLConnectionManager.ts +++ b/modules/module-mysql/src/replication/MySQLConnectionManager.ts @@ -1,4 +1,4 @@ -import { ResolvedConnectionConfig } from '../types/types.js'; +import { NormalizedMySQLConnectionConfig } from '../types/types.js'; import mysqlPromise from 'mysql2/promise'; import mysql, { RowDataPacket } from 'mysql2'; import * as mysql_utils from '../utils/mysql_utils.js'; @@ -17,7 +17,7 @@ export class MySQLConnectionManager { private binlogListeners: ZongJi[] = []; constructor( - public options: ResolvedConnectionConfig, + public options: NormalizedMySQLConnectionConfig, public poolOptions: mysqlPromise.PoolOptions ) { // The pool is lazy - no connections are opened until a query is performed. @@ -101,10 +101,4 @@ export class MySQLConnectionManager { }); }); } - - destroy() { - for (const listener of this.binlogListeners) { - listener.stop(); - } - } } diff --git a/modules/module-mysql/src/types/types.ts b/modules/module-mysql/src/types/types.ts index 9c188bf80..3b812413f 100644 --- a/modules/module-mysql/src/types/types.ts +++ b/modules/module-mysql/src/types/types.ts @@ -59,7 +59,7 @@ export function normalizeConnectionConfig(options: MySQLConnectionConfig): Norma throw new Error(`Invalid URI - protocol must be mysql, got ${uri.scheme}`); } } else { - uri = urijs.parse('postgresql:///'); + uri = urijs.parse('mysql:///'); } const hostname = options.hostname ?? uri.host ?? ''; diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 349a2c877..0b3e64177 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -28,7 +28,7 @@ export async function retriedQuery(options: RetriedQueryOptions) { } } -export function createPool(config: types.ResolvedConnectionConfig, options?: mysql.PoolOptions): mysql.Pool { +export function createPool(config: types.NormalizedMySQLConnectionConfig, options?: mysql.PoolOptions): mysql.Pool { const sslOptions = { ca: config.cacert, key: config.client_private_key, From 18ce275e9a1ac406a540e102d3a56eb27e05238b Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 15 Oct 2024 20:45:29 +0200 Subject: [PATCH 243/298] Added BinlogStream tests (WiP) --- modules/module-mysql/package.json | 4 +- .../test/src/binlog_stream.test.ts | 288 ++++++++++++++++++ .../test/src/binlog_stream_utils.ts | 152 +++++++++ modules/module-mysql/test/src/env.ts | 7 + modules/module-mysql/test/src/setup.ts | 7 + modules/module-mysql/test/src/util.ts | 62 ++++ modules/module-mysql/test/tsconfig.json | 28 ++ modules/module-mysql/vitest.config.ts | 15 + 8 files changed, 562 insertions(+), 1 deletion(-) create mode 100644 modules/module-mysql/test/src/binlog_stream.test.ts create mode 100644 modules/module-mysql/test/src/binlog_stream_utils.ts create mode 100644 modules/module-mysql/test/src/env.ts create mode 100644 modules/module-mysql/test/src/setup.ts create mode 100644 modules/module-mysql/test/src/util.ts create mode 100644 modules/module-mysql/test/tsconfig.json create mode 100644 modules/module-mysql/vitest.config.ts diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 4c4f42b07..250c00e35 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -8,7 +8,9 @@ "type": "module", "scripts": { "build": "tsc -b", - "clean": "rm -rf ./lib && tsc -b --clean" + "build:tests": "tsc -b test/tsconfig.json", + "clean": "rm -rf ./lib && tsc -b --clean", + "test": "vitest" }, "exports": { ".": { diff --git a/modules/module-mysql/test/src/binlog_stream.test.ts b/modules/module-mysql/test/src/binlog_stream.test.ts new file mode 100644 index 000000000..1e6a21aa4 --- /dev/null +++ b/modules/module-mysql/test/src/binlog_stream.test.ts @@ -0,0 +1,288 @@ +import { putOp, removeOp } from '@core-tests/stream_utils.js'; +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; +import { BucketStorageFactory, Metrics } from '@powersync/service-core'; +import * as crypto from 'crypto'; +import { describe, expect, test } from 'vitest'; +import { binlogStreamTest } from './binlog_stream_utils.js'; +import { logger } from '@powersync/lib-services-framework'; + +type StorageFactory = () => Promise; + +const BASIC_SYNC_RULES = ` +bucket_definitions: + global: + data: + - SELECT id, description FROM "test_data" +`; + +describe( + ' Binlog stream - mongodb', + function () { + defineBinlogStreamTests(MONGO_STORAGE_FACTORY); + }, + { timeout: 20_000 } +); + +function defineBinlogStreamTests(factory: StorageFactory) { + test( + 'Replicate basic values', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT id, description, num FROM "test_data"`); + + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, num BIGINT)` + ); + + await context.replicateSnapshot(); + + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + context.startStreaming(); + await connectionManager.query(`INSERT INTO test_data(description, num) VALUES('test1', 1152921504606846976)`); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test1' AND num = 1152921504606846976` + ); + const testId = result.test_id; + logger.info('Finished Inserting data with id:' + testId); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1', num: 1152921504606846976n })]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + expect(endRowCount - startRowCount).toEqual(1); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); + + // test( + // 'replicating case sensitive table', + // binlogStreamTest(factory, async (context) => { + // const { connectionManager } = context; + // await context.updateSyncRules(` + // bucket_definitions: + // global: + // data: + // - SELECT id, description FROM "test_DATA" + // `); + + // await connectionManager.query( + // `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + // ); + + // await context.replicateSnapshot(); + + // const startRowCount = + // (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + // const startTxCount = + // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + // context.startStreaming(); + + // await connectionManager.query(`INSERT INTO test_DATA(description) VALUES('test1')`); + // const [[result]] = await connectionManager.query( + // `SELECT id AS test_id FROM test_data WHERE description = 'test1'` + // ); + // const testId = result.test_id; + + // const data = await context.getBucketData('global[]'); + + // expect(data).toMatchObject([putOp('test_DATA', { id: testId, description: 'test1' })]); + // const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + // const endTxCount = + // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + // expect(endRowCount - startRowCount).toEqual(1); + // expect(endTxCount - startTxCount).toEqual(1); + // }) + // ); + + // // TODO: Not supported yet + // // test( + // // 'replicating TRUNCATE', + // // binlogStreamTest(factory, async (context) => { + // // const { connectionManager } = context; + // // const syncRuleContent = ` + // // bucket_definitions: + // // global: + // // data: + // // - SELECT id, description FROM "test_data" + // // by_test_data: + // // parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id + // // data: [] + // // `; + // // await context.updateSyncRules(syncRuleContent); + // // await connectionManager.query(`DROP TABLE IF EXISTS test_data`); + // // await connectionManager.query( + // // `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)` + // // ); + // // + // // await context.replicateSnapshot(); + // // context.startStreaming(); + // // + // // const [{ test_id }] = pgwireRows( + // // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) + // // ); + // // await connectionManager.query(`TRUNCATE test_data`); + // // + // // const data = await context.getBucketData('global[]'); + // // + // // expect(data).toMatchObject([ + // // putOp('test_data', { id: test_id, description: 'test1' }), + // // removeOp('test_data', test_id) + // // ]); + // // }) + // // ); + + // test( + // 'replicating changing primary key', + // binlogStreamTest(factory, async (context) => { + // const { connectionManager } = context; + // await context.updateSyncRules(BASIC_SYNC_RULES); + + // await connectionManager.query( + // `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + // ); + + // await context.replicateSnapshot(); + // context.startStreaming(); + + // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); + // const [[result1]] = await connectionManager.query( + // `SELECT id AS test_id FROM test_data WHERE description = 'test1'` + // ); + // const testId1 = result1.test_id; + + // await connectionManager.query(`UPDATE test_data SET id = UUID(), description = 'test2a' WHERE id = '${testId1}'`); + // const [[result2]] = await connectionManager.query( + // `SELECT id AS test_id FROM test_data WHERE description = 'test2a'` + // ); + // const testId2 = result2.test_id; + + // // This update may fail replicating with: + // // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} + // await connectionManager.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${testId2}'`); + + // // Re-use old id again + // await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId1}', 'test1b')`); + // await connectionManager.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${testId1}'`); + + // const data = await context.getBucketData('global[]'); + // expect(data).toMatchObject([ + // // Initial insert + // putOp('test_data', { id: testId1, description: 'test1' }), + // // Update id, then description + // removeOp('test_data', testId1), + // putOp('test_data', { id: testId2, description: 'test2a' }), + // putOp('test_data', { id: testId2, description: 'test2b' }), + // // Re-use old id + // putOp('test_data', { id: testId1, description: 'test1b' }), + // putOp('test_data', { id: testId1, description: 'test1c' }) + // ]); + // }) + // ); + + // test( + // 'initial sync', + // binlogStreamTest(factory, async (context) => { + // const { connectionManager } = context; + // await context.updateSyncRules(BASIC_SYNC_RULES); + + // await connectionManager.query( + // `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + // ); + + // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); + // const [[result]] = await connectionManager.query( + // `SELECT id AS test_id FROM test_data WHERE description = 'test1'` + // ); + // const testId = result.test_id; + + // await context.replicateSnapshot(); + // context.startStreaming(); + + // const data = await context.getBucketData('global[]'); + // expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1' })]); + // }) + // ); + + // // test( + // // 'record too large', + // // binlogStreamTest(factory, async (context) => { + // // await context.updateSyncRules(`bucket_definitions: + // // global: + // // data: + // // - SELECT id, description, other FROM "test_data"`); + // // const { connectionManager } = context; + // // + // // await connectionManager.query(`CREATE TABLE test_data(id text primary key, description text, other text)`); + // // + // // await context.replicateSnapshot(); + // // + // // // 4MB + // // const largeDescription = crypto.randomBytes(2_000_000).toString('hex'); + // // // 18MB + // // const tooLargeDescription = crypto.randomBytes(9_000_000).toString('hex'); + // // + // // await connectionManager.query({ + // // statement: `INSERT INTO test_data(id, description, other) VALUES('t1', $1, 'foo')`, + // // params: [{ type: 'varchar', value: tooLargeDescription }] + // // }); + // // await connectionManager.query({ + // // statement: `UPDATE test_data SET description = $1 WHERE id = 't1'`, + // // params: [{ type: 'varchar', value: largeDescription }] + // // }); + // // + // // context.startStreaming(); + // // + // // const data = await context.getBucketData('global[]'); + // // expect(data.length).toEqual(1); + // // const row = JSON.parse(data[0].data as string); + // // delete row.description; + // // expect(row).toEqual({ id: 't1', other: 'foo' }); + // // delete data[0].data; + // // expect(data[0]).toMatchObject({ object_id: 't1', object_type: 'test_data', op: 'PUT', op_id: '1' }); + // // }) + // // ); + + // test( + // 'table not in sync rules', + // binlogStreamTest(factory, async (context) => { + // const { connectionManager } = context; + // await context.updateSyncRules(BASIC_SYNC_RULES); + + // await connectionManager.query( + // `CREATE TABLE test_donotsync (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + // ); + + // await context.replicateSnapshot(); + + // const startRowCount = + // (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + // const startTxCount = + // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + // context.startStreaming(); + + // await connectionManager.query(`INSERT INTO test_donotsync(description) VALUES('test1')`); + // const data = await context.getBucketData('global[]'); + + // expect(data).toMatchObject([]); + // const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + // const endTxCount = + // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + // // There was a transaction, but we should not replicate any actual data + // expect(endRowCount - startRowCount).toEqual(0); + // expect(endTxCount - startTxCount).toEqual(1); + // }) + // ); +} diff --git a/modules/module-mysql/test/src/binlog_stream_utils.ts b/modules/module-mysql/test/src/binlog_stream_utils.ts new file mode 100644 index 000000000..fca736a45 --- /dev/null +++ b/modules/module-mysql/test/src/binlog_stream_utils.ts @@ -0,0 +1,152 @@ +import { ActiveCheckpoint, BucketStorageFactory, OpId, SyncRulesBucketStorage } from '@powersync/service-core'; +import { TEST_CONNECTION_OPTIONS, clearAndRecreateTestDb } from './util.js'; +import { fromAsync } from '@core-tests/stream_utils.js'; +import { BinLogStream, BinLogStreamOptions } from '@module/replication/BinLogStream.js'; +import { MySQLConnectionManager } from '@module/replication/MySQLConnectionManager.js'; +import mysqlPromise from 'mysql2/promise'; +import { readExecutedGtid } from '@module/common/read-executed-gtid.js'; +import { logger } from '@powersync/lib-services-framework'; + +/** + * Tests operating on the binlog stream need to configure the stream and manage asynchronous + * replication, which gets a little tricky. + * + * This wraps a test in a function that configures all the context, and tears it down afterward. + */ +export function binlogStreamTest( + factory: () => Promise, + test: (context: BinlogStreamTestContext) => Promise +): () => Promise { + return async () => { + const f = await factory(); + const connectionManager = new MySQLConnectionManager(TEST_CONNECTION_OPTIONS, {}); + + const connection = await connectionManager.getConnection(); + await clearAndRecreateTestDb(connection); + connection.release(); + const context = new BinlogStreamTestContext(f, connectionManager); + try { + await test(context); + } finally { + await context.dispose(); + } + }; +} + +export class BinlogStreamTestContext { + private _binlogStream?: BinLogStream; + private abortController = new AbortController(); + private streamPromise?: Promise; + public storage?: SyncRulesBucketStorage; + private replicationDone = false; + + constructor( + public factory: BucketStorageFactory, + public connectionManager: MySQLConnectionManager + ) {} + + async dispose() { + this.abortController.abort(); + await this.streamPromise; + await this.connectionManager.end(); + } + + get connectionTag() { + return this.connectionManager.connectionTag; + } + + async updateSyncRules(content: string) { + const syncRules = await this.factory.updateSyncRules({ content: content }); + this.storage = this.factory.getInstance(syncRules); + return this.storage!; + } + + get binlogStream() { + if (this.storage == null) { + throw new Error('updateSyncRules() first'); + } + if (this._binlogStream) { + return this._binlogStream; + } + const options: BinLogStreamOptions = { + storage: this.storage, + connections: this.connectionManager, + abortSignal: this.abortController.signal + }; + this._binlogStream = new BinLogStream(options); + return this._binlogStream!; + } + + async replicateSnapshot() { + await this.binlogStream.initReplication(); + this.replicationDone = true; + } + + startStreaming() { + if (!this.replicationDone) { + throw new Error('Call replicateSnapshot() before startStreaming()'); + } + this.streamPromise = this.binlogStream.streamChanges(); + } + + async getCheckpoint(options?: { timeout?: number }) { + const connection = await this.connectionManager.getConnection(); + let checkpoint = await Promise.race([ + getClientCheckpoint(connection, this.factory, { timeout: options?.timeout ?? 60_000 }), + this.streamPromise + ]); + connection.release(); + if (typeof checkpoint == undefined) { + // This indicates an issue with the test setup - streamingPromise completed instead + // of getClientCheckpoint() + throw new Error('Test failure - streamingPromise completed'); + } + return checkpoint as string; + } + + async getBucketsDataBatch(buckets: Record, options?: { timeout?: number }) { + let checkpoint = await this.getCheckpoint(options); + const map = new Map(Object.entries(buckets)); + return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map)); + } + + async getBucketData(bucket: string, start?: string, options?: { timeout?: number }) { + start ??= '0'; + let checkpoint = await this.getCheckpoint(options); + const map = new Map([[bucket, start]]); + const batch = this.storage!.getBucketDataBatch(checkpoint, map); + const batches = await fromAsync(batch); + return batches[0]?.batch.data ?? []; + } +} + +export async function getClientCheckpoint( + connection: mysqlPromise.Connection, + bucketStorage: BucketStorageFactory, + options?: { timeout?: number } +): Promise { + const start = Date.now(); + const gtid = await readExecutedGtid(connection); + // This old API needs a persisted checkpoint id. + // Since we don't use LSNs anymore, the only way to get that is to wait. + + const timeout = options?.timeout ?? 50_000; + let lastCp: ActiveCheckpoint | null = null; + + logger.info('Expected Checkpoint: ' + gtid.comparable); + while (Date.now() - start < timeout) { + const cp = await bucketStorage.getActiveCheckpoint(); + lastCp = cp; + //logger.info('Last Checkpoint: ' + lastCp.lsn); + if (!cp.hasSyncRules()) { + throw new Error('No sync rules available'); + } + if (cp.lsn && cp.lsn >= gtid.comparable) { + return cp.checkpoint; + } + + await new Promise((resolve) => setTimeout(resolve, 30)); + } + + throw new Error(`Timeout while waiting for checkpoint ${gtid.comparable}. Last checkpoint: ${lastCp?.lsn}`); +} diff --git a/modules/module-mysql/test/src/env.ts b/modules/module-mysql/test/src/env.ts new file mode 100644 index 000000000..3dad20a22 --- /dev/null +++ b/modules/module-mysql/test/src/env.ts @@ -0,0 +1,7 @@ +import { utils } from '@powersync/lib-services-framework'; + +export const env = utils.collectEnvironmentVariables({ + MYSQL_TEST_URI: utils.type.string.default('mysql://myuser:mypassword@localhost:3306/mydatabase'), + CI: utils.type.boolean.default('false'), + SLOW_TESTS: utils.type.boolean.default('false') +}); diff --git a/modules/module-mysql/test/src/setup.ts b/modules/module-mysql/test/src/setup.ts new file mode 100644 index 000000000..b924cf736 --- /dev/null +++ b/modules/module-mysql/test/src/setup.ts @@ -0,0 +1,7 @@ +import { container } from '@powersync/lib-services-framework'; +import { beforeAll } from 'vitest'; + +beforeAll(() => { + // Executes for every test file + container.registerDefaults(); +}); diff --git a/modules/module-mysql/test/src/util.ts b/modules/module-mysql/test/src/util.ts new file mode 100644 index 000000000..a489e0b1b --- /dev/null +++ b/modules/module-mysql/test/src/util.ts @@ -0,0 +1,62 @@ +import * as types from '@module/types/types.js'; +import { BucketStorageFactory, Metrics, MongoBucketStorage } from '@powersync/service-core'; +import { env } from './env.js'; +import mysqlPromise from 'mysql2/promise'; +import { connectMongo } from '@core-tests/util.js'; +import { getMySQLVersion } from '@module/common/check-source-configuration.js'; +import { gte } from 'semver'; +import { RowDataPacket } from 'mysql2'; + +// The metrics need to be initialized before they can be used +await Metrics.initialise({ + disable_telemetry_sharing: true, + powersync_instance_id: 'test', + internal_metrics_endpoint: 'unused.for.tests.com' +}); +Metrics.getInstance().resetCounters(); + +export const TEST_URI = env.MYSQL_TEST_URI; + +export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ + type: 'mysql', + uri: TEST_URI +}); + +export type StorageFactory = () => Promise; + +export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { + const db = await connectMongo(); + + // None of the tests insert data into this collection, so it was never created + if (!(await db.db.listCollections({ name: db.bucket_parameters.collectionName }).hasNext())) { + await db.db.createCollection('bucket_parameters'); + } + + await db.clear(); + + return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); +}; + +export async function clearAndRecreateTestDb(connection: mysqlPromise.Connection) { + const version = await getMySQLVersion(connection); + if (gte(version, '8.4.0')) { + await connection.query('RESET BINARY LOGS AND GTIDS'); + } else { + await connection.query('RESET MASTER'); + } + + // await connection.query(`DROP DATABASE IF EXISTS ${TEST_CONNECTION_OPTIONS.database}`); + // + // await connection.query(`CREATE DATABASE IF NOT EXISTS ${TEST_CONNECTION_OPTIONS.database}`); + + const [result] = await connection.query( + `SELECT TABLE_NAME FROM information_schema.tables + WHERE TABLE_SCHEMA = '${TEST_CONNECTION_OPTIONS.database}'` + ); + for (let row of result) { + const name = row.TABLE_NAME; + if (name.startsWith('test_')) { + await connection.query(`DROP TABLE ${name}`); + } + } +} diff --git a/modules/module-mysql/test/tsconfig.json b/modules/module-mysql/test/tsconfig.json new file mode 100644 index 000000000..18898c4ee --- /dev/null +++ b/modules/module-mysql/test/tsconfig.json @@ -0,0 +1,28 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "baseUrl": "./", + "noEmit": true, + "esModuleInterop": true, + "skipLibCheck": true, + "sourceMap": true, + "paths": { + "@/*": ["../../../packages/service-core/src/*"], + "@module/*": ["../src/*"], + "@core-tests/*": ["../../../packages/service-core/test/src/*"] + } + }, + "include": ["src"], + "references": [ + { + "path": "../" + }, + { + "path": "../../../packages/service-core/test" + }, + { + "path": "../../../packages/service-core/" + } + ] +} diff --git a/modules/module-mysql/vitest.config.ts b/modules/module-mysql/vitest.config.ts new file mode 100644 index 000000000..7a39c1f71 --- /dev/null +++ b/modules/module-mysql/vitest.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vitest/config'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [tsconfigPaths()], + test: { + setupFiles: './test/src/setup.ts', + poolOptions: { + threads: { + singleThread: true + } + }, + pool: 'threads' + } +}); From 20d4b0dc65a35d8cde872cc8e03173b643c956fb Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 15 Oct 2024 20:46:16 +0200 Subject: [PATCH 244/298] Initializing batch in constructor of MongoBucketBatch --- packages/service-core/src/storage/mongo/MongoBucketBatch.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index 81c03e3e5..873ee9493 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -71,6 +71,7 @@ export class MongoBucketBatch implements BucketStorageBatch { this.session = this.client.startSession(); this.last_checkpoint_lsn = last_checkpoint_lsn; this.no_checkpoint_before_lsn = no_checkpoint_before_lsn; + this.batch = new OperationBatch(); } async flush(): Promise { From 203a7374e6bedacd4cb9f69a1a3ea2b89b0ec6b1 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 15 Oct 2024 21:14:27 +0200 Subject: [PATCH 245/298] Some MongoModule merge conflict handling and cleanup --- .../src/api/MongoRouteAPIAdapter.ts | 4 +- .../src/replication/ChangeStream.ts | 14 +- .../replication/ChangeStreamReplicationJob.ts | 3 - .../src/replication/ChangeStreamReplicator.ts | 4 +- modules/module-mongodb/src/types/types.ts | 2 +- .../test/src/change_stream.test.ts | 24 +-- .../test/src/change_stream_utils.ts | 4 +- .../src/api/MySQLRouteAPIAdapter.ts | 2 +- .../src/api/PostgresRouteAPIAdapter.ts | 2 +- pnpm-lock.yaml | 178 +++++++++++++++++- 10 files changed, 203 insertions(+), 34 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index d58d2cf58..dd611ce9b 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -33,7 +33,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { await this.client.close(); } - async getSourceConfig(): Promise { + async getSourceConfig(): Promise { return this.config; } @@ -165,7 +165,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { return result; } - async getReplicationLag(syncRulesId: string): Promise { + async getReplicationLag(options: api.ReplicationLagOptions): Promise { // There is no fast way to get replication lag in bytes in MongoDB. // We can get replication lag in seconds, but need a different API for that. return undefined; diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 3d36e1814..b952a9d4d 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -1,6 +1,6 @@ import { container, logger } from '@powersync/lib-services-framework'; -import { Metrics, SourceEntityDescriptor, SourceTable, storage } from '@powersync/service-core'; -import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern, toSyncRulesRow } from '@powersync/service-sync-rules'; +import { Metrics, SaveOperationTag, SourceEntityDescriptor, SourceTable, storage } from '@powersync/service-core'; +import { DatabaseInputRow, SqliteRow, SqlSyncRules, TablePattern } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; import { MongoManager } from './MongoManager.js'; import { @@ -248,7 +248,7 @@ export class ChangeStream { // This auto-flushes when the batch reaches its size limit await batch.save({ - tag: 'insert', + tag: SaveOperationTag.INSERT, sourceTable: table, before: undefined, beforeReplicaId: undefined, @@ -330,7 +330,7 @@ export class ChangeStream { if (change.operationType == 'insert') { const baseRecord = constructAfterRecord(change.fullDocument); return await batch.save({ - tag: 'insert', + tag: SaveOperationTag.INSERT, sourceTable: table, before: undefined, beforeReplicaId: undefined, @@ -341,7 +341,7 @@ export class ChangeStream { if (change.fullDocument == null) { // Treat as delete return await batch.save({ - tag: 'delete', + tag: SaveOperationTag.DELETE, sourceTable: table, before: undefined, beforeReplicaId: change.documentKey._id @@ -349,7 +349,7 @@ export class ChangeStream { } const after = constructAfterRecord(change.fullDocument!); return await batch.save({ - tag: 'update', + tag: SaveOperationTag.UPDATE, sourceTable: table, before: undefined, beforeReplicaId: undefined, @@ -358,7 +358,7 @@ export class ChangeStream { }); } else if (change.operationType == 'delete') { return await batch.save({ - tag: 'delete', + tag: SaveOperationTag.DELETE, sourceTable: table, before: undefined, beforeReplicaId: change.documentKey._id diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts index fb60e7aa7..06583d571 100644 --- a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts @@ -1,5 +1,4 @@ import { container } from '@powersync/lib-services-framework'; -import { MongoManager } from './MongoManager.js'; import { MissingReplicationSlotError, ChangeStream } from './ChangeStream.js'; import { replication } from '@powersync/service-core'; @@ -13,12 +12,10 @@ export interface ChangeStreamReplicationJobOptions extends replication.AbstractR export class ChangeStreamReplicationJob extends replication.AbstractReplicationJob { private connectionFactory: ConnectionManagerFactory; - private readonly connectionManager: MongoManager; constructor(options: ChangeStreamReplicationJobOptions) { super(options); this.connectionFactory = options.connectionFactory; - this.connectionManager = this.connectionFactory.create(); } async cleanUp(): Promise { diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts index 84b2c7f68..2cf96c494 100644 --- a/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicator.ts @@ -3,14 +3,14 @@ import { ChangeStreamReplicationJob } from './ChangeStreamReplicationJob.js'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; import { MongoErrorRateLimiter } from './MongoErrorRateLimiter.js'; -export interface WalStreamReplicatorOptions extends replication.AbstractReplicatorOptions { +export interface ChangeStreamReplicatorOptions extends replication.AbstractReplicatorOptions { connectionFactory: ConnectionManagerFactory; } export class ChangeStreamReplicator extends replication.AbstractReplicator { private readonly connectionFactory: ConnectionManagerFactory; - constructor(options: WalStreamReplicatorOptions) { + constructor(options: ChangeStreamReplicatorOptions) { super(options); this.connectionFactory = options.connectionFactory; } diff --git a/modules/module-mongodb/src/types/types.ts b/modules/module-mongodb/src/types/types.ts index 572a8b4dd..ac7873876 100644 --- a/modules/module-mongodb/src/types/types.ts +++ b/modules/module-mongodb/src/types/types.ts @@ -15,7 +15,7 @@ export interface NormalizedMongoConnectionConfig { password?: string; } -export const MongoConnectionConfig = service_types.configFile.dataSourceConfig.and( +export const MongoConnectionConfig = service_types.configFile.DataSourceConfig.and( t.object({ type: t.literal(MONGO_CONNECTION_TYPE), /** Unique identifier for the connection - optional when a single connection is present. */ diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index f950e4f35..2b5c949d4 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -3,7 +3,7 @@ import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; import { BucketStorageFactory } from '@powersync/service-core'; import * as crypto from 'crypto'; import { describe, expect, test } from 'vitest'; -import { walStreamTest } from './change_stream_utils.js'; +import { changeStreamTest } from './change_stream_utils.js'; import * as mongo from 'mongodb'; import { setTimeout } from 'node:timers/promises'; @@ -19,15 +19,15 @@ bucket_definitions: describe( 'change stream - mongodb', function () { - defineWalStreamTests(MONGO_STORAGE_FACTORY); + defineChangeStreamTests(MONGO_STORAGE_FACTORY); }, { timeout: 20_000 } ); -function defineWalStreamTests(factory: StorageFactory) { +function defineChangeStreamTests(factory: StorageFactory) { test( 'replicating basic values', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db } = context; await context.updateSyncRules(` bucket_definitions: @@ -66,7 +66,7 @@ bucket_definitions: test( 'no fullDocument available', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db, client } = context; await context.updateSyncRules(` bucket_definitions: @@ -111,7 +111,7 @@ bucket_definitions: test( 'replicating case sensitive table', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db } = context; await context.updateSyncRules(` bucket_definitions: @@ -136,7 +136,7 @@ bucket_definitions: test( 'replicating large values', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db } = context; await context.updateSyncRules(` bucket_definitions: @@ -168,7 +168,7 @@ bucket_definitions: test( 'replicating dropCollection', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db } = context; const syncRuleContent = ` bucket_definitions: @@ -200,7 +200,7 @@ bucket_definitions: test( 'replicating renameCollection', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db } = context; const syncRuleContent = ` bucket_definitions: @@ -232,7 +232,7 @@ bucket_definitions: test( 'initial sync', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db } = context; await context.updateSyncRules(BASIC_SYNC_RULES); @@ -251,7 +251,7 @@ bucket_definitions: // Not correctly implemented yet test.skip( 'large record', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { await context.updateSyncRules(`bucket_definitions: global: data: @@ -287,7 +287,7 @@ bucket_definitions: test( 'table not in sync rules', - walStreamTest(factory, async (context) => { + changeStreamTest(factory, async (context) => { const { db } = context; await context.updateSyncRules(BASIC_SYNC_RULES); diff --git a/modules/module-mongodb/test/src/change_stream_utils.ts b/modules/module-mongodb/test/src/change_stream_utils.ts index 19148695e..e533c56ca 100644 --- a/modules/module-mongodb/test/src/change_stream_utils.ts +++ b/modules/module-mongodb/test/src/change_stream_utils.ts @@ -8,12 +8,12 @@ import * as mongo from 'mongodb'; import { createCheckpoint } from '@module/replication/MongoRelation.js'; /** - * Tests operating on the wal stream need to configure the stream and manage asynchronous + * Tests operating on the mongo change stream need to configure the stream and manage asynchronous * replication, which gets a little tricky. * * This wraps a test in a function that configures all the context, and tears it down afterwards. */ -export function walStreamTest( +export function changeStreamTest( factory: () => Promise, test: (context: ChangeStreamTestContext) => Promise ): () => Promise { diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index 17e584c25..eb9c272af 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -249,7 +249,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { }; } - async getReplicationLag(options: api.ReplicationLagOptions): Promise { + async getReplicationLag(options: api.ReplicationLagOptions): Promise { const { bucketStorage } = options; const lastCheckpoint = await bucketStorage.getCheckpoint(); diff --git a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts index f3c7b9aef..11cd1cdbd 100644 --- a/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts +++ b/modules/module-postgres/src/api/PostgresRouteAPIAdapter.ts @@ -203,7 +203,7 @@ export class PostgresRouteAPIAdapter implements api.RouteAPI { }); } - async getReplicationLag(options: api.ReplicationLagOptions): Promise { + async getReplicationLag(options: api.ReplicationLagOptions): Promise { const { bucketStorage: slotName } = options; const results = await pg_utils.retriedQuery(this.pool, { statement: `SELECT diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1c1a27f3..39c640137 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -137,6 +137,61 @@ importers: specifier: ^0.34.6 version: 0.34.6 + modules/module-mysql: + dependencies: + '@powersync/lib-services-framework': + specifier: workspace:* + version: link:../../libs/lib-services + '@powersync/mysql-zongji': + specifier: 0.0.0-dev-20241008105633 + version: 0.0.0-dev-20241008105633 + '@powersync/service-core': + specifier: workspace:* + version: link:../../packages/service-core + '@powersync/service-sync-rules': + specifier: workspace:* + version: link:../../packages/sync-rules + '@powersync/service-types': + specifier: workspace:* + version: link:../../packages/types + async: + specifier: ^3.2.4 + version: 3.2.5 + mysql2: + specifier: ^3.11.0 + version: 3.11.3 + semver: + specifier: ^7.5.4 + version: 7.6.2 + ts-codec: + specifier: ^1.2.2 + version: 1.2.2 + uri-js: + specifier: ^4.4.1 + version: 4.4.1 + uuid: + specifier: ^9.0.1 + version: 9.0.1 + devDependencies: + '@types/async': + specifier: ^3.2.24 + version: 3.2.24 + '@types/semver': + specifier: ^7.5.4 + version: 7.5.8 + '@types/uuid': + specifier: ^9.0.8 + version: 9.0.8 + typescript: + specifier: ^5.5.4 + version: 5.6.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) + vitest: + specifier: ^0.34.6 + version: 0.34.6 + modules/module-postgres: dependencies: '@powersync/lib-services-framework': @@ -422,6 +477,9 @@ importers: '@powersync/service-module-mongodb': specifier: workspace:* version: link:../modules/module-mongodb + '@powersync/service-module-mysql': + specifier: workspace:* + version: link:../modules/module-mysql '@powersync/service-module-postgres': specifier: workspace:* version: link:../modules/module-postgres @@ -1124,6 +1182,10 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} + '@powersync/mysql-zongji@0.0.0-dev-20241008105633': + resolution: {integrity: sha512-HNKV9etnhQ7VqpHJHqyW94OSNIOprWDRmhLR1iPsE7DYyvrDexk28Tc8bnxc0ti2JfwO47rwWrP/C1F1bfTXcw==} + engines: {node: '>=20.0.0'} + '@prisma/instrumentation@5.16.1': resolution: {integrity: sha512-4m5gRFWnQb8s/yTyGbMZkL7A5uJgqOWcWJxapwcAD0T0kh5sGPEVSQl/zTQvE9aduXhFAxOtC3gO+R8Hb5xO1Q==} @@ -1404,6 +1466,10 @@ packages: '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} + '@vlasky/mysql@2.18.6': + resolution: {integrity: sha512-c+qz/zzqecteQLchoje0E0rjLla935d6hHPpMKmfyQJnHlycLpR49ekS6s/zUAt8w0Um5hFglKXm4+PeJTVhaQ==} + engines: {node: '>= 0.6'} + abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -1544,6 +1610,10 @@ packages: avvio@8.3.2: resolution: {integrity: sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==} + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1560,6 +1630,13 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + big-integer@1.6.51: + resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} + engines: {node: '>=0.6'} + + bignumber.js@9.1.1: + resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1840,6 +1917,10 @@ packages: delegates@1.0.0: resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} @@ -2064,6 +2145,9 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} deprecated: This package is no longer supported. + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -2296,6 +2380,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -2439,6 +2526,9 @@ packages: resolution: {integrity: sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==} engines: {node: '>= 12.0.0'} + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + lossless-json@2.0.11: resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} @@ -2462,6 +2552,10 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lru.min@1.1.1: + resolution: {integrity: sha512-FbAj6lXil6t8z4z3j0E5mfRlPzxkySotzUHwRXjlpRh10vc6AI6WN62ehZj82VG7M20rqogJ0GLwar2Xa05a8Q==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} @@ -2620,6 +2714,14 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mysql2@3.11.3: + resolution: {integrity: sha512-Qpu2ADfbKzyLdwC/5d4W7+5Yz7yBzCU05YWt5npWzACST37wJsB23wgOSo00qi043urkiRwXtEvJc9UnuLX/MQ==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3037,6 +3139,9 @@ packages: readable-stream@1.0.34: resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + readable-stream@2.3.7: + resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} + readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -3188,6 +3293,9 @@ packages: engines: {node: '>=10'} hasBin: true + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -3309,6 +3417,10 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + ssri@10.0.6: resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -4609,6 +4721,12 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + '@powersync/mysql-zongji@0.0.0-dev-20241008105633': + dependencies: + '@vlasky/mysql': 2.18.6 + big-integer: 1.6.51 + iconv-lite: 0.6.3 + '@prisma/instrumentation@5.16.1': dependencies: '@opentelemetry/api': 1.8.0 @@ -4696,7 +4814,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4704,7 +4822,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -4905,6 +5023,13 @@ snapshots: loupe: 3.1.1 tinyrainbow: 1.2.0 + '@vlasky/mysql@2.18.6': + dependencies: + bignumber.js: 9.1.1 + readable-stream: 2.3.7 + safe-buffer: 5.2.1 + sqlstring: 2.3.3 + abbrev@1.1.1: {} abort-controller@3.0.0: @@ -5023,6 +5148,8 @@ snapshots: '@fastify/error': 3.4.1 fastq: 1.17.1 + aws-ssl-profiles@1.1.2: {} + balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -5040,6 +5167,10 @@ snapshots: dependencies: is-windows: 1.0.2 + big-integer@1.6.51: {} + + bignumber.js@9.1.1: {} + binary-extensions@2.3.0: {} bl@4.1.0: @@ -5370,6 +5501,8 @@ snapshots: delegates@1.0.0: {} + denque@2.1.0: {} + detect-indent@6.1.0: {} diff-sequences@29.6.3: {} @@ -5621,6 +5754,10 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + get-caller-file@2.0.5: {} get-func-name@2.0.2: {} @@ -5747,7 +5884,6 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - optional: true ieee754@1.2.1: {} @@ -5858,6 +5994,8 @@ snapshots: is-path-inside@3.0.3: {} + is-property@1.0.2: {} + is-stream@2.0.1: {} is-subdir@1.2.0: @@ -5982,6 +6120,8 @@ snapshots: safe-stable-stringify: 2.4.3 triple-beam: 1.4.1 + long@5.2.3: {} + lossless-json@2.0.11: {} loupe@2.3.7: @@ -6003,6 +6143,8 @@ snapshots: lru-cache@7.18.3: {} + lru.min@1.1.1: {} + magic-string@0.30.11: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -6170,6 +6312,22 @@ snapshots: mute-stream@1.0.0: {} + mysql2@3.11.3: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.2.3 + lru.min: 1.1.1 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + nanoid@3.3.7: {} nearley@2.20.1: @@ -6650,6 +6808,16 @@ snapshots: isarray: 0.0.1 string_decoder: 0.10.31 + readable-stream@2.3.7: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -6805,6 +6973,8 @@ snapshots: semver@7.6.2: {} + seq-queue@0.0.5: {} + set-blocking@2.0.0: {} set-cookie-parser@2.6.0: {} @@ -6920,6 +7090,8 @@ snapshots: sprintf-js@1.1.3: {} + sqlstring@2.3.3: {} + ssri@10.0.6: dependencies: minipass: 7.1.2 From e2182166828aa4174736e386abf6d5ffecebc627 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 15 Oct 2024 21:38:47 +0200 Subject: [PATCH 246/298] Made mysql module publishing public --- modules/module-mysql/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 250c00e35..b3021b4e5 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -6,6 +6,9 @@ "license": "FSL-1.1-Apache-2.0", "main": "dist/index.js", "type": "module", + "publishConfig": { + "access": "public" + }, "scripts": { "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", From 8befdceb7e7ce58ced0a7327a9803445e738434a Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 15 Oct 2024 21:52:04 +0200 Subject: [PATCH 247/298] Updated dockerfile --- service/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/service/Dockerfile b/service/Dockerfile index 715931b11..b214a4851 100644 --- a/service/Dockerfile +++ b/service/Dockerfile @@ -19,6 +19,7 @@ COPY libs/lib-services/package.json libs/lib-services/tsconfig.json libs/lib-ser COPY modules/module-postgres/package.json modules/module-postgres/tsconfig.json modules/module-postgres/ COPY modules/module-mongodb/package.json modules/module-mongodb/tsconfig.json modules/module-mongodb/ +COPY modules/module-mongodb/package.json modules/module-mongodb/tsconfig.json modules/module-mysql/ RUN pnpm install --frozen-lockfile @@ -36,6 +37,7 @@ COPY libs/lib-services/src libs/lib-services/src/ COPY modules/module-postgres/src modules/module-postgres/src/ COPY modules/module-mongodb/src modules/module-mongodb/src/ +COPY modules/module-mongodb/src modules/module-mysql/src/ RUN pnpm build:production && \ rm -rf node_modules **/node_modules && \ From c2bd479e79231d4fb0f003e282c708a94b860948 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 15 Oct 2024 22:33:34 +0200 Subject: [PATCH 248/298] Added mysql module to service tsconfig --- service/tsconfig.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service/tsconfig.json b/service/tsconfig.json index 40c9a5329..1576a67cc 100644 --- a/service/tsconfig.json +++ b/service/tsconfig.json @@ -35,6 +35,9 @@ }, { "path": "../modules/module-mongodb" + }, + { + "path": "../modules/module-mysql" } ] } From 38737d43ba79dfb23d6417669a3879bb52ff6390 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 16 Oct 2024 09:32:33 +0200 Subject: [PATCH 249/298] Fixed dockerfile mysql module copy --- service/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/Dockerfile b/service/Dockerfile index b214a4851..8dfa0a7db 100644 --- a/service/Dockerfile +++ b/service/Dockerfile @@ -19,7 +19,7 @@ COPY libs/lib-services/package.json libs/lib-services/tsconfig.json libs/lib-ser COPY modules/module-postgres/package.json modules/module-postgres/tsconfig.json modules/module-postgres/ COPY modules/module-mongodb/package.json modules/module-mongodb/tsconfig.json modules/module-mongodb/ -COPY modules/module-mongodb/package.json modules/module-mongodb/tsconfig.json modules/module-mysql/ +COPY modules/module-mysql/package.json modules/module-mysql/tsconfig.json modules/module-mysql/ RUN pnpm install --frozen-lockfile @@ -37,7 +37,7 @@ COPY libs/lib-services/src libs/lib-services/src/ COPY modules/module-postgres/src modules/module-postgres/src/ COPY modules/module-mongodb/src modules/module-mongodb/src/ -COPY modules/module-mongodb/src modules/module-mysql/src/ +COPY modules/module-mysql/src modules/module-mysql/src/ RUN pnpm build:production && \ rm -rf node_modules **/node_modules && \ From 62ffaaca585bc0c4110ae31a5cf113a9fb4cd9f7 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 16 Oct 2024 13:22:11 +0200 Subject: [PATCH 250/298] Updated zongji dev package version Some cleanup of modules package.json --- modules/module-mongodb/package.json | 2 +- modules/module-mysql/package.json | 4 ++-- modules/module-postgres/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- tsconfig.json | 3 +++ 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/modules/module-mongodb/package.json b/modules/module-mongodb/package.json index 5809bed99..51269ec8d 100644 --- a/modules/module-mongodb/package.json +++ b/modules/module-mongodb/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", - "clean": "rm -rf ./lib && tsc -b --clean", + "clean": "rm -rf ./dist && tsc -b --clean", "test": "vitest --no-threads" }, "exports": { diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index b3021b4e5..0763ee523 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", - "clean": "rm -rf ./lib && tsc -b --clean", + "clean": "rm -rf ./dist && tsc -b --clean", "test": "vitest" }, "exports": { @@ -32,7 +32,7 @@ "@powersync/service-core": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@powersync/mysql-zongji": "0.0.0-dev-20241008105633", + "@powersync/mysql-zongji": "0.0.0-dev-20241016072406", "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index aad70aba3..d42db9204 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", - "clean": "rm -rf ./lib && tsc -b --clean", + "clean": "rm -rf ./dist && tsc -b --clean", "test": "vitest" }, "exports": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39c640137..e7f8e05e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: workspace:* version: link:../../libs/lib-services '@powersync/mysql-zongji': - specifier: 0.0.0-dev-20241008105633 - version: 0.0.0-dev-20241008105633 + specifier: 0.0.0-dev-20241016072406 + version: 0.0.0-dev-20241016072406 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core @@ -1182,8 +1182,8 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@powersync/mysql-zongji@0.0.0-dev-20241008105633': - resolution: {integrity: sha512-HNKV9etnhQ7VqpHJHqyW94OSNIOprWDRmhLR1iPsE7DYyvrDexk28Tc8bnxc0ti2JfwO47rwWrP/C1F1bfTXcw==} + '@powersync/mysql-zongji@0.0.0-dev-20241016072406': + resolution: {integrity: sha512-hStCDtNXzSzK71g68okWHpY7Z/APDc69C4PPblB3xGeudPXWMtnyCmgT+iHVSoW6mMqnBjTCrjYSXN3e4/zc3A==} engines: {node: '>=20.0.0'} '@prisma/instrumentation@5.16.1': @@ -4721,7 +4721,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/mysql-zongji@0.0.0-dev-20241008105633': + '@powersync/mysql-zongji@0.0.0-dev-20241016072406': dependencies: '@vlasky/mysql': 2.18.6 big-integer: 1.6.51 diff --git a/tsconfig.json b/tsconfig.json index 3c19076fc..542ecf4b2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,9 @@ { "path": "./modules/module-mysql" }, + { + "path": "./modules/module-mongodb" + }, { "path": "./libs/lib-services" }, From c11de662b0374d78f6fe2b8425498ac274d9710c Mon Sep 17 00:00:00 2001 From: Steven Ontong Date: Wed, 16 Oct 2024 16:19:22 +0200 Subject: [PATCH 251/298] updates after pulling in main branch --- .../service-core/src/storage/MongoBucketStorage.ts | 2 +- packages/service-core/test/src/data_storage.test.ts | 2 +- packages/service-core/test/src/sync.test.ts | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index e0b3fe2b5..9da62758c 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -340,7 +340,7 @@ export class MongoBucketStorage } }; - const active_sync_rules = await this.getActiveSyncRules(); + const active_sync_rules = await this.getActiveSyncRules({ defaultSchema: 'public' }); if (active_sync_rules == null) { return { operations_size_bytes: 0, diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index be14c0dab..05885b704 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -1450,7 +1450,7 @@ bucket_definitions: }); const r = await f.configureSyncRules('bucket_definitions: {}'); - const storage = f.getInstance(r.persisted_sync_rules!.parsed()); + const storage = f.getInstance(r.persisted_sync_rules!); await storage.autoActivate(); const metrics2 = await f.getStorageMetrics(); diff --git a/packages/service-core/test/src/sync.test.ts b/packages/service-core/test/src/sync.test.ts index 77a7f0f2e..606647038 100644 --- a/packages/service-core/test/src/sync.test.ts +++ b/packages/service-core/test/src/sync.test.ts @@ -30,7 +30,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = f.getInstance(syncRules); await storage.autoActivate(); const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { @@ -81,7 +81,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.autoActivate(); const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => { @@ -125,7 +125,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.autoActivate(); const stream = streamResponse({ @@ -152,7 +152,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.autoActivate(); const stream = streamResponse({ @@ -213,7 +213,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.autoActivate(); const exp = Date.now() / 1000 + 0.1; @@ -251,7 +251,7 @@ function defineTests(factory: StorageFactory) { content: BASIC_SYNC_RULES }); - const storage = await f.getInstance(syncRules.parsed()); + const storage = await f.getInstance(syncRules); await storage.autoActivate(); await storage.startBatch(BATCH_OPTIONS, async (batch) => { From 1ff485d312425e7994b1d12d3e59b7de48f384c0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 18 Oct 2024 09:37:18 +0200 Subject: [PATCH 252/298] Updated vitest --- modules/module-mongodb/package.json | 2 +- modules/module-mysql/package.json | 6 +- .../src/storage/MongoBucketStorage.ts | 4 +- pnpm-lock.yaml | 324 +----------------- 4 files changed, 15 insertions(+), 321 deletions(-) diff --git a/modules/module-mongodb/package.json b/modules/module-mongodb/package.json index 51269ec8d..16b65a4ab 100644 --- a/modules/module-mongodb/package.json +++ b/modules/module-mongodb/package.json @@ -41,7 +41,7 @@ "devDependencies": { "@types/uuid": "^9.0.4", "typescript": "^5.2.2", - "vitest": "^0.34.6", + "vitest": "^2.1.1", "vite-tsconfig-paths": "^4.3.2" } } diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 0763ee523..b07ed8dae 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -32,7 +32,7 @@ "@powersync/service-core": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@powersync/mysql-zongji": "0.0.0-dev-20241016072406", + "@powersync/mysql-zongji": "0.0.0-dev-20241016125109", "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", @@ -43,9 +43,9 @@ "devDependencies": { "@types/semver": "^7.5.4", "@types/async": "^3.2.24", - "@types/uuid": "^9.0.8", + "@types/uuid": "^9.0.4", "typescript": "^5.5.4", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^0.34.6" + "vitest": "^2.1.1" } } diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index c0254da71..1877423c7 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -21,7 +21,7 @@ import { UpdateSyncRulesOptions, WriteCheckpoint } from './BucketStorage.js'; -import { PowerSyncMongo, PowerSyncMongoOptions } from './mongo/db.js'; +import { PowerSyncMongo } from './mongo/db.js'; import { SyncRuleDocument, SyncRuleState } from './mongo/models.js'; import { MongoPersistedSyncRulesContent } from './mongo/MongoPersistedSyncRulesContent.js'; import { MongoSyncBucketStorage } from './mongo/MongoSyncBucketStorage.js'; @@ -36,8 +36,6 @@ import { WriteCheckpointMode } from './write-checkpoint.js'; -export interface MongoBucketStorageOptions extends PowerSyncMongoOptions {} - export class MongoBucketStorage extends DisposableObserver implements BucketStorageFactory diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7f8e05e0..5f9b62c2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,8 +134,8 @@ importers: specifier: ^4.3.2 version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) vitest: - specifier: ^0.34.6 - version: 0.34.6 + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) modules/module-mysql: dependencies: @@ -143,8 +143,8 @@ importers: specifier: workspace:* version: link:../../libs/lib-services '@powersync/mysql-zongji': - specifier: 0.0.0-dev-20241016072406 - version: 0.0.0-dev-20241016072406 + specifier: 0.0.0-dev-20241016125109 + version: 0.0.0-dev-20241016125109 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core @@ -180,7 +180,7 @@ importers: specifier: ^7.5.4 version: 7.5.8 '@types/uuid': - specifier: ^9.0.8 + specifier: ^9.0.4 version: 9.0.8 typescript: specifier: ^5.5.4 @@ -189,8 +189,8 @@ importers: specifier: ^4.3.2 version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) vitest: - specifier: ^0.34.6 - version: 0.34.6 + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) modules/module-postgres: dependencies: @@ -847,10 +847,6 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1182,8 +1178,8 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@powersync/mysql-zongji@0.0.0-dev-20241016072406': - resolution: {integrity: sha512-hStCDtNXzSzK71g68okWHpY7Z/APDc69C4PPblB3xGeudPXWMtnyCmgT+iHVSoW6mMqnBjTCrjYSXN3e4/zc3A==} + '@powersync/mysql-zongji@0.0.0-dev-20241016125109': + resolution: {integrity: sha512-lK0Ryq5JYNkfDOLjZwAgLmFdx5OTdPikFFOrUJ+pNrH2qyOho/rIP8PxBG2JUqw/NhA9IigtpyTa9bC0WLCbRA==} engines: {node: '>=20.0.0'} '@prisma/instrumentation@5.16.1': @@ -1311,9 +1307,6 @@ packages: resolution: {integrity: sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sindresorhus/is@5.6.0': resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} engines: {node: '>=14.16'} @@ -1352,12 +1345,6 @@ packages: '@types/async@3.2.24': resolution: {integrity: sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==} - '@types/chai-subset@1.3.5': - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} - - '@types/chai@4.3.20': - resolution: {integrity: sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==} - '@types/connect@3.4.36': resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==} @@ -1421,9 +1408,6 @@ packages: '@types/ws@8.2.3': resolution: {integrity: sha512-ahRJZquUYCdOZf/rCsWg88S0/+cb9wazUBHv6HZEe3XdYaBe2zr/slM8J28X07Hn88Pnm4ezo7N8/ofnOgrPVQ==} - '@vitest/expect@0.34.6': - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} - '@vitest/expect@2.1.1': resolution: {integrity: sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==} @@ -1442,27 +1426,15 @@ packages: '@vitest/pretty-format@2.1.1': resolution: {integrity: sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==} - '@vitest/runner@0.34.6': - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} - '@vitest/runner@2.1.1': resolution: {integrity: sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==} - '@vitest/snapshot@0.34.6': - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} - '@vitest/snapshot@2.1.1': resolution: {integrity: sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==} - '@vitest/spy@0.34.6': - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} - '@vitest/spy@2.1.1': resolution: {integrity: sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==} - '@vitest/utils@0.34.6': - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} - '@vitest/utils@2.1.1': resolution: {integrity: sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==} @@ -1557,10 +1529,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -1590,9 +1558,6 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - assertion-error@1.1.0: - resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1695,10 +1660,6 @@ packages: resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==} engines: {node: '>=14.16'} - chai@4.5.0: - resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} - engines: {node: '>=4'} - chai@5.1.1: resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} engines: {node: '>=12'} @@ -1718,9 +1679,6 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - check-error@1.0.3: - resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} - check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -1820,9 +1778,6 @@ packages: engines: {node: ^14.13.0 || >=16.0.0} hasBin: true - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} - config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -1895,10 +1850,6 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - deep-eql@4.1.4: - resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} - engines: {node: '>=6'} - deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -1925,10 +1876,6 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -2497,10 +2444,6 @@ packages: light-my-request@5.13.0: resolution: {integrity: sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==} - local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2532,9 +2475,6 @@ packages: lossless-json@2.0.11: resolution: {integrity: sha512-BP0vn+NGYvzDielvBZaFain/wgeJ1hTvURCqtKvhr1SCPePdaaTanmmcplrHfEJSJOUql7hk4FHwToNJjWRY3g==} - loupe@2.3.7: - resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} - loupe@3.1.1: resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} @@ -2658,9 +2598,6 @@ packages: engines: {node: '>=10'} hasBin: true - mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} - mnemonist@0.39.5: resolution: {integrity: sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==} @@ -2873,10 +2810,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -2943,9 +2876,6 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - pathval@1.1.1: - resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} - pathval@2.0.0: resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} @@ -2996,9 +2926,6 @@ packages: resolution: {integrity: sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==} hasBin: true - pkg-types@1.2.0: - resolution: {integrity: sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==} - postcss@8.4.39: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} @@ -3029,10 +2956,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - proc-log@3.0.0: resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3120,9 +3043,6 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - read-package-json-fast@3.0.2: resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3482,9 +3402,6 @@ packages: resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} engines: {node: '>=14.16'} - strip-literal@1.3.0: - resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -3527,10 +3444,6 @@ packages: tinyexec@0.3.0: resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} - tinypool@1.0.1: resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3539,10 +3452,6 @@ packages: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} - tinyspy@2.2.1: - resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==} - engines: {node: '>=14.0.0'} - tinyspy@3.0.2: resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} @@ -3630,10 +3539,6 @@ packages: resolution: {integrity: sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - type-detect@4.1.0: - resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} - engines: {node: '>=4'} - type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -3654,9 +3559,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - ufo@1.5.4: - resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} - undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} @@ -3719,11 +3621,6 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite-node@0.34.6: - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true - vite-node@2.1.1: resolution: {integrity: sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3765,37 +3662,6 @@ packages: terser: optional: true - vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true - vitest@2.1.1: resolution: {integrity: sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -3952,10 +3818,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - yoctocolors-cjs@2.1.2: resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} engines: {node: '>=18'} @@ -4247,10 +4109,6 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.0': {} @@ -4721,7 +4579,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/mysql-zongji@0.0.0-dev-20241016072406': + '@powersync/mysql-zongji@0.0.0-dev-20241016125109': dependencies: '@vlasky/mysql': 2.18.6 big-integer: 1.6.51 @@ -4860,8 +4718,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@sinclair/typebox@0.27.8': {} - '@sindresorhus/is@5.6.0': {} '@syncpoint/wkx@0.5.2': @@ -4891,12 +4747,6 @@ snapshots: '@types/async@3.2.24': {} - '@types/chai-subset@1.3.5': - dependencies: - '@types/chai': 4.3.20 - - '@types/chai@4.3.20': {} - '@types/connect@3.4.36': dependencies: '@types/node': 22.5.5 @@ -4955,12 +4805,6 @@ snapshots: dependencies: '@types/node': 22.5.5 - '@vitest/expect@0.34.6': - dependencies: - '@vitest/spy': 0.34.6 - '@vitest/utils': 0.34.6 - chai: 4.5.0 - '@vitest/expect@2.1.1': dependencies: '@vitest/spy': 2.1.1 @@ -4980,43 +4824,21 @@ snapshots: dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@0.34.6': - dependencies: - '@vitest/utils': 0.34.6 - p-limit: 4.0.0 - pathe: 1.1.2 - '@vitest/runner@2.1.1': dependencies: '@vitest/utils': 2.1.1 pathe: 1.1.2 - '@vitest/snapshot@0.34.6': - dependencies: - magic-string: 0.30.11 - pathe: 1.1.2 - pretty-format: 29.7.0 - '@vitest/snapshot@2.1.1': dependencies: '@vitest/pretty-format': 2.1.1 magic-string: 0.30.11 pathe: 1.1.2 - '@vitest/spy@0.34.6': - dependencies: - tinyspy: 2.2.1 - '@vitest/spy@2.1.1': dependencies: tinyspy: 3.0.2 - '@vitest/utils@0.34.6': - dependencies: - diff-sequences: 29.6.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - '@vitest/utils@2.1.1': dependencies: '@vitest/pretty-format': 2.1.1 @@ -5105,8 +4927,6 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} anymatch@3.1.3: @@ -5131,8 +4951,6 @@ snapshots: array-union@2.1.0: {} - assertion-error@1.1.0: {} - assertion-error@2.0.1: {} async-mutex@0.5.0: @@ -5271,16 +5089,6 @@ snapshots: camelcase@7.0.1: {} - chai@4.5.0: - dependencies: - assertion-error: 1.1.0 - check-error: 1.0.3 - deep-eql: 4.1.4 - get-func-name: 2.0.2 - loupe: 2.3.7 - pathval: 1.1.1 - type-detect: 4.1.0 - chai@5.1.1: dependencies: assertion-error: 2.0.1 @@ -5304,10 +5112,6 @@ snapshots: chardet@0.7.0: {} - check-error@1.0.3: - dependencies: - get-func-name: 2.0.2 - check-error@2.1.1: {} chokidar@3.6.0: @@ -5409,8 +5213,6 @@ snapshots: tree-kill: 1.2.2 yargs: 17.7.2 - confbox@0.1.7: {} - config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -5485,10 +5287,6 @@ snapshots: dependencies: mimic-response: 3.1.0 - deep-eql@4.1.4: - dependencies: - type-detect: 4.1.0 - deep-eql@5.0.2: {} deep-extend@0.6.0: {} @@ -5505,8 +5303,6 @@ snapshots: detect-indent@6.1.0: {} - diff-sequences@29.6.3: {} - diff@4.0.2: {} dir-glob@3.0.1: @@ -6090,8 +5886,6 @@ snapshots: process-warning: 3.0.0 set-cookie-parser: 2.6.0 - local-pkg@0.4.3: {} - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -6124,10 +5918,6 @@ snapshots: lossless-json@2.0.11: {} - loupe@2.3.7: - dependencies: - get-func-name: 2.0.2 - loupe@3.1.1: dependencies: get-func-name: 2.0.2 @@ -6276,13 +6066,6 @@ snapshots: mkdirp@1.0.4: {} - mlly@1.7.1: - dependencies: - acorn: 8.12.1 - pathe: 1.1.2 - pkg-types: 1.2.0 - ufo: 1.5.4 - mnemonist@0.39.5: dependencies: obliterator: 2.0.4 @@ -6544,10 +6327,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@4.0.0: - dependencies: - yocto-queue: 1.1.1 - p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -6618,8 +6397,6 @@ snapshots: pathe@1.1.2: {} - pathval@1.1.1: {} - pathval@2.0.0: {} pause-stream@0.0.11: @@ -6674,12 +6451,6 @@ snapshots: sonic-boom: 3.8.1 thread-stream: 2.7.0 - pkg-types@1.2.0: - dependencies: - confbox: 0.1.7 - mlly: 1.7.1 - pathe: 1.1.2 - postcss@8.4.39: dependencies: nanoid: 3.3.7 @@ -6700,12 +6471,6 @@ snapshots: prettier@3.3.3: {} - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - proc-log@3.0.0: {} process-nextick-args@2.0.1: {} @@ -6780,8 +6545,6 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-is@18.3.1: {} - read-package-json-fast@3.0.2: dependencies: json-parse-even-better-errors: 3.0.2 @@ -7148,10 +6911,6 @@ snapshots: strip-json-comments@5.0.1: {} - strip-literal@1.3.0: - dependencies: - acorn: 8.12.1 - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -7194,14 +6953,10 @@ snapshots: tinyexec@0.3.0: {} - tinypool@0.7.0: {} - tinypool@1.0.1: {} tinyrainbow@1.2.0: {} - tinyspy@2.2.1: {} - tinyspy@3.0.2: {} tmp@0.0.33: @@ -7291,8 +7046,6 @@ snapshots: transitivePeerDependencies: - supports-color - type-detect@4.1.0: {} - type-fest@0.21.3: {} type-fest@1.4.0: {} @@ -7305,8 +7058,6 @@ snapshots: typescript@5.6.2: {} - ufo@1.5.4: {} - undefsafe@2.0.5: {} undici-types@6.19.8: {} @@ -7371,24 +7122,6 @@ snapshots: vary@1.1.2: {} - vite-node@0.34.6(@types/node@22.5.5): - dependencies: - cac: 6.7.14 - debug: 4.3.7 - mlly: 1.7.1 - pathe: 1.1.2 - picocolors: 1.1.0 - vite: 5.3.3(@types/node@22.5.5) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - vite-node@2.1.1(@types/node@22.5.5): dependencies: cac: 6.7.14 @@ -7425,41 +7158,6 @@ snapshots: '@types/node': 22.5.5 fsevents: 2.3.3 - vitest@0.34.6: - dependencies: - '@types/chai': 4.3.20 - '@types/chai-subset': 1.3.5 - '@types/node': 22.5.5 - '@vitest/expect': 0.34.6 - '@vitest/runner': 0.34.6 - '@vitest/snapshot': 0.34.6 - '@vitest/spy': 0.34.6 - '@vitest/utils': 0.34.6 - acorn: 8.12.1 - acorn-walk: 8.3.3 - cac: 6.7.14 - chai: 4.5.0 - debug: 4.3.7 - local-pkg: 0.4.3 - magic-string: 0.30.11 - pathe: 1.1.2 - picocolors: 1.1.0 - std-env: 3.7.0 - strip-literal: 1.3.0 - tinybench: 2.9.0 - tinypool: 0.7.0 - vite: 5.3.3(@types/node@22.5.5) - vite-node: 0.34.6(@types/node@22.5.5) - why-is-node-running: 2.3.0 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - vitest@2.1.1(@types/node@22.5.5): dependencies: '@vitest/expect': 2.1.1 @@ -7622,8 +7320,6 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.1.1: {} - yoctocolors-cjs@2.1.2: {} zod@3.23.8: {} From 415019b084bf6a054ea07d892cc23f64a2102e45 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 18 Oct 2024 09:38:42 +0200 Subject: [PATCH 253/298] Added configuration error handling Filter out tables not in sync rules Improve abort logic handling --- .../src/replication/BinLogReplicationJob.ts | 15 +- .../src/replication/BinLogStream.ts | 268 ++++++------ .../src/replication/zongji/zongji.d.ts | 14 + .../test/src/binlog_stream.test.ts | 383 +++++++++--------- 4 files changed, 358 insertions(+), 322 deletions(-) diff --git a/modules/module-mysql/src/replication/BinLogReplicationJob.ts b/modules/module-mysql/src/replication/BinLogReplicationJob.ts index 15e96fe17..aa1a838b2 100644 --- a/modules/module-mysql/src/replication/BinLogReplicationJob.ts +++ b/modules/module-mysql/src/replication/BinLogReplicationJob.ts @@ -1,6 +1,6 @@ import { container } from '@powersync/lib-services-framework'; import { replication } from '@powersync/service-core'; -import { BinLogStream } from './BinLogStream.js'; +import { BinlogConfigurationError, BinLogStream } from './BinLogStream.js'; import { MySQLConnectionManagerFactory } from './MySQLConnectionManagerFactory.js'; export interface BinLogReplicationJobOptions extends replication.AbstractReplicationJobOptions { @@ -32,12 +32,6 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob { } }); this.logger.error(`Replication failed on ${this.slot_name}`, e); - - // Slot removal type error logic goes here - // if (e) { - // // This stops replication on this slot, and creates a new slot - // await this.options.storage.factory.slotRemoved(this.slot_name); - // } } finally { this.abortController.abort(); } @@ -73,12 +67,15 @@ export class BinLogReplicationJob extends replication.AbstractReplicationJob { }); await stream.replicate(); } catch (e) { + if (this.abortController.signal.aborted) { + return; + } this.logger.error(`Replication error`, e); if (e.cause != null) { this.logger.error(`cause`, e.cause); } - // TODO not recoverable error - if (false) { + + if (e instanceof BinlogConfigurationError) { throw e; } else { // Report the error if relevant, before retrying diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index 741403c10..51fb79d79 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -34,6 +34,12 @@ interface WriteChangePayload { export type Data = Record; +export class BinlogConfigurationError extends Error { + constructor(message: string) { + super(message); + } +} + /** * MySQL does not have same relation structure. Just returning unique key as string. * @param source @@ -301,6 +307,7 @@ AND table_type = 'BASE TABLE';`, // all connections automatically closed, including this one. await this.initReplication(); await this.streamChanges(); + logger.info('BinlogStream has been shut down'); } catch (e) { await this.storage.reportError(e); throw e; @@ -309,9 +316,13 @@ AND table_type = 'BASE TABLE';`, async initReplication() { const connection = await this.connections.getConnection(); - await common.checkSourceConfiguration(connection); + const errors = await common.checkSourceConfiguration(connection); connection.release(); + if (errors.length > 0) { + throw new BinlogConfigurationError(`Binlog Configuration Errors: ${errors.join(', ')}`); + } + const initialReplicationCompleted = await this.checkInitialReplicated(); if (!initialReplicationCompleted) { await this.startInitialReplication(); @@ -342,135 +353,150 @@ AND table_type = 'BASE TABLE';`, const binLogPositionState = fromGTID.position; connection.release(); - await this.storage.startBatch( - { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, - async (batch) => { - const zongji = this.connections.createBinlogListener(); - - let currentGTID: common.ReplicatedGTID | null = null; - - const queue = async.queue(async (evt: BinLogEvent) => { - // State machine - switch (true) { - case zongji_utils.eventIsGTIDLog(evt): - currentGTID = common.ReplicatedGTID.fromBinLogEvent({ - raw_gtid: { - server_id: evt.serverId, - transaction_range: evt.transactionRange - }, - position: { - filename: binLogPositionState.filename, - offset: evt.nextPosition - } - }); - break; - case zongji_utils.eventIsRotation(evt): - // Update the position - binLogPositionState.filename = evt.binlogName; - binLogPositionState.offset = evt.position; - break; - case zongji_utils.eventIsWriteMutation(evt): - // TODO, can multiple tables be present? - const writeTableInfo = evt.tableMap[evt.tableId]; - await this.writeChanges(batch, { - type: storage.SaveOperationTag.INSERT, - data: evt.rows, - database: writeTableInfo.parentSchema, - table: writeTableInfo.tableName, - sourceTable: this.getTable( - getMysqlRelId({ - schema: writeTableInfo.parentSchema, - name: writeTableInfo.tableName - }) - ) - }); - break; - case zongji_utils.eventIsUpdateMutation(evt): - const updateTableInfo = evt.tableMap[evt.tableId]; - await this.writeChanges(batch, { - type: storage.SaveOperationTag.UPDATE, - data: evt.rows.map((row) => row.after), - previous_data: evt.rows.map((row) => row.before), - database: updateTableInfo.parentSchema, - table: updateTableInfo.tableName, - sourceTable: this.getTable( - getMysqlRelId({ - schema: updateTableInfo.parentSchema, - name: updateTableInfo.tableName - }) - ) - }); - break; - case zongji_utils.eventIsDeleteMutation(evt): - // TODO, can multiple tables be present? - const deleteTableInfo = evt.tableMap[evt.tableId]; - await this.writeChanges(batch, { - type: storage.SaveOperationTag.DELETE, - data: evt.rows, - database: deleteTableInfo.parentSchema, - table: deleteTableInfo.tableName, - // TODO cleanup - sourceTable: this.getTable( - getMysqlRelId({ - schema: deleteTableInfo.parentSchema, - name: deleteTableInfo.tableName - }) - ) - }); - break; - case zongji_utils.eventIsXid(evt): - Metrics.getInstance().transactions_replicated_total.add(1); - // Need to commit with a replicated GTID with updated next position - await batch.commit( - new common.ReplicatedGTID({ - raw_gtid: currentGTID!.raw, + if (!this.stopped) { + await this.storage.startBatch( + { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, + async (batch) => { + const zongji = this.connections.createBinlogListener(); + + let currentGTID: common.ReplicatedGTID | null = null; + + const queue = async.queue(async (evt: BinLogEvent) => { + // State machine + switch (true) { + case zongji_utils.eventIsGTIDLog(evt): + currentGTID = common.ReplicatedGTID.fromBinLogEvent({ + raw_gtid: { + server_id: evt.serverId, + transaction_range: evt.transactionRange + }, position: { filename: binLogPositionState.filename, offset: evt.nextPosition } - }).comparable - ); - currentGTID = null; - // chunks_replicated_total.add(1); - break; - } - }, 1); - - zongji.on('binlog', (evt: BinLogEvent) => { - logger.info(`Pushing Binlog event ${evt.getEventName()}`); - queue.push(evt); - }); - - logger.info(`Starting replication from ${binLogPositionState.filename}:${binLogPositionState.offset}`); - zongji.start({ - includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'], - excludeEvents: [], - filename: binLogPositionState.filename, - position: binLogPositionState.offset - }); + }); + break; + case zongji_utils.eventIsRotation(evt): + // Update the position + binLogPositionState.filename = evt.binlogName; + binLogPositionState.offset = evt.position; + break; + case zongji_utils.eventIsWriteMutation(evt): + // TODO, can multiple tables be present? + const writeTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { + type: storage.SaveOperationTag.INSERT, + data: evt.rows, + database: writeTableInfo.parentSchema, + table: writeTableInfo.tableName, + sourceTable: this.getTable( + getMysqlRelId({ + schema: writeTableInfo.parentSchema, + name: writeTableInfo.tableName + }) + ) + }); + break; + case zongji_utils.eventIsUpdateMutation(evt): + const updateTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { + type: storage.SaveOperationTag.UPDATE, + data: evt.rows.map((row) => row.after), + previous_data: evt.rows.map((row) => row.before), + database: updateTableInfo.parentSchema, + table: updateTableInfo.tableName, + sourceTable: this.getTable( + getMysqlRelId({ + schema: updateTableInfo.parentSchema, + name: updateTableInfo.tableName + }) + ) + }); + break; + case zongji_utils.eventIsDeleteMutation(evt): + // TODO, can multiple tables be present? + const deleteTableInfo = evt.tableMap[evt.tableId]; + await this.writeChanges(batch, { + type: storage.SaveOperationTag.DELETE, + data: evt.rows, + database: deleteTableInfo.parentSchema, + table: deleteTableInfo.tableName, + // TODO cleanup + sourceTable: this.getTable( + getMysqlRelId({ + schema: deleteTableInfo.parentSchema, + name: deleteTableInfo.tableName + }) + ) + }); + break; + case zongji_utils.eventIsXid(evt): + Metrics.getInstance().transactions_replicated_total.add(1); + // Need to commit with a replicated GTID with updated next position + await batch.commit( + new common.ReplicatedGTID({ + raw_gtid: currentGTID!.raw, + position: { + filename: binLogPositionState.filename, + offset: evt.nextPosition + } + }).comparable + ); + currentGTID = null; + // chunks_replicated_total.add(1); + break; + } + }, 1); + + zongji.on('binlog', (evt: BinLogEvent) => { + if (!this.stopped) { + logger.info(`Pushing Binlog event ${evt.getEventName()}`); + queue.push(evt); + } else { + logger.info(`Abort signal detected, ignoring event ${evt.getEventName()}`); + } + }); - // Forever young - await new Promise((resolve, reject) => { - queue.error((error) => { - zongji.stop(); - queue.kill(); - reject(error); + // Only listen for changes to tables in the sync rules + const includedTables = [...this.tableCache.values()].map((table) => table.table); + logger.info(`Starting replication from ${binLogPositionState.filename}:${binLogPositionState.offset}`); + zongji.start({ + includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'], + excludeEvents: [], + includeSchema: { [this.defaultSchema]: includedTables }, + filename: binLogPositionState.filename, + position: binLogPositionState.offset }); - this.abortSignal.addEventListener( - 'abort', - async () => { + + // Forever young + await new Promise((resolve, reject) => { + queue.error((error) => { + logger.error('Queue error.', error); + zongji.stop(); + queue.kill(); + reject(error); + }); + if (!this.stopped) { + this.abortSignal.addEventListener( + 'abort', + () => { + logger.info('Abort signal received, stopping replication.'); + zongji.stop(); + queue.kill(); + resolve(); + }, + { once: true } + ); + } else { + logger.info('Process was already aborted.'); zongji.stop(); queue.kill(); - if (!queue.length) { - await queue.drain(); - } resolve(); - }, - { once: true } - ); - }); - } - ); + } + }); + } + ); + } } private async writeChanges( diff --git a/modules/module-mysql/src/replication/zongji/zongji.d.ts b/modules/module-mysql/src/replication/zongji/zongji.d.ts index e14ff3abe..9cae9c50d 100644 --- a/modules/module-mysql/src/replication/zongji/zongji.d.ts +++ b/modules/module-mysql/src/replication/zongji/zongji.d.ts @@ -5,9 +5,23 @@ declare module '@powersync/mysql-zongji' { password: string; }; + interface DatabaseFilter { + [databaseName: string]: string[] | true; + } + export type StartOptions = { includeEvents?: string[]; excludeEvents?: string[]; + /** + * Describe which databases and tables to include (Only for row events). Use database names as the key and pass an array of table names or true (for the entire database). + * Example: { 'my_database': ['allow_table', 'another_table'], 'another_db': true } + */ + includeSchema?: DatabaseFilter; + /** + * Object describing which databases and tables to exclude (Same format as includeSchema) + * Example: { 'other_db': ['disallowed_table'], 'ex_db': true } + */ + excludeSchema?: DatabaseFilter; /** * BinLog position filename to start reading events from */ diff --git a/modules/module-mysql/test/src/binlog_stream.test.ts b/modules/module-mysql/test/src/binlog_stream.test.ts index 1e6a21aa4..60dd743a2 100644 --- a/modules/module-mysql/test/src/binlog_stream.test.ts +++ b/modules/module-mysql/test/src/binlog_stream.test.ts @@ -29,10 +29,10 @@ function defineBinlogStreamTests(factory: StorageFactory) { binlogStreamTest(factory, async (context) => { const { connectionManager } = context; await context.updateSyncRules(` -bucket_definitions: - global: - data: - - SELECT id, description, num FROM "test_data"`); + bucket_definitions: + global: + data: + - SELECT id, description, num FROM "test_data"`); await connectionManager.query( `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, num BIGINT)` @@ -64,225 +64,224 @@ bucket_definitions: }) ); - // test( - // 'replicating case sensitive table', - // binlogStreamTest(factory, async (context) => { - // const { connectionManager } = context; - // await context.updateSyncRules(` - // bucket_definitions: - // global: - // data: - // - SELECT id, description FROM "test_DATA" - // `); + test( + 'replicating case sensitive table', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(` + bucket_definitions: + global: + data: + - SELECT id, description FROM "test_DATA" + `); - // await connectionManager.query( - // `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - // ); + await connectionManager.query( + `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); - // await context.replicateSnapshot(); + await context.replicateSnapshot(); - // const startRowCount = - // (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - // const startTxCount = - // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - // context.startStreaming(); + context.startStreaming(); - // await connectionManager.query(`INSERT INTO test_DATA(description) VALUES('test1')`); - // const [[result]] = await connectionManager.query( - // `SELECT id AS test_id FROM test_data WHERE description = 'test1'` - // ); - // const testId = result.test_id; + await connectionManager.query(`INSERT INTO test_DATA(description) VALUES('test1')`); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_DATA WHERE description = 'test1'` + ); + const testId = result.test_id; - // const data = await context.getBucketData('global[]'); + const data = await context.getBucketData('global[]'); - // expect(data).toMatchObject([putOp('test_DATA', { id: testId, description: 'test1' })]); - // const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - // const endTxCount = - // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - // expect(endRowCount - startRowCount).toEqual(1); - // expect(endTxCount - startTxCount).toEqual(1); - // }) - // ); + expect(data).toMatchObject([putOp('test_DATA', { id: testId, description: 'test1' })]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + expect(endRowCount - startRowCount).toEqual(1); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); - // // TODO: Not supported yet - // // test( - // // 'replicating TRUNCATE', - // // binlogStreamTest(factory, async (context) => { - // // const { connectionManager } = context; - // // const syncRuleContent = ` - // // bucket_definitions: - // // global: - // // data: - // // - SELECT id, description FROM "test_data" - // // by_test_data: - // // parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id - // // data: [] - // // `; - // // await context.updateSyncRules(syncRuleContent); - // // await connectionManager.query(`DROP TABLE IF EXISTS test_data`); - // // await connectionManager.query( - // // `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)` - // // ); - // // - // // await context.replicateSnapshot(); - // // context.startStreaming(); - // // - // // const [{ test_id }] = pgwireRows( - // // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) - // // ); - // // await connectionManager.query(`TRUNCATE test_data`); - // // - // // const data = await context.getBucketData('global[]'); - // // - // // expect(data).toMatchObject([ - // // putOp('test_data', { id: test_id, description: 'test1' }), - // // removeOp('test_data', test_id) - // // ]); - // // }) - // // ); + // TODO: Not supported yet + // test( + // 'replicating TRUNCATE', + // binlogStreamTest(factory, async (context) => { + // const { connectionManager } = context; + // const syncRuleContent = ` + // bucket_definitions: + // global: + // data: + // - SELECT id, description FROM "test_data" + // by_test_data: + // parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id + // data: [] + // `; + // await context.updateSyncRules(syncRuleContent); + // await connectionManager.query(`DROP TABLE IF EXISTS test_data`); + // await connectionManager.query( + // `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)` + // ); + // + // await context.replicateSnapshot(); + // context.startStreaming(); + // + // const [{ test_id }] = pgwireRows( + // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) + // ); + // await connectionManager.query(`TRUNCATE test_data`); + // + // const data = await context.getBucketData('global[]'); + // + // expect(data).toMatchObject([ + // putOp('test_data', { id: test_id, description: 'test1' }), + // removeOp('test_data', test_id) + // ]); + // }) + // ); - // test( - // 'replicating changing primary key', - // binlogStreamTest(factory, async (context) => { - // const { connectionManager } = context; - // await context.updateSyncRules(BASIC_SYNC_RULES); + test( + 'replicating changing primary key', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); - // await connectionManager.query( - // `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - // ); + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); - // await context.replicateSnapshot(); - // context.startStreaming(); + await context.replicateSnapshot(); + context.startStreaming(); - // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); - // const [[result1]] = await connectionManager.query( - // `SELECT id AS test_id FROM test_data WHERE description = 'test1'` - // ); - // const testId1 = result1.test_id; + await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); + const [[result1]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test1'` + ); + const testId1 = result1.test_id; - // await connectionManager.query(`UPDATE test_data SET id = UUID(), description = 'test2a' WHERE id = '${testId1}'`); - // const [[result2]] = await connectionManager.query( - // `SELECT id AS test_id FROM test_data WHERE description = 'test2a'` - // ); - // const testId2 = result2.test_id; + await connectionManager.query(`UPDATE test_data SET id = UUID(), description = 'test2a' WHERE id = '${testId1}'`); + const [[result2]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test2a'` + ); + const testId2 = result2.test_id; - // // This update may fail replicating with: - // // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} - // await connectionManager.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${testId2}'`); + // This update may fail replicating with: + // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} + await connectionManager.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${testId2}'`); - // // Re-use old id again - // await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId1}', 'test1b')`); - // await connectionManager.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${testId1}'`); + // Re-use old id again + await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId1}', 'test1b')`); + await connectionManager.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${testId1}'`); - // const data = await context.getBucketData('global[]'); - // expect(data).toMatchObject([ - // // Initial insert - // putOp('test_data', { id: testId1, description: 'test1' }), - // // Update id, then description - // removeOp('test_data', testId1), - // putOp('test_data', { id: testId2, description: 'test2a' }), - // putOp('test_data', { id: testId2, description: 'test2b' }), - // // Re-use old id - // putOp('test_data', { id: testId1, description: 'test1b' }), - // putOp('test_data', { id: testId1, description: 'test1c' }) - // ]); - // }) - // ); + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([ + // Initial insert + putOp('test_data', { id: testId1, description: 'test1' }), + // Update id, then description + removeOp('test_data', testId1), + putOp('test_data', { id: testId2, description: 'test2a' }), + putOp('test_data', { id: testId2, description: 'test2b' }), + // Re-use old id + putOp('test_data', { id: testId1, description: 'test1b' }), + putOp('test_data', { id: testId1, description: 'test1c' }) + ]); + }) + ); - // test( - // 'initial sync', - // binlogStreamTest(factory, async (context) => { - // const { connectionManager } = context; - // await context.updateSyncRules(BASIC_SYNC_RULES); + test( + 'initial sync', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); + + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); + + await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test1'` + ); + const testId = result.test_id; - // await connectionManager.query( - // `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - // ); + await context.replicateSnapshot(); - // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); - // const [[result]] = await connectionManager.query( - // `SELECT id AS test_id FROM test_data WHERE description = 'test1'` - // ); - // const testId = result.test_id; + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1' })]); + }) + ); + // test( + // 'record too large', + // binlogStreamTest(factory, async (context) => { + // await context.updateSyncRules(`bucket_definitions: + // global: + // data: + // - SELECT id, description, other FROM "test_data"`); + // const { connectionManager } = context; + // + // await connectionManager.query(`CREATE TABLE test_data(id text primary key, description text, other text)`); + // // await context.replicateSnapshot(); + // + // // 4MB + // const largeDescription = crypto.randomBytes(2_000_000).toString('hex'); + // // 18MB + // const tooLargeDescription = crypto.randomBytes(9_000_000).toString('hex'); + // + // await connectionManager.query({ + // statement: `INSERT INTO test_data(id, description, other) VALUES('t1', $1, 'foo')`, + // params: [{ type: 'varchar', value: tooLargeDescription }] + // }); + // await connectionManager.query({ + // statement: `UPDATE test_data SET description = $1 WHERE id = 't1'`, + // params: [{ type: 'varchar', value: largeDescription }] + // }); + // // context.startStreaming(); - + // // const data = await context.getBucketData('global[]'); - // expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1' })]); + // expect(data.length).toEqual(1); + // const row = JSON.parse(data[0].data as string); + // delete row.description; + // expect(row).toEqual({ id: 't1', other: 'foo' }); + // delete data[0].data; + // expect(data[0]).toMatchObject({ object_id: 't1', object_type: 'test_data', op: 'PUT', op_id: '1' }); // }) // ); - // // test( - // // 'record too large', - // // binlogStreamTest(factory, async (context) => { - // // await context.updateSyncRules(`bucket_definitions: - // // global: - // // data: - // // - SELECT id, description, other FROM "test_data"`); - // // const { connectionManager } = context; - // // - // // await connectionManager.query(`CREATE TABLE test_data(id text primary key, description text, other text)`); - // // - // // await context.replicateSnapshot(); - // // - // // // 4MB - // // const largeDescription = crypto.randomBytes(2_000_000).toString('hex'); - // // // 18MB - // // const tooLargeDescription = crypto.randomBytes(9_000_000).toString('hex'); - // // - // // await connectionManager.query({ - // // statement: `INSERT INTO test_data(id, description, other) VALUES('t1', $1, 'foo')`, - // // params: [{ type: 'varchar', value: tooLargeDescription }] - // // }); - // // await connectionManager.query({ - // // statement: `UPDATE test_data SET description = $1 WHERE id = 't1'`, - // // params: [{ type: 'varchar', value: largeDescription }] - // // }); - // // - // // context.startStreaming(); - // // - // // const data = await context.getBucketData('global[]'); - // // expect(data.length).toEqual(1); - // // const row = JSON.parse(data[0].data as string); - // // delete row.description; - // // expect(row).toEqual({ id: 't1', other: 'foo' }); - // // delete data[0].data; - // // expect(data[0]).toMatchObject({ object_id: 't1', object_type: 'test_data', op: 'PUT', op_id: '1' }); - // // }) - // // ); - - // test( - // 'table not in sync rules', - // binlogStreamTest(factory, async (context) => { - // const { connectionManager } = context; - // await context.updateSyncRules(BASIC_SYNC_RULES); + test( + 'table not in sync rules', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); - // await connectionManager.query( - // `CREATE TABLE test_donotsync (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - // ); + await connectionManager.query( + `CREATE TABLE test_donotsync (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); - // await context.replicateSnapshot(); + await context.replicateSnapshot(); - // const startRowCount = - // (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - // const startTxCount = - // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - // context.startStreaming(); + context.startStreaming(); - // await connectionManager.query(`INSERT INTO test_donotsync(description) VALUES('test1')`); - // const data = await context.getBucketData('global[]'); + await connectionManager.query(`INSERT INTO test_donotsync(description) VALUES('test1')`); + const data = await context.getBucketData('global[]'); - // expect(data).toMatchObject([]); - // const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - // const endTxCount = - // (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + expect(data).toMatchObject([]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - // // There was a transaction, but we should not replicate any actual data - // expect(endRowCount - startRowCount).toEqual(0); - // expect(endTxCount - startTxCount).toEqual(1); - // }) - // ); + // There was a transaction, but we should not replicate any actual data + expect(endRowCount - startRowCount).toEqual(0); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); } From b4b1f6e6cae8104bb63aa01f71ddaac567420c2b Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 18 Oct 2024 09:53:37 +0200 Subject: [PATCH 254/298] Don't start replication if already aborted --- .../src/replication/BinLogStream.ts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index 51fb79d79..de1dccffc 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -453,10 +453,14 @@ AND table_type = 'BASE TABLE';`, logger.info(`Pushing Binlog event ${evt.getEventName()}`); queue.push(evt); } else { - logger.info(`Abort signal detected, ignoring event ${evt.getEventName()}`); + logger.info(`Replication is busy stopping, ignoring event ${evt.getEventName()}`); } }); + if (this.stopped) { + // Powersync is shutting down, don't start replicating + return; + } // Only listen for changes to tables in the sync rules const includedTables = [...this.tableCache.values()].map((table) => table.table); logger.info(`Starting replication from ${binLogPositionState.filename}:${binLogPositionState.offset}`); @@ -476,23 +480,17 @@ AND table_type = 'BASE TABLE';`, queue.kill(); reject(error); }); - if (!this.stopped) { - this.abortSignal.addEventListener( - 'abort', - () => { - logger.info('Abort signal received, stopping replication.'); - zongji.stop(); - queue.kill(); - resolve(); - }, - { once: true } - ); - } else { - logger.info('Process was already aborted.'); - zongji.stop(); - queue.kill(); - resolve(); - } + + this.abortSignal.addEventListener( + 'abort', + () => { + logger.info('Abort signal received, stopping replication...'); + zongji.stop(); + queue.kill(); + resolve(); + }, + { once: true } + ); }); } ); From 049669b9d8e07fbda85e455a83af6ba6ff7359f0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 21 Oct 2024 20:41:40 +0200 Subject: [PATCH 255/298] Exposed DateStrings connection option in Zongji constructor Using updated binlog listener package --- modules/module-mysql/package.json | 2 +- modules/module-mysql/src/replication/zongji/zongji.d.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index b07ed8dae..824a40636 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -32,7 +32,7 @@ "@powersync/service-core": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@powersync/mysql-zongji": "0.0.0-dev-20241016125109", + "@powersync/mysql-zongji": "0.0.0-dev-20241021144804", "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", diff --git a/modules/module-mysql/src/replication/zongji/zongji.d.ts b/modules/module-mysql/src/replication/zongji/zongji.d.ts index 9cae9c50d..771a3ff8e 100644 --- a/modules/module-mysql/src/replication/zongji/zongji.d.ts +++ b/modules/module-mysql/src/replication/zongji/zongji.d.ts @@ -3,6 +3,7 @@ declare module '@powersync/mysql-zongji' { host: string; user: string; password: string; + dateStrings?: boolean; }; interface DatabaseFilter { From 57580037f5d3a11acba8727296d9d71e88350315 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 21 Oct 2024 20:43:56 +0200 Subject: [PATCH 256/298] Lockfile --- pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f9b62c2c..8b356b59f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: workspace:* version: link:../../libs/lib-services '@powersync/mysql-zongji': - specifier: 0.0.0-dev-20241016125109 - version: 0.0.0-dev-20241016125109 + specifier: 0.0.0-dev-20241021144804 + version: 0.0.0-dev-20241021144804 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core @@ -1178,8 +1178,8 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@powersync/mysql-zongji@0.0.0-dev-20241016125109': - resolution: {integrity: sha512-lK0Ryq5JYNkfDOLjZwAgLmFdx5OTdPikFFOrUJ+pNrH2qyOho/rIP8PxBG2JUqw/NhA9IigtpyTa9bC0WLCbRA==} + '@powersync/mysql-zongji@0.0.0-dev-20241021144804': + resolution: {integrity: sha512-P2A3x8i12ipcFOeAuJGYnDBJkRqL1e2QqDxBLnaxqQzXmaNRhxbAHeG+luMvpNGucUBBVvdmdXqe3jR0vgOgqA==} engines: {node: '>=20.0.0'} '@prisma/instrumentation@5.16.1': @@ -4579,7 +4579,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/mysql-zongji@0.0.0-dev-20241016125109': + '@powersync/mysql-zongji@0.0.0-dev-20241021144804': dependencies: '@vlasky/mysql': 2.18.6 big-integer: 1.6.51 From 2f9e24f56e60cd503681c8d49901c747a722af48 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 22 Oct 2024 11:37:33 +0200 Subject: [PATCH 257/298] Added ConnectionTester interface Replication Modules now implement this interface. Removed singleton module exports --- modules/module-mongodb/src/index.ts | 6 +--- .../module-mongodb/src/module/MongoModule.ts | 13 +++++++ .../src/replication/replication-utils.ts | 12 +++++++ .../src/common/check-source-configuration.ts | 2 +- modules/module-mysql/src/index.ts | 6 +--- .../module-mysql/src/module/MySQLModule.ts | 18 ++++++++++ modules/module-postgres/src/index.ts | 6 +--- .../src/module/PostgresModule.ts | 19 +++++++++- .../src/replication/replication-utils.ts | 2 +- .../src/replication/ReplicationModule.ts | 35 ++++++++++++++----- service/src/entry.ts | 8 ++--- 11 files changed, 96 insertions(+), 31 deletions(-) create mode 100644 modules/module-mongodb/src/replication/replication-utils.ts diff --git a/modules/module-mongodb/src/index.ts b/modules/module-mongodb/src/index.ts index 4cfc25695..6ecba2a8e 100644 --- a/modules/module-mongodb/src/index.ts +++ b/modules/module-mongodb/src/index.ts @@ -1,5 +1 @@ -import { MongoModule } from './module/MongoModule.js'; - -export const module = new MongoModule(); - -export default module; +export * from './module/MongoModule.js'; diff --git a/modules/module-mongodb/src/module/MongoModule.ts b/modules/module-mongodb/src/module/MongoModule.ts index 3f6e27636..bbd9ab869 100644 --- a/modules/module-mongodb/src/module/MongoModule.ts +++ b/modules/module-mongodb/src/module/MongoModule.ts @@ -4,6 +4,8 @@ import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactor import { MongoErrorRateLimiter } from '../replication/MongoErrorRateLimiter.js'; import { ChangeStreamReplicator } from '../replication/ChangeStreamReplicator.js'; import * as types from '../types/types.js'; +import { MongoManager } from '../replication/MongoManager.js'; +import { checkSourceConfiguration } from '../replication/replication-utils.js'; export class MongoModule extends replication.ReplicationModule { constructor() { @@ -49,4 +51,15 @@ export class MongoModule extends replication.ReplicationModule { // TODO: Implement? } + + async testConnection(config: types.MongoConnectionConfig): Promise { + this.decodeConfig(config); + const normalisedConfig = this.resolveConfig(this.decodedConfig!); + const connectionManager = new MongoManager(normalisedConfig); + try { + return checkSourceConfiguration(connectionManager); + } finally { + await connectionManager.end(); + } + } } diff --git a/modules/module-mongodb/src/replication/replication-utils.ts b/modules/module-mongodb/src/replication/replication-utils.ts new file mode 100644 index 000000000..71a521d77 --- /dev/null +++ b/modules/module-mongodb/src/replication/replication-utils.ts @@ -0,0 +1,12 @@ +import * as mongo from 'mongodb'; +import { MongoManager } from './MongoManager.js'; + +export async function checkSourceConfiguration(connectionManager: MongoManager): Promise { + const db = connectionManager.db; + const hello = await db.command({ hello: 1 }); + if (hello.msg == 'isdbgrid') { + throw new Error('Sharded MongoDB Clusters are not supported yet (including MongoDB Serverless instances).'); + } else if (hello.setName == null) { + throw new Error('Standalone MongoDB instances are not supported - use a replicaset.'); + } +} diff --git a/modules/module-mysql/src/common/check-source-configuration.ts b/modules/module-mysql/src/common/check-source-configuration.ts index a48c4184b..6572a1066 100644 --- a/modules/module-mysql/src/common/check-source-configuration.ts +++ b/modules/module-mysql/src/common/check-source-configuration.ts @@ -1,7 +1,7 @@ import mysqlPromise from 'mysql2/promise'; import * as mysql_utils from '../utils/mysql_utils.js'; -export async function checkSourceConfiguration(connection: mysqlPromise.Connection) { +export async function checkSourceConfiguration(connection: mysqlPromise.Connection): Promise { const errors: string[] = []; const [[result]] = await mysql_utils.retriedQuery({ connection, diff --git a/modules/module-mysql/src/index.ts b/modules/module-mysql/src/index.ts index a6426f95a..3abe77fc5 100644 --- a/modules/module-mysql/src/index.ts +++ b/modules/module-mysql/src/index.ts @@ -1,5 +1 @@ -import { MySQLModule } from './module/MySQLModule.js'; - -export const module = new MySQLModule(); - -export default module; +export * from './module/MySQLModule.js'; diff --git a/modules/module-mysql/src/module/MySQLModule.ts b/modules/module-mysql/src/module/MySQLModule.ts index 8283e6c5c..a4ab36bae 100644 --- a/modules/module-mysql/src/module/MySQLModule.ts +++ b/modules/module-mysql/src/module/MySQLModule.ts @@ -5,6 +5,9 @@ import { BinLogReplicator } from '../replication/BinLogReplicator.js'; import { MySQLErrorRateLimiter } from '../replication/MySQLErrorRateLimiter.js'; import * as types from '../types/types.js'; import { MySQLConnectionManagerFactory } from '../replication/MySQLConnectionManagerFactory.js'; +import { MySQLConnectionConfig } from '../types/types.js'; +import { checkSourceConfiguration } from '../common/check-source-configuration.js'; +import { MySQLConnectionManager } from '../replication/MySQLConnectionManager.js'; export class MySQLModule extends replication.ReplicationModule { constructor() { @@ -50,4 +53,19 @@ export class MySQLModule extends replication.ReplicationModule { // No specific teardown required for MySQL } + + async testConnection(config: MySQLConnectionConfig): Promise { + this.decodeConfig(config); + const normalisedConfig = this.resolveConfig(this.decodedConfig!); + const connectionManager = new MySQLConnectionManager(normalisedConfig, {}); + const connection = await connectionManager.getConnection(); + try { + const errors = await checkSourceConfiguration(connection); + if (errors.length > 0) { + throw new Error(errors.join('\n')); + } + } finally { + await connectionManager.end(); + } + } } diff --git a/modules/module-postgres/src/index.ts b/modules/module-postgres/src/index.ts index 04e83e4e6..3b0d87195 100644 --- a/modules/module-postgres/src/index.ts +++ b/modules/module-postgres/src/index.ts @@ -1,5 +1 @@ -import { PostgresModule } from './module/PostgresModule.js'; - -export const module = new PostgresModule(); - -export default module; +export * from './module/PostgresModule.js'; diff --git a/modules/module-postgres/src/module/PostgresModule.ts b/modules/module-postgres/src/module/PostgresModule.ts index 2b59e730d..5b61275e2 100644 --- a/modules/module-postgres/src/module/PostgresModule.ts +++ b/modules/module-postgres/src/module/PostgresModule.ts @@ -5,9 +5,11 @@ import { SupabaseKeyCollector } from '../auth/SupabaseKeyCollector.js'; import { ConnectionManagerFactory } from '../replication/ConnectionManagerFactory.js'; import { PgManager } from '../replication/PgManager.js'; import { PostgresErrorRateLimiter } from '../replication/PostgresErrorRateLimiter.js'; -import { cleanUpReplicationSlot } from '../replication/replication-utils.js'; +import { checkSourceConfiguration, cleanUpReplicationSlot } from '../replication/replication-utils.js'; import { WalStreamReplicator } from '../replication/WalStreamReplicator.js'; import * as types from '../types/types.js'; +import { PostgresConnectionConfig } from '../types/types.js'; +import { PUBLICATION_NAME } from '../replication/WalStream.js'; export class PostgresModule extends replication.ReplicationModule { constructor() { @@ -112,4 +114,19 @@ export class PostgresModule extends replication.ReplicationModule { + this.decodeConfig(config); + const normalisedConfig = this.resolveConfig(this.decodedConfig!); + const connectionManager = new PgManager(normalisedConfig, { + idleTimeout: 30_000, + maxSize: 1 + }); + const connection = await connectionManager.snapshotConnection(); + try { + return checkSourceConfiguration(connection, PUBLICATION_NAME); + } finally { + await connectionManager.end(); + } + } } diff --git a/modules/module-postgres/src/replication/replication-utils.ts b/modules/module-postgres/src/replication/replication-utils.ts index b2f0db008..c6b1e3fe1 100644 --- a/modules/module-postgres/src/replication/replication-utils.ts +++ b/modules/module-postgres/src/replication/replication-utils.ts @@ -93,7 +93,7 @@ WHERE oid = $1::oid LIMIT 1`, } } -export async function checkSourceConfiguration(db: pgwire.PgClient, publicationName: string) { +export async function checkSourceConfiguration(db: pgwire.PgClient, publicationName: string): Promise { // Check basic config await pgwire_utils.retriedQuery( db, diff --git a/packages/service-core/src/replication/ReplicationModule.ts b/packages/service-core/src/replication/ReplicationModule.ts index dfba89a71..5b5bca8de 100644 --- a/packages/service-core/src/replication/ReplicationModule.ts +++ b/packages/service-core/src/replication/ReplicationModule.ts @@ -7,6 +7,18 @@ import * as modules from '../modules/modules-index.js'; import * as system from '../system/system-index.js'; import { schema } from '@powersync/lib-services-framework'; import { AbstractReplicator } from './AbstractReplicator.js'; +import { TearDownOptions } from '../modules/modules-index.js'; + +/** + * Provides a common interface for testing the connection to a DataSource. + */ +export interface ConnectionTester { + /** + * Confirm if a connection can be established to the datasource for the provided datasource configuration + * @param config + */ + testConnection(config: TConfig): Promise; +} export interface ReplicationModuleOptions extends modules.AbstractModuleOptions { type: string; @@ -17,7 +29,10 @@ export interface ReplicationModuleOptions extends modules.AbstractModuleOptions * A replication module describes all the functionality that PowerSync requires to * replicate data from a DataSource. Whenever a new data source is added to powersync this class should be extended. */ -export abstract class ReplicationModule extends modules.AbstractModule { +export abstract class ReplicationModule + extends modules.AbstractModule + implements ConnectionTester +{ protected type: string; protected configSchema: t.AnyCodec; protected decodedConfig: TConfig | undefined; @@ -43,6 +58,8 @@ export abstract class ReplicationModule extend */ protected abstract createReplicator(context: system.ServiceContext): AbstractReplicator; + public abstract testConnection(config: TConfig): Promise; + /** * Register this module's Replicators and RouteAPI adapters if the required configuration is present. */ @@ -54,11 +71,7 @@ export abstract class ReplicationModule extend const matchingConfig = context.configuration.connections.filter((dataSource) => dataSource.type === this.type); if (!matchingConfig.length) { - // This module is needed given the config - return; - } - - if (!matchingConfig.length) { + // No configuration for this module was found return; } @@ -70,9 +83,8 @@ export abstract class ReplicationModule extend try { const baseMatchingConfig = matchingConfig[0] as TConfig; - // If validation fails, log the error and continue, no replication will happen for this data source - this.validateConfig(baseMatchingConfig); - this.decodedConfig = this.configSchema.decode(baseMatchingConfig); + // If decoding fails, log the error and continue, no replication will happen for this data source + this.decodeConfig(baseMatchingConfig); context.replicationEngine?.register(this.createReplicator(context)); context.routerEngine?.registerAPI(this.createRouteAPIAdapter()); @@ -81,6 +93,11 @@ export abstract class ReplicationModule extend } } + protected decodeConfig(config: TConfig): void { + this.validateConfig(config); + this.decodedConfig = this.configSchema.decode(config); + } + private validateConfig(config: TConfig): void { const validator = schema .parseJSONSchema( diff --git a/service/src/entry.ts b/service/src/entry.ts index b984519bc..b4add3850 100644 --- a/service/src/entry.ts +++ b/service/src/entry.ts @@ -1,20 +1,20 @@ import { container, ContainerImplementation } from '@powersync/lib-services-framework'; import * as core from '@powersync/service-core'; -import MySQLModule from '@powersync/service-module-mysql'; -import PostgresModule from '@powersync/service-module-postgres'; -import MongoModule from '@powersync/service-module-mongodb'; import { startServer } from './runners/server.js'; import { startStreamRunner } from './runners/stream-worker.js'; import { startUnifiedRunner } from './runners/unified-runner.js'; import { createSentryReporter } from './util/alerting.js'; +import { PostgresModule } from '@powersync/service-module-postgres'; +import { MySQLModule } from '@powersync/service-module-mysql'; +import { MongoModule } from '@powersync/service-module-mongodb'; // Initialize framework components container.registerDefaults(); container.register(ContainerImplementation.REPORTER, createSentryReporter()); const moduleManager = new core.modules.ModuleManager(); -moduleManager.register([PostgresModule, MySQLModule, MongoModule]); +moduleManager.register([new PostgresModule(), new MySQLModule(), new MongoModule()]); // This is a bit of a hack. Commands such as the teardown command or even migrations might // want access to the ModuleManager in order to use modules container.register(core.ModuleManager, moduleManager); From 2ccd6aff3ae8f7cd39166b88d5dce1dc1ff83579 Mon Sep 17 00:00:00 2001 From: stevensJourney <51082125+stevensJourney@users.noreply.github.com> Date: Tue, 22 Oct 2024 11:55:05 +0200 Subject: [PATCH 258/298] [Modules] Move Write Checkpoint APIs (#110) --- .changeset/slow-stingrays-kiss.md | 5 ++ .../src/routes/endpoints/checkpointing.ts | 9 +++- .../service-core/src/storage/BucketStorage.ts | 11 ++-- .../src/storage/MongoBucketStorage.ts | 44 +++------------ .../service-core/src/storage/StorageEngine.ts | 26 +-------- .../src/storage/StorageProvider.ts | 10 +--- ...te-checkpoint.ts => WriteCheckpointAPI.ts} | 44 ++++++++++----- .../src/storage/mongo/MongoBucketBatch.ts | 4 +- .../src/storage/mongo/MongoStorageProvider.ts | 3 +- .../storage/mongo/MongoSyncBucketStorage.ts | 53 +++++++++++++++++-- .../storage/mongo/MongoWriteCheckpointAPI.ts | 24 ++++++--- .../service-core/src/storage/storage-index.ts | 4 +- .../util/config/compound-config-collector.ts | 3 +- .../service-core/src/util/config/types.ts | 1 + packages/sync-rules/package.json | 5 +- packages/types/src/config/PowerSyncConfig.ts | 4 +- service/Dockerfile | 1 + 17 files changed, 138 insertions(+), 113 deletions(-) create mode 100644 .changeset/slow-stingrays-kiss.md rename packages/service-core/src/storage/{write-checkpoint.ts => WriteCheckpointAPI.ts} (55%) diff --git a/.changeset/slow-stingrays-kiss.md b/.changeset/slow-stingrays-kiss.md new file mode 100644 index 000000000..a93126de7 --- /dev/null +++ b/.changeset/slow-stingrays-kiss.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-core': minor +--- + +Moved Write Checkpoint APIs to SyncBucketStorage diff --git a/packages/service-core/src/routes/endpoints/checkpointing.ts b/packages/service-core/src/routes/endpoints/checkpointing.ts index 0cfd2dc12..3ce913ad2 100644 --- a/packages/service-core/src/routes/endpoints/checkpointing.ts +++ b/packages/service-core/src/routes/endpoints/checkpointing.ts @@ -1,6 +1,7 @@ import { logger, router, schema } from '@powersync/lib-services-framework'; import * as t from 'ts-codec'; +import * as framework from '@powersync/lib-services-framework'; import * as util from '../../util/util-index.js'; import { authUser } from '../auth.js'; import { routeDefinition } from '../router.js'; @@ -63,7 +64,13 @@ export const writeCheckpoint2 = routeDefinition({ storageEngine: { activeBucketStorage } } = service_context; - const writeCheckpoint = await activeBucketStorage.createManagedWriteCheckpoint({ + const activeSyncRules = await activeBucketStorage.getActiveSyncRulesContent(); + if (!activeSyncRules) { + throw new framework.errors.ValidationError(`Cannot create Write Checkpoint since no sync rules are active.`); + } + + using syncBucketStorage = activeBucketStorage.getInstance(activeSyncRules); + const writeCheckpoint = await syncBucketStorage.createManagedWriteCheckpoint({ user_id: full_user_id, heads: { '1': currentCheckpoint } }); diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 84165fb7c..446362a5a 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -12,16 +12,15 @@ import * as util from '../util/util-index.js'; import { ReplicationEventPayload } from './ReplicationEventPayload.js'; import { SourceEntityDescriptor } from './SourceEntity.js'; import { SourceTable } from './SourceTable.js'; -import { BatchedCustomWriteCheckpointOptions, ReplicaId, WriteCheckpointAPI } from './storage-index.js'; +import { BatchedCustomWriteCheckpointOptions, ReplicaId } from './storage-index.js'; +import { SyncStorageWriteCheckpointAPI } from './WriteCheckpointAPI.js'; export interface BucketStorageFactoryListener extends DisposableListener { syncStorageCreated: (storage: SyncRulesBucketStorage) => void; replicationEvent: (event: ReplicationEventPayload) => void; } -export interface BucketStorageFactory - extends DisposableObserverClient, - WriteCheckpointAPI { +export interface BucketStorageFactory extends DisposableObserverClient { /** * Update sync rules from configuration, if changed. */ @@ -206,7 +205,9 @@ export interface SyncRulesBucketStorageListener extends DisposableListener { batchStarted: (batch: BucketStorageBatch) => void; } -export interface SyncRulesBucketStorage extends DisposableObserverClient { +export interface SyncRulesBucketStorage + extends DisposableObserverClient, + SyncStorageWriteCheckpointAPI { readonly group_id: number; readonly slot_name: string; diff --git a/packages/service-core/src/storage/MongoBucketStorage.ts b/packages/service-core/src/storage/MongoBucketStorage.ts index 9da62758c..f3175b915 100644 --- a/packages/service-core/src/storage/MongoBucketStorage.ts +++ b/packages/service-core/src/storage/MongoBucketStorage.ts @@ -25,16 +25,7 @@ import { PowerSyncMongo, PowerSyncMongoOptions } from './mongo/db.js'; import { SyncRuleDocument, SyncRuleState } from './mongo/models.js'; import { MongoPersistedSyncRulesContent } from './mongo/MongoPersistedSyncRulesContent.js'; import { MongoSyncBucketStorage } from './mongo/MongoSyncBucketStorage.js'; -import { MongoWriteCheckpointAPI } from './mongo/MongoWriteCheckpointAPI.js'; import { generateSlotName } from './mongo/util.js'; -import { - CustomWriteCheckpointOptions, - DEFAULT_WRITE_CHECKPOINT_MODE, - LastWriteCheckpointFilters, - ManagedWriteCheckpointOptions, - WriteCheckpointAPI, - WriteCheckpointMode -} from './write-checkpoint.js'; export interface MongoBucketStorageOptions extends PowerSyncMongoOptions {} @@ -47,10 +38,6 @@ export class MongoBucketStorage // TODO: This is still Postgres specific and needs to be reworked public readonly slot_name_prefix: string; - readonly write_checkpoint_mode: WriteCheckpointMode; - - protected readonly writeCheckpointAPI: WriteCheckpointAPI; - private readonly storageCache = new LRUCache({ max: 3, fetchMethod: async (id) => { @@ -78,7 +65,6 @@ export class MongoBucketStorage db: PowerSyncMongo, options: { slot_name_prefix: string; - write_checkpoint_mode?: WriteCheckpointMode; } ) { super(); @@ -86,11 +72,6 @@ export class MongoBucketStorage this.db = db; this.session = this.client.startSession(); this.slot_name_prefix = options.slot_name_prefix; - this.write_checkpoint_mode = options.write_checkpoint_mode ?? DEFAULT_WRITE_CHECKPOINT_MODE; - this.writeCheckpointAPI = new MongoWriteCheckpointAPI({ - db, - mode: this.write_checkpoint_mode - }); } getInstance(options: PersistedSyncRulesContent): MongoSyncBucketStorage { @@ -299,22 +280,6 @@ export class MongoBucketStorage }); } - async batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise { - return this.writeCheckpointAPI.batchCreateCustomWriteCheckpoints(checkpoints); - } - - async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise { - return this.writeCheckpointAPI.createCustomWriteCheckpoint(options); - } - - async createManagedWriteCheckpoint(options: ManagedWriteCheckpointOptions): Promise { - return this.writeCheckpointAPI.createManagedWriteCheckpoint(options); - } - - async lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise { - return this.writeCheckpointAPI.lastWriteCheckpoint(filters); - } - async getActiveCheckpoint(): Promise { const doc = await this.db.sync_rules.findOne( { @@ -426,7 +391,7 @@ export class MongoBucketStorage } return (await this.storageCache.fetch(doc._id)) ?? null; } - }; + } satisfies ActiveCheckpoint; } /** @@ -516,6 +481,7 @@ export class MongoBucketStorage if (doc == null) { continue; } + const op = this.makeActiveCheckpoint(doc); // Check for LSN / checkpoint changes - ignore other metadata changes if (lastOp == null || op.lsn != lastOp.lsn || op.checkpoint != lastOp.checkpoint) { @@ -546,12 +512,14 @@ export class MongoBucketStorage // 1. checkpoint (op_id) changes. // 2. write checkpoint changes for the specific user const bucketStorage = await cp.getBucketStorage(); + if (!bucketStorage) { + continue; + } const lsnFilters: Record = lsn ? { 1: lsn } : {}; - const currentWriteCheckpoint = await this.lastWriteCheckpoint({ + const currentWriteCheckpoint = await bucketStorage.lastWriteCheckpoint({ user_id, - sync_rules_id: bucketStorage?.group_id, heads: { ...lsnFilters } diff --git a/packages/service-core/src/storage/StorageEngine.ts b/packages/service-core/src/storage/StorageEngine.ts index bdafeb240..a6639211a 100644 --- a/packages/service-core/src/storage/StorageEngine.ts +++ b/packages/service-core/src/storage/StorageEngine.ts @@ -1,17 +1,12 @@ import { DisposableListener, DisposableObserver, logger } from '@powersync/lib-services-framework'; import { ResolvedPowerSyncConfig } from '../util/util-index.js'; import { BucketStorageFactory } from './BucketStorage.js'; -import { ActiveStorage, BucketStorageProvider, StorageSettings } from './StorageProvider.js'; -import { DEFAULT_WRITE_CHECKPOINT_MODE } from './write-checkpoint.js'; +import { ActiveStorage, BucketStorageProvider } from './StorageProvider.js'; export type StorageEngineOptions = { configuration: ResolvedPowerSyncConfig; }; -export const DEFAULT_STORAGE_SETTINGS: StorageSettings = { - writeCheckpointMode: DEFAULT_WRITE_CHECKPOINT_MODE -}; - export interface StorageEngineListener extends DisposableListener { storageActivated: (storage: BucketStorageFactory) => void; } @@ -20,11 +15,9 @@ export class StorageEngine extends DisposableObserver { // TODO: This will need to revisited when we actually support multiple storage providers. private storageProviders: Map = new Map(); private currentActiveStorage: ActiveStorage | null = null; - private _activeSettings: StorageSettings; constructor(private options: StorageEngineOptions) { super(); - this._activeSettings = DEFAULT_STORAGE_SETTINGS; } get activeBucketStorage(): BucketStorageFactory { @@ -39,20 +32,6 @@ export class StorageEngine extends DisposableObserver { return this.currentActiveStorage; } - get activeSettings(): StorageSettings { - return { ...this._activeSettings }; - } - - updateSettings(settings: Partial) { - if (this.currentActiveStorage) { - throw new Error(`Storage is already active, settings cannot be modified.`); - } - this._activeSettings = { - ...this._activeSettings, - ...settings - }; - } - /** * Register a provider which generates a {@link BucketStorageFactory} * given the matching config specified in the loaded {@link ResolvedPowerSyncConfig} @@ -65,8 +44,7 @@ export class StorageEngine extends DisposableObserver { logger.info('Starting Storage Engine...'); const { configuration } = this.options; this.currentActiveStorage = await this.storageProviders.get(configuration.storage.type)!.getStorage({ - resolvedConfig: configuration, - ...this.activeSettings + resolvedConfig: configuration }); this.iterateListeners((cb) => cb.storageActivated?.(this.activeBucketStorage)); logger.info(`Successfully activated storage: ${configuration.storage.type}.`); diff --git a/packages/service-core/src/storage/StorageProvider.ts b/packages/service-core/src/storage/StorageProvider.ts index 7c730fb4b..385a042fd 100644 --- a/packages/service-core/src/storage/StorageProvider.ts +++ b/packages/service-core/src/storage/StorageProvider.ts @@ -1,6 +1,5 @@ import * as util from '../util/util-index.js'; import { BucketStorageFactory } from './BucketStorage.js'; -import { WriteCheckpointMode } from './write-checkpoint.js'; export interface ActiveStorage { storage: BucketStorageFactory; @@ -12,14 +11,7 @@ export interface ActiveStorage { tearDown(): Promise; } -/** - * Settings which can be modified by various modules in their initialization. - */ -export interface StorageSettings { - writeCheckpointMode: WriteCheckpointMode; -} - -export interface GetStorageOptions extends StorageSettings { +export interface GetStorageOptions { // TODO: This should just be the storage config. Update once the slot name prefix coupling has been removed from the storage resolvedConfig: util.ResolvedPowerSyncConfig; } diff --git a/packages/service-core/src/storage/write-checkpoint.ts b/packages/service-core/src/storage/WriteCheckpointAPI.ts similarity index 55% rename from packages/service-core/src/storage/write-checkpoint.ts rename to packages/service-core/src/storage/WriteCheckpointAPI.ts index 0b61fe0c1..d38ac3979 100644 --- a/packages/service-core/src/storage/write-checkpoint.ts +++ b/packages/service-core/src/storage/WriteCheckpointAPI.ts @@ -3,7 +3,7 @@ export enum WriteCheckpointMode { * Raw mappings of `user_id` to `write_checkpoint`s should * be supplied for each set of sync rules. */ - CUSTOM = 'manual', + CUSTOM = 'custom', /** * Write checkpoints are stored as a mapping of `user_id` plus * replication HEAD (lsn in Postgres) to an automatically generated @@ -26,19 +26,19 @@ export interface CustomWriteCheckpointFilters extends BaseWriteCheckpointIdentif sync_rules_id: number; } -export interface CustomWriteCheckpointOptions extends CustomWriteCheckpointFilters { +export interface BatchedCustomWriteCheckpointOptions extends BaseWriteCheckpointIdentifier { /** * A supplied incrementing Write Checkpoint number */ checkpoint: bigint; } -/** - * Options for creating a custom Write Checkpoint in a batch. - * A {@link BucketStorageBatch} is already associated with a Sync Rules instance. - * The `sync_rules_id` is not required here. - */ -export type BatchedCustomWriteCheckpointOptions = Omit; +export interface CustomWriteCheckpointOptions extends BatchedCustomWriteCheckpointOptions { + /** + * Sync rules which were active when this checkpoint was created. + */ + sync_rules_id: number; +} /** * Managed Write Checkpoints are a mapping of User ID to replication HEAD @@ -52,15 +52,33 @@ export interface ManagedWriteCheckpointFilters extends BaseWriteCheckpointIdenti export type ManagedWriteCheckpointOptions = ManagedWriteCheckpointFilters; +export type SyncStorageLastWriteCheckpointFilters = BaseWriteCheckpointIdentifier | ManagedWriteCheckpointFilters; export type LastWriteCheckpointFilters = CustomWriteCheckpointFilters | ManagedWriteCheckpointFilters; -export interface WriteCheckpointAPI { - batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise; - - createCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): Promise; - +export interface BaseWriteCheckpointAPI { + readonly writeCheckpointMode: WriteCheckpointMode; + setWriteCheckpointMode(mode: WriteCheckpointMode): void; createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise; +} + +/** + * Write Checkpoint API to be used in conjunction with a {@link SyncRulesBucketStorage}. + * This storage corresponds with a set of sync rules. These APIs don't require specifying a + * sync rules id. + */ +export interface SyncStorageWriteCheckpointAPI extends BaseWriteCheckpointAPI { + batchCreateCustomWriteCheckpoints(checkpoints: BatchedCustomWriteCheckpointOptions[]): Promise; + createCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): Promise; + lastWriteCheckpoint(filters: SyncStorageLastWriteCheckpointFilters): Promise; +} +/** + * Write Checkpoint API which is interfaced directly with the storage layer. This requires + * sync rules identifiers for custom write checkpoints. + */ +export interface WriteCheckpointAPI extends BaseWriteCheckpointAPI { + batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise; + createCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): Promise; lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise; } diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index f0f95cd4e..c24a04999 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -12,7 +12,7 @@ import { SaveOptions } from '../BucketStorage.js'; import { SourceTable } from '../SourceTable.js'; -import { CustomWriteCheckpointOptions } from '../write-checkpoint.js'; +import { BatchedCustomWriteCheckpointOptions, CustomWriteCheckpointOptions } from '../WriteCheckpointAPI.js'; import { PowerSyncMongo } from './db.js'; import { CurrentBucket, CurrentDataDocument, SourceKey, SyncRuleDocument } from './models.js'; import { MongoIdSequence } from './MongoIdSequence.js'; @@ -83,7 +83,7 @@ export class MongoBucketBatch extends DisposableObserver client.close(), tearDown: () => { diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index e4b57788e..9243c0107 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -2,7 +2,8 @@ import { SqliteJsonRow, SqliteJsonValue, SqlSyncRules } from '@powersync/service import * as bson from 'bson'; import * as mongo from 'mongodb'; -import { DisposableObserver } from '@powersync/lib-services-framework'; +import { DisposableObserver, logger } from '@powersync/lib-services-framework'; +import * as timers from 'timers/promises'; import * as db from '../../db/db-index.js'; import * as util from '../../util/util-index.js'; import { @@ -26,13 +27,19 @@ import { import { ChecksumCache, FetchPartialBucketChecksum, PartialChecksum, PartialChecksumMap } from '../ChecksumCache.js'; import { MongoBucketStorage } from '../MongoBucketStorage.js'; import { SourceTable } from '../SourceTable.js'; +import { + BatchedCustomWriteCheckpointOptions, + ManagedWriteCheckpointOptions, + SyncStorageLastWriteCheckpointFilters, + WriteCheckpointAPI, + WriteCheckpointMode +} from '../WriteCheckpointAPI.js'; import { PowerSyncMongo } from './db.js'; import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './models.js'; import { MongoBucketBatch } from './MongoBucketBatch.js'; import { MongoCompactor } from './MongoCompactor.js'; +import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js'; import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js'; -import { logger } from '@powersync/lib-services-framework'; -import * as timers from 'timers/promises'; export class MongoSyncBucketStorage extends DisposableObserver @@ -46,15 +53,53 @@ export class MongoSyncBucketStorage }); private parsedSyncRulesCache: SqlSyncRules | undefined; + private writeCheckpointAPI: WriteCheckpointAPI; constructor( public readonly factory: MongoBucketStorage, public readonly group_id: number, private readonly sync_rules: PersistedSyncRulesContent, - public readonly slot_name: string + public readonly slot_name: string, + writeCheckpointMode: WriteCheckpointMode = WriteCheckpointMode.MANAGED ) { super(); this.db = factory.db; + this.writeCheckpointAPI = new MongoWriteCheckpointAPI({ + db: this.db, + mode: writeCheckpointMode + }); + } + + get writeCheckpointMode() { + return this.writeCheckpointAPI.writeCheckpointMode; + } + + setWriteCheckpointMode(mode: WriteCheckpointMode): void { + this.writeCheckpointAPI.setWriteCheckpointMode(mode); + } + + batchCreateCustomWriteCheckpoints(checkpoints: BatchedCustomWriteCheckpointOptions[]): Promise { + return this.writeCheckpointAPI.batchCreateCustomWriteCheckpoints( + checkpoints.map((checkpoint) => ({ ...checkpoint, sync_rules_id: this.group_id })) + ); + } + + createCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): Promise { + return this.writeCheckpointAPI.createCustomWriteCheckpoint({ + ...checkpoint, + sync_rules_id: this.group_id + }); + } + + createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise { + return this.writeCheckpointAPI.createManagedWriteCheckpoint(checkpoint); + } + + lastWriteCheckpoint(filters: SyncStorageLastWriteCheckpointFilters): Promise { + return this.writeCheckpointAPI.lastWriteCheckpoint({ + ...filters, + sync_rules_id: this.group_id + }); } getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules { diff --git a/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts b/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts index 230db3153..bbcebb0de 100644 --- a/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts +++ b/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts @@ -7,7 +7,7 @@ import { ManagedWriteCheckpointOptions, WriteCheckpointAPI, WriteCheckpointMode -} from '../write-checkpoint.js'; +} from '../WriteCheckpointAPI.js'; import { PowerSyncMongo } from './db.js'; export type MongoCheckpointAPIOptions = { @@ -17,11 +17,19 @@ export type MongoCheckpointAPIOptions = { export class MongoWriteCheckpointAPI implements WriteCheckpointAPI { readonly db: PowerSyncMongo; - readonly mode: WriteCheckpointMode; + private _mode: WriteCheckpointMode; constructor(options: MongoCheckpointAPIOptions) { this.db = options.db; - this.mode = options.mode; + this._mode = options.mode; + } + + get writeCheckpointMode() { + return this._mode; + } + + setWriteCheckpointMode(mode: WriteCheckpointMode): void { + this._mode = mode; } async batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise { @@ -29,9 +37,9 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI { } async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise { - if (this.mode !== WriteCheckpointMode.CUSTOM) { + if (this.writeCheckpointMode !== WriteCheckpointMode.CUSTOM) { throw new framework.errors.ValidationError( - `Creating a custom Write Checkpoint when the current Write Checkpoint mode is set to "${this.mode}"` + `Creating a custom Write Checkpoint when the current Write Checkpoint mode is set to "${this.writeCheckpointMode}"` ); } @@ -52,9 +60,9 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI { } async createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise { - if (this.mode !== WriteCheckpointMode.MANAGED) { + if (this.writeCheckpointMode !== WriteCheckpointMode.MANAGED) { throw new framework.errors.ValidationError( - `Creating a managed Write Checkpoint when the current Write Checkpoint mode is set to "${this.mode}"` + `Attempting to create a managed Write Checkpoint when the current Write Checkpoint mode is set to "${this.writeCheckpointMode}"` ); } @@ -77,7 +85,7 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI { } async lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise { - switch (this.mode) { + switch (this.writeCheckpointMode) { case WriteCheckpointMode.CUSTOM: if (false == 'sync_rules_id' in filters) { throw new framework.errors.ValidationError(`Sync rules ID is required for custom Write Checkpoint filtering`); diff --git a/packages/service-core/src/storage/storage-index.ts b/packages/service-core/src/storage/storage-index.ts index b58576639..231bb3849 100644 --- a/packages/service-core/src/storage/storage-index.ts +++ b/packages/service-core/src/storage/storage-index.ts @@ -5,6 +5,7 @@ export * from './SourceEntity.js'; export * from './SourceTable.js'; export * from './StorageEngine.js'; +export * from './mongo/config.js'; export * from './mongo/db.js'; export * from './mongo/models.js'; export * from './mongo/MongoBucketBatch.js'; @@ -17,5 +18,4 @@ export * from './mongo/MongoSyncRulesLock.js'; export * from './mongo/OperationBatch.js'; export * from './mongo/PersistedBatch.js'; export * from './mongo/util.js'; -export * from './mongo/config.js'; -export * from './write-checkpoint.js'; +export * from './WriteCheckpointAPI.js'; diff --git a/packages/service-core/src/util/config/compound-config-collector.ts b/packages/service-core/src/util/config/compound-config-collector.ts index 78e8500cb..7c2b70c87 100644 --- a/packages/service-core/src/util/config/compound-config-collector.ts +++ b/packages/service-core/src/util/config/compound-config-collector.ts @@ -122,7 +122,8 @@ export class CompoundConfigCollector { }, // TODO maybe move this out of the connection or something // slot_name_prefix: connections[0]?.slot_name_prefix ?? 'powersync_' - slot_name_prefix: 'powersync_' + slot_name_prefix: 'powersync_', + parameters: baseConfig.parameters ?? {} }; return config; diff --git a/packages/service-core/src/util/config/types.ts b/packages/service-core/src/util/config/types.ts index e5f461e49..99829526d 100644 --- a/packages/service-core/src/util/config/types.ts +++ b/packages/service-core/src/util/config/types.ts @@ -64,4 +64,5 @@ export type ResolvedPowerSyncConfig = { /** Prefix for postgres replication slot names. May eventually be connection-specific. */ slot_name_prefix: string; + parameters: Record; }; diff --git a/packages/sync-rules/package.json b/packages/sync-rules/package.json index bb942b1e8..727188bb6 100644 --- a/packages/sync-rules/package.json +++ b/packages/sync-rules/package.json @@ -15,10 +15,9 @@ "type": "module", "scripts": { "clean": "rm -r ./dist && tsc -b --clean", - "build": "tsc -b", + "build:tsc": "tsc -b", + "build": "pnpm build:tsc && node scripts/compile-schema.js", "build:tests": "tsc -b test/tsconfig.json", - "compile:schema": "pnpm build && node scripts/compile-schema.js", - "postversion": "pnpm compile:schema", "test": "vitest" }, "dependencies": { diff --git a/packages/types/src/config/PowerSyncConfig.ts b/packages/types/src/config/PowerSyncConfig.ts index e9dff54f4..dbd33e1ae 100644 --- a/packages/types/src/config/PowerSyncConfig.ts +++ b/packages/types/src/config/PowerSyncConfig.ts @@ -135,7 +135,9 @@ export const powerSyncConfig = t.object({ disable_telemetry_sharing: t.boolean, internal_service_endpoint: t.string.optional() }) - .optional() + .optional(), + + parameters: t.record(t.number.or(t.string).or(t.boolean).or(t.Null)).optional() }); export type PowerSyncConfig = t.Decoded; diff --git a/service/Dockerfile b/service/Dockerfile index 715931b11..d6641555e 100644 --- a/service/Dockerfile +++ b/service/Dockerfile @@ -29,6 +29,7 @@ COPY packages/jpgwire/src packages/jpgwire/src/ COPY packages/jpgwire/ca packages/jpgwire/ca/ COPY packages/jsonbig/src packages/jsonbig/src/ COPY packages/sync-rules/src packages/sync-rules/src/ +COPY packages/sync-rules/scripts packages/sync-rules/scripts/ COPY packages/rsocket-router/src packages/rsocket-router/src/ COPY packages/types/src packages/types/src/ From cabcb93f45b83f7305fbeae12c426d7f110b6a8a Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 23 Oct 2024 13:47:49 +0200 Subject: [PATCH 259/298] Made it possible to specify timezone on zongji listener configuration to stop unwanted timezone skew Added serverId configuration Added check for binlog existence before starting replication. --- modules/module-mysql/package.json | 3 +- .../src/common/mysql-to-sqlite.ts | 15 +- .../src/common/read-executed-gtid.ts | 12 + .../src/replication/BinLogStream.ts | 35 ++- .../src/replication/zongji/zongji.d.ts | 6 + modules/module-mysql/src/types/types.ts | 6 +- modules/module-mysql/src/utils/mysql_utils.ts | 10 +- .../test/src/binlog_stream.test.ts | 287 ------------------ .../test/src/binlog_stream_utils.ts | 152 ---------- packages/jpgwire/package.json | 2 +- pnpm-lock.yaml | 23 +- 11 files changed, 89 insertions(+), 462 deletions(-) delete mode 100644 modules/module-mysql/test/src/binlog_stream.test.ts delete mode 100644 modules/module-mysql/test/src/binlog_stream_utils.ts diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 824a40636..8e1651e69 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -32,7 +32,8 @@ "@powersync/service-core": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@powersync/mysql-zongji": "0.0.0-dev-20241021144804", + "@powersync/mysql-zongji": "0.0.0-dev-20241022103518", + "date-fns": "^4.1.0", "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", diff --git a/modules/module-mysql/src/common/mysql-to-sqlite.ts b/modules/module-mysql/src/common/mysql-to-sqlite.ts index 120f7ab5f..6cbb95541 100644 --- a/modules/module-mysql/src/common/mysql-to-sqlite.ts +++ b/modules/module-mysql/src/common/mysql-to-sqlite.ts @@ -1,10 +1,21 @@ +import { logger } from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import { ExpressionType } from '@powersync/service-sync-rules'; +import { ColumnDescriptor } from '@powersync/service-core'; +import { formatISO } from 'date-fns'; +import mysql from 'mysql2'; -export function toSQLiteRow(row: Record): sync_rules.SqliteRow { +export function toSQLiteRow(row: Record, columns?: Map): sync_rules.SqliteRow { for (let key in row) { if (row[key] instanceof Date) { - row[key] = row[key].toISOString(); + logger.info(`Date before conversion: ${key}=${row[key].toISOString()}`); + const column = columns?.get(key); + if (column?.typeId == mysql.Types.DATE) { + row[key] = formatISO(row[key], { representation: 'date' }); + } else { + row[key] = row[key].toISOString(); + } + logger.info(`Converted date to string: ${key}=${row[key]}`); } } return sync_rules.toSyncRulesRow(row); diff --git a/modules/module-mysql/src/common/read-executed-gtid.ts b/modules/module-mysql/src/common/read-executed-gtid.ts index 51ac7c97f..7f224c5b9 100644 --- a/modules/module-mysql/src/common/read-executed-gtid.ts +++ b/modules/module-mysql/src/common/read-executed-gtid.ts @@ -41,3 +41,15 @@ export async function readExecutedGtid(connection: mysqlPromise.Connection): Pro raw_gtid: binlogStatus.Executed_Gtid_Set }); } + +export async function isBinlogStillAvailable( + connection: mysqlPromise.Connection, + binlogFile: string +): Promise { + const [logFiles] = await mysql_utils.retriedQuery({ + connection, + query: `SHOW BINARY LOGS;` + }); + + return logFiles.some((f) => f['Log_name'] == binlogFile); +} diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index de1dccffc..acfee364f 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -2,15 +2,16 @@ import { logger } from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import async from 'async'; -import { framework, getUuidReplicaIdentityBson, Metrics, storage } from '@powersync/service-core'; -import mysql from 'mysql2'; +import { ColumnDescriptor, framework, getUuidReplicaIdentityBson, Metrics, storage } from '@powersync/service-core'; +import mysql, { FieldPacket } from 'mysql2'; import { BinLogEvent } from '@powersync/mysql-zongji'; import * as common from '../common/common-index.js'; import * as zongji_utils from './zongji/zongji-utils.js'; import { MySQLConnectionManager } from './MySQLConnectionManager.js'; -import { ReplicatedGTID } from '../common/common-index.js'; +import { isBinlogStillAvailable, ReplicatedGTID } from '../common/common-index.js'; import mysqlPromise from 'mysql2/promise'; +import { MySQLTypesMap } from '../utils/mysql_utils.js'; export interface BinLogStreamOptions { connections: MySQLConnectionManager; @@ -211,10 +212,23 @@ AND table_type = 'BASE TABLE';`, */ protected async checkInitialReplicated(): Promise { const status = await this.storage.getStatus(); + const lastKnowGTID = status.checkpoint_lsn ? common.ReplicatedGTID.fromSerialized(status.checkpoint_lsn) : null; if (status.snapshot_done && status.checkpoint_lsn) { - logger.info(`Initial replication already done. MySQL appears healthy`); + logger.info(`Initial replication already done.`); + + if (lastKnowGTID) { + // Check if the binlog is still available. If it isn't we need to snapshot again. + const connection = await this.connections.getConnection(); + try { + return await isBinlogStillAvailable(connection, lastKnowGTID.position.filename); + } finally { + connection.release(); + } + } + return true; } + return false; } @@ -270,17 +284,24 @@ AND table_type = 'BASE TABLE';`, logger.info(`Replicating ${table.qualifiedName}`); // TODO count rows and log progress at certain batch sizes + const columns = new Map(); return new Promise((resolve, reject) => { // MAX_EXECUTION_TIME(0) hint disables execution timeout for this query connection .query(`SELECT /*+ MAX_EXECUTION_TIME(0) */ * FROM ${table.schema}.${table.table}`) - .stream() .on('error', (err) => { reject(err); }) - .on('data', async (row) => { + .on('fields', (fields: FieldPacket[]) => { + // Map the columns and their types + fields.forEach((field) => { + const columnType = MySQLTypesMap[field.type as number]; + columns.set(field.name, { name: field.name, type: columnType, typeId: field.type }); + }); + }) + .on('result', async (row) => { connection.pause(); - const record = common.toSQLiteRow(row); + const record = common.toSQLiteRow(row, columns); await batch.save({ tag: storage.SaveOperationTag.INSERT, diff --git a/modules/module-mysql/src/replication/zongji/zongji.d.ts b/modules/module-mysql/src/replication/zongji/zongji.d.ts index 771a3ff8e..5a7353fe9 100644 --- a/modules/module-mysql/src/replication/zongji/zongji.d.ts +++ b/modules/module-mysql/src/replication/zongji/zongji.d.ts @@ -4,6 +4,7 @@ declare module '@powersync/mysql-zongji' { user: string; password: string; dateStrings?: boolean; + timeZone?: string; }; interface DatabaseFilter { @@ -31,6 +32,11 @@ declare module '@powersync/mysql-zongji' { * BinLog position offset to start reading events from in file specified */ position?: number; + + /** + * Unique server ID for this replication client. + */ + serverId?: number; }; export type ColumnSchema = { diff --git a/modules/module-mysql/src/types/types.ts b/modules/module-mysql/src/types/types.ts index 3b812413f..43dd17696 100644 --- a/modules/module-mysql/src/types/types.ts +++ b/modules/module-mysql/src/types/types.ts @@ -14,6 +14,7 @@ export interface NormalizedMySQLConnectionConfig { username: string; password: string; + server_id: number; cacert?: string; client_certificate?: string; @@ -29,6 +30,7 @@ export const MySQLConnectionConfig = service_types.configFile.DataSourceConfig.a username: t.string.optional(), password: t.string.optional(), database: t.string.optional(), + server_id: t.number.optional(), cacert: t.string.optional(), client_certificate: t.string.optional(), @@ -97,6 +99,8 @@ export function normalizeConnectionConfig(options: MySQLConnectionConfig): Norma database, username, - password + password, + + server_id: options.server_id ?? 1 }; } diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 0b3e64177..b21be9b0f 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -1,8 +1,13 @@ import { logger } from '@powersync/lib-services-framework'; -import mysql from 'mysql2'; +import mysql, { Types } from 'mysql2'; import mysqlPromise from 'mysql2/promise'; import * as types from '../types/types.js'; +export const MySQLTypesMap: { [key: number]: string } = {}; +for (const [name, code] of Object.entries(Types)) { + MySQLTypesMap[code as number] = name; +} + export type RetriedQueryOptions = { connection: mysqlPromise.Connection; query: string; @@ -42,6 +47,9 @@ export function createPool(config: types.NormalizedMySQLConnectionConfig, option password: config.password, database: config.database, ssl: hasSSLOptions ? sslOptions : undefined, + supportBigNumbers: true, + // dateStrings: true, + timezone: 'Z', // Ensure no auto timezone manipulation of the dates occur ...(options || {}) }); } diff --git a/modules/module-mysql/test/src/binlog_stream.test.ts b/modules/module-mysql/test/src/binlog_stream.test.ts deleted file mode 100644 index 60dd743a2..000000000 --- a/modules/module-mysql/test/src/binlog_stream.test.ts +++ /dev/null @@ -1,287 +0,0 @@ -import { putOp, removeOp } from '@core-tests/stream_utils.js'; -import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; -import { BucketStorageFactory, Metrics } from '@powersync/service-core'; -import * as crypto from 'crypto'; -import { describe, expect, test } from 'vitest'; -import { binlogStreamTest } from './binlog_stream_utils.js'; -import { logger } from '@powersync/lib-services-framework'; - -type StorageFactory = () => Promise; - -const BASIC_SYNC_RULES = ` -bucket_definitions: - global: - data: - - SELECT id, description FROM "test_data" -`; - -describe( - ' Binlog stream - mongodb', - function () { - defineBinlogStreamTests(MONGO_STORAGE_FACTORY); - }, - { timeout: 20_000 } -); - -function defineBinlogStreamTests(factory: StorageFactory) { - test( - 'Replicate basic values', - binlogStreamTest(factory, async (context) => { - const { connectionManager } = context; - await context.updateSyncRules(` - bucket_definitions: - global: - data: - - SELECT id, description, num FROM "test_data"`); - - await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, num BIGINT)` - ); - - await context.replicateSnapshot(); - - const startRowCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const startTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - - context.startStreaming(); - await connectionManager.query(`INSERT INTO test_data(description, num) VALUES('test1', 1152921504606846976)`); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test1' AND num = 1152921504606846976` - ); - const testId = result.test_id; - logger.info('Finished Inserting data with id:' + testId); - - const data = await context.getBucketData('global[]'); - - expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1', num: 1152921504606846976n })]); - const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const endTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - expect(endRowCount - startRowCount).toEqual(1); - expect(endTxCount - startTxCount).toEqual(1); - }) - ); - - test( - 'replicating case sensitive table', - binlogStreamTest(factory, async (context) => { - const { connectionManager } = context; - await context.updateSyncRules(` - bucket_definitions: - global: - data: - - SELECT id, description FROM "test_DATA" - `); - - await connectionManager.query( - `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); - - await context.replicateSnapshot(); - - const startRowCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const startTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - - context.startStreaming(); - - await connectionManager.query(`INSERT INTO test_DATA(description) VALUES('test1')`); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_DATA WHERE description = 'test1'` - ); - const testId = result.test_id; - - const data = await context.getBucketData('global[]'); - - expect(data).toMatchObject([putOp('test_DATA', { id: testId, description: 'test1' })]); - const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const endTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - expect(endRowCount - startRowCount).toEqual(1); - expect(endTxCount - startTxCount).toEqual(1); - }) - ); - - // TODO: Not supported yet - // test( - // 'replicating TRUNCATE', - // binlogStreamTest(factory, async (context) => { - // const { connectionManager } = context; - // const syncRuleContent = ` - // bucket_definitions: - // global: - // data: - // - SELECT id, description FROM "test_data" - // by_test_data: - // parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id - // data: [] - // `; - // await context.updateSyncRules(syncRuleContent); - // await connectionManager.query(`DROP TABLE IF EXISTS test_data`); - // await connectionManager.query( - // `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)` - // ); - // - // await context.replicateSnapshot(); - // context.startStreaming(); - // - // const [{ test_id }] = pgwireRows( - // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) - // ); - // await connectionManager.query(`TRUNCATE test_data`); - // - // const data = await context.getBucketData('global[]'); - // - // expect(data).toMatchObject([ - // putOp('test_data', { id: test_id, description: 'test1' }), - // removeOp('test_data', test_id) - // ]); - // }) - // ); - - test( - 'replicating changing primary key', - binlogStreamTest(factory, async (context) => { - const { connectionManager } = context; - await context.updateSyncRules(BASIC_SYNC_RULES); - - await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); - - await context.replicateSnapshot(); - context.startStreaming(); - - await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); - const [[result1]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test1'` - ); - const testId1 = result1.test_id; - - await connectionManager.query(`UPDATE test_data SET id = UUID(), description = 'test2a' WHERE id = '${testId1}'`); - const [[result2]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test2a'` - ); - const testId2 = result2.test_id; - - // This update may fail replicating with: - // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} - await connectionManager.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${testId2}'`); - - // Re-use old id again - await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId1}', 'test1b')`); - await connectionManager.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${testId1}'`); - - const data = await context.getBucketData('global[]'); - expect(data).toMatchObject([ - // Initial insert - putOp('test_data', { id: testId1, description: 'test1' }), - // Update id, then description - removeOp('test_data', testId1), - putOp('test_data', { id: testId2, description: 'test2a' }), - putOp('test_data', { id: testId2, description: 'test2b' }), - // Re-use old id - putOp('test_data', { id: testId1, description: 'test1b' }), - putOp('test_data', { id: testId1, description: 'test1c' }) - ]); - }) - ); - - test( - 'initial sync', - binlogStreamTest(factory, async (context) => { - const { connectionManager } = context; - await context.updateSyncRules(BASIC_SYNC_RULES); - - await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); - - await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test1'` - ); - const testId = result.test_id; - - await context.replicateSnapshot(); - - const data = await context.getBucketData('global[]'); - expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1' })]); - }) - ); - - // test( - // 'record too large', - // binlogStreamTest(factory, async (context) => { - // await context.updateSyncRules(`bucket_definitions: - // global: - // data: - // - SELECT id, description, other FROM "test_data"`); - // const { connectionManager } = context; - // - // await connectionManager.query(`CREATE TABLE test_data(id text primary key, description text, other text)`); - // - // await context.replicateSnapshot(); - // - // // 4MB - // const largeDescription = crypto.randomBytes(2_000_000).toString('hex'); - // // 18MB - // const tooLargeDescription = crypto.randomBytes(9_000_000).toString('hex'); - // - // await connectionManager.query({ - // statement: `INSERT INTO test_data(id, description, other) VALUES('t1', $1, 'foo')`, - // params: [{ type: 'varchar', value: tooLargeDescription }] - // }); - // await connectionManager.query({ - // statement: `UPDATE test_data SET description = $1 WHERE id = 't1'`, - // params: [{ type: 'varchar', value: largeDescription }] - // }); - // - // context.startStreaming(); - // - // const data = await context.getBucketData('global[]'); - // expect(data.length).toEqual(1); - // const row = JSON.parse(data[0].data as string); - // delete row.description; - // expect(row).toEqual({ id: 't1', other: 'foo' }); - // delete data[0].data; - // expect(data[0]).toMatchObject({ object_id: 't1', object_type: 'test_data', op: 'PUT', op_id: '1' }); - // }) - // ); - - test( - 'table not in sync rules', - binlogStreamTest(factory, async (context) => { - const { connectionManager } = context; - await context.updateSyncRules(BASIC_SYNC_RULES); - - await connectionManager.query( - `CREATE TABLE test_donotsync (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); - - await context.replicateSnapshot(); - - const startRowCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const startTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - - context.startStreaming(); - - await connectionManager.query(`INSERT INTO test_donotsync(description) VALUES('test1')`); - const data = await context.getBucketData('global[]'); - - expect(data).toMatchObject([]); - const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; - const endTxCount = - (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; - - // There was a transaction, but we should not replicate any actual data - expect(endRowCount - startRowCount).toEqual(0); - expect(endTxCount - startTxCount).toEqual(1); - }) - ); -} diff --git a/modules/module-mysql/test/src/binlog_stream_utils.ts b/modules/module-mysql/test/src/binlog_stream_utils.ts deleted file mode 100644 index fca736a45..000000000 --- a/modules/module-mysql/test/src/binlog_stream_utils.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { ActiveCheckpoint, BucketStorageFactory, OpId, SyncRulesBucketStorage } from '@powersync/service-core'; -import { TEST_CONNECTION_OPTIONS, clearAndRecreateTestDb } from './util.js'; -import { fromAsync } from '@core-tests/stream_utils.js'; -import { BinLogStream, BinLogStreamOptions } from '@module/replication/BinLogStream.js'; -import { MySQLConnectionManager } from '@module/replication/MySQLConnectionManager.js'; -import mysqlPromise from 'mysql2/promise'; -import { readExecutedGtid } from '@module/common/read-executed-gtid.js'; -import { logger } from '@powersync/lib-services-framework'; - -/** - * Tests operating on the binlog stream need to configure the stream and manage asynchronous - * replication, which gets a little tricky. - * - * This wraps a test in a function that configures all the context, and tears it down afterward. - */ -export function binlogStreamTest( - factory: () => Promise, - test: (context: BinlogStreamTestContext) => Promise -): () => Promise { - return async () => { - const f = await factory(); - const connectionManager = new MySQLConnectionManager(TEST_CONNECTION_OPTIONS, {}); - - const connection = await connectionManager.getConnection(); - await clearAndRecreateTestDb(connection); - connection.release(); - const context = new BinlogStreamTestContext(f, connectionManager); - try { - await test(context); - } finally { - await context.dispose(); - } - }; -} - -export class BinlogStreamTestContext { - private _binlogStream?: BinLogStream; - private abortController = new AbortController(); - private streamPromise?: Promise; - public storage?: SyncRulesBucketStorage; - private replicationDone = false; - - constructor( - public factory: BucketStorageFactory, - public connectionManager: MySQLConnectionManager - ) {} - - async dispose() { - this.abortController.abort(); - await this.streamPromise; - await this.connectionManager.end(); - } - - get connectionTag() { - return this.connectionManager.connectionTag; - } - - async updateSyncRules(content: string) { - const syncRules = await this.factory.updateSyncRules({ content: content }); - this.storage = this.factory.getInstance(syncRules); - return this.storage!; - } - - get binlogStream() { - if (this.storage == null) { - throw new Error('updateSyncRules() first'); - } - if (this._binlogStream) { - return this._binlogStream; - } - const options: BinLogStreamOptions = { - storage: this.storage, - connections: this.connectionManager, - abortSignal: this.abortController.signal - }; - this._binlogStream = new BinLogStream(options); - return this._binlogStream!; - } - - async replicateSnapshot() { - await this.binlogStream.initReplication(); - this.replicationDone = true; - } - - startStreaming() { - if (!this.replicationDone) { - throw new Error('Call replicateSnapshot() before startStreaming()'); - } - this.streamPromise = this.binlogStream.streamChanges(); - } - - async getCheckpoint(options?: { timeout?: number }) { - const connection = await this.connectionManager.getConnection(); - let checkpoint = await Promise.race([ - getClientCheckpoint(connection, this.factory, { timeout: options?.timeout ?? 60_000 }), - this.streamPromise - ]); - connection.release(); - if (typeof checkpoint == undefined) { - // This indicates an issue with the test setup - streamingPromise completed instead - // of getClientCheckpoint() - throw new Error('Test failure - streamingPromise completed'); - } - return checkpoint as string; - } - - async getBucketsDataBatch(buckets: Record, options?: { timeout?: number }) { - let checkpoint = await this.getCheckpoint(options); - const map = new Map(Object.entries(buckets)); - return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map)); - } - - async getBucketData(bucket: string, start?: string, options?: { timeout?: number }) { - start ??= '0'; - let checkpoint = await this.getCheckpoint(options); - const map = new Map([[bucket, start]]); - const batch = this.storage!.getBucketDataBatch(checkpoint, map); - const batches = await fromAsync(batch); - return batches[0]?.batch.data ?? []; - } -} - -export async function getClientCheckpoint( - connection: mysqlPromise.Connection, - bucketStorage: BucketStorageFactory, - options?: { timeout?: number } -): Promise { - const start = Date.now(); - const gtid = await readExecutedGtid(connection); - // This old API needs a persisted checkpoint id. - // Since we don't use LSNs anymore, the only way to get that is to wait. - - const timeout = options?.timeout ?? 50_000; - let lastCp: ActiveCheckpoint | null = null; - - logger.info('Expected Checkpoint: ' + gtid.comparable); - while (Date.now() - start < timeout) { - const cp = await bucketStorage.getActiveCheckpoint(); - lastCp = cp; - //logger.info('Last Checkpoint: ' + lastCp.lsn); - if (!cp.hasSyncRules()) { - throw new Error('No sync rules available'); - } - if (cp.lsn && cp.lsn >= gtid.comparable) { - return cp.checkpoint; - } - - await new Promise((resolve) => setTimeout(resolve, 30)); - } - - throw new Error(`Timeout while waiting for checkpoint ${gtid.comparable}. Last checkpoint: ${lastCp?.lsn}`); -} diff --git a/packages/jpgwire/package.json b/packages/jpgwire/package.json index 3d36eb3d6..d0b47430e 100644 --- a/packages/jpgwire/package.json +++ b/packages/jpgwire/package.json @@ -21,7 +21,7 @@ "@powersync/service-jsonbig": "workspace:^", "@powersync/service-types": "workspace:^", "@powersync/service-sync-rules": "workspace:^", - "date-fns": "^3.6.0", + "date-fns": "^4.1.0", "pgwire": "github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b356b59f..b0851cdd0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: workspace:* version: link:../../libs/lib-services '@powersync/mysql-zongji': - specifier: 0.0.0-dev-20241021144804 - version: 0.0.0-dev-20241021144804 + specifier: 0.0.0-dev-20241022103518 + version: 0.0.0-dev-20241022103518 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core @@ -157,6 +157,9 @@ importers: async: specifier: ^3.2.4 version: 3.2.5 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 mysql2: specifier: ^3.11.0 version: 3.11.3 @@ -253,8 +256,8 @@ importers: specifier: workspace:^ version: link:../types date-fns: - specifier: ^3.6.0 - version: 3.6.0 + specifier: ^4.1.0 + version: 4.1.0 pgwire: specifier: github:kagis/pgwire#f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 version: https://codeload.github.com/kagis/pgwire/tar.gz/f1cb95f9a0f42a612bb5a6b67bb2eb793fc5fc87 @@ -1178,8 +1181,8 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@powersync/mysql-zongji@0.0.0-dev-20241021144804': - resolution: {integrity: sha512-P2A3x8i12ipcFOeAuJGYnDBJkRqL1e2QqDxBLnaxqQzXmaNRhxbAHeG+luMvpNGucUBBVvdmdXqe3jR0vgOgqA==} + '@powersync/mysql-zongji@0.0.0-dev-20241022103518': + resolution: {integrity: sha512-/udQJ87ArI3mPxDrTJXj1DML4tLyAv4pJ+QFumhIDU/WJVZLNw11JwlKn+EtCOfVzd20B39B2AwKWXi4vTWVtw==} engines: {node: '>=20.0.0'} '@prisma/instrumentation@5.16.1': @@ -1825,8 +1828,8 @@ packages: resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} engines: {node: '>=0.11'} - date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} debug@4.3.5: resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} @@ -4579,7 +4582,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/mysql-zongji@0.0.0-dev-20241021144804': + '@powersync/mysql-zongji@0.0.0-dev-20241022103518': dependencies: '@vlasky/mysql': 2.18.6 big-integer: 1.6.51 @@ -5271,7 +5274,7 @@ snapshots: dependencies: '@babel/runtime': 7.24.8 - date-fns@3.6.0: {} + date-fns@4.1.0: {} debug@4.3.5(supports-color@5.5.0): dependencies: From 5ce7ebfac20b6fa9cdc132186d1bcbd09a7711a0 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 23 Oct 2024 17:18:03 +0200 Subject: [PATCH 260/298] Correctly handle date parsing on binlog events and table snapshotting --- modules/module-mysql/package.json | 3 +- .../src/common/mysql-to-sqlite.ts | 7 +-- .../src/replication/BinLogStream.ts | 53 ++++++++----------- pnpm-lock.yaml | 13 ++--- 4 files changed, 30 insertions(+), 46 deletions(-) diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 8e1651e69..4d16d74a8 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -32,8 +32,7 @@ "@powersync/service-core": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@powersync/mysql-zongji": "0.0.0-dev-20241022103518", - "date-fns": "^4.1.0", + "@powersync/mysql-zongji": "0.0.0-dev-20241023144335", "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", diff --git a/modules/module-mysql/src/common/mysql-to-sqlite.ts b/modules/module-mysql/src/common/mysql-to-sqlite.ts index 6cbb95541..29fbfe0fc 100644 --- a/modules/module-mysql/src/common/mysql-to-sqlite.ts +++ b/modules/module-mysql/src/common/mysql-to-sqlite.ts @@ -1,21 +1,18 @@ -import { logger } from '@powersync/lib-services-framework'; import * as sync_rules from '@powersync/service-sync-rules'; import { ExpressionType } from '@powersync/service-sync-rules'; import { ColumnDescriptor } from '@powersync/service-core'; -import { formatISO } from 'date-fns'; import mysql from 'mysql2'; export function toSQLiteRow(row: Record, columns?: Map): sync_rules.SqliteRow { for (let key in row) { if (row[key] instanceof Date) { - logger.info(`Date before conversion: ${key}=${row[key].toISOString()}`); const column = columns?.get(key); if (column?.typeId == mysql.Types.DATE) { - row[key] = formatISO(row[key], { representation: 'date' }); + // Only parse the date part + row[key] = row[key].toISOString().split('T')[0]; } else { row[key] = row[key].toISOString(); } - logger.info(`Converted date to string: ${key}=${row[key]}`); } } return sync_rules.toSyncRulesRow(row); diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index acfee364f..83b222a84 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -5,7 +5,7 @@ import async from 'async'; import { ColumnDescriptor, framework, getUuidReplicaIdentityBson, Metrics, storage } from '@powersync/service-core'; import mysql, { FieldPacket } from 'mysql2'; -import { BinLogEvent } from '@powersync/mysql-zongji'; +import { BinLogEvent, TableMapEntry } from '@powersync/mysql-zongji'; import * as common from '../common/common-index.js'; import * as zongji_utils from './zongji/zongji-utils.js'; import { MySQLConnectionManager } from './MySQLConnectionManager.js'; @@ -31,6 +31,7 @@ interface WriteChangePayload { database: string; table: string; sourceTable: storage.SourceTable; + columns: Map; } export type Data = Record; @@ -408,14 +409,7 @@ AND table_type = 'BASE TABLE';`, await this.writeChanges(batch, { type: storage.SaveOperationTag.INSERT, data: evt.rows, - database: writeTableInfo.parentSchema, - table: writeTableInfo.tableName, - sourceTable: this.getTable( - getMysqlRelId({ - schema: writeTableInfo.parentSchema, - name: writeTableInfo.tableName - }) - ) + tableEntry: writeTableInfo }); break; case zongji_utils.eventIsUpdateMutation(evt): @@ -424,14 +418,7 @@ AND table_type = 'BASE TABLE';`, type: storage.SaveOperationTag.UPDATE, data: evt.rows.map((row) => row.after), previous_data: evt.rows.map((row) => row.before), - database: updateTableInfo.parentSchema, - table: updateTableInfo.tableName, - sourceTable: this.getTable( - getMysqlRelId({ - schema: updateTableInfo.parentSchema, - name: updateTableInfo.tableName - }) - ) + tableEntry: updateTableInfo }); break; case zongji_utils.eventIsDeleteMutation(evt): @@ -440,15 +427,7 @@ AND table_type = 'BASE TABLE';`, await this.writeChanges(batch, { type: storage.SaveOperationTag.DELETE, data: evt.rows, - database: deleteTableInfo.parentSchema, - table: deleteTableInfo.tableName, - // TODO cleanup - sourceTable: this.getTable( - getMysqlRelId({ - schema: deleteTableInfo.parentSchema, - name: deleteTableInfo.tableName - }) - ) + tableEntry: deleteTableInfo }); break; case zongji_utils.eventIsXid(evt): @@ -524,14 +503,26 @@ AND table_type = 'BASE TABLE';`, type: storage.SaveOperationTag; data: Data[]; previous_data?: Data[]; - database: string; - table: string; - sourceTable: storage.SourceTable; + tableEntry: TableMapEntry; } ): Promise { + const columns = new Map(); + msg.tableEntry.columns.forEach((column) => { + columns.set(column.name, { name: column.name, typeId: column.type }); + }); + for (const [index, row] of msg.data.entries()) { await this.writeChange(batch, { - ...msg, + type: msg.type, + database: msg.tableEntry.parentSchema, + sourceTable: this.getTable( + getMysqlRelId({ + schema: msg.tableEntry.parentSchema, + name: msg.tableEntry.tableName + }) + ), + table: msg.tableEntry.tableName, + columns: columns, data: row, previous_data: msg.previous_data?.[index] }); @@ -546,7 +537,7 @@ AND table_type = 'BASE TABLE';`, switch (payload.type) { case storage.SaveOperationTag.INSERT: Metrics.getInstance().rows_replicated_total.add(1); - const record = common.toSQLiteRow(payload.data); + const record = common.toSQLiteRow(payload.data, payload.columns); return await batch.save({ tag: storage.SaveOperationTag.INSERT, sourceTable: payload.sourceTable, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0851cdd0..c4f1bd0e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,8 +143,8 @@ importers: specifier: workspace:* version: link:../../libs/lib-services '@powersync/mysql-zongji': - specifier: 0.0.0-dev-20241022103518 - version: 0.0.0-dev-20241022103518 + specifier: 0.0.0-dev-20241023144335 + version: 0.0.0-dev-20241023144335 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core @@ -157,9 +157,6 @@ importers: async: specifier: ^3.2.4 version: 3.2.5 - date-fns: - specifier: ^4.1.0 - version: 4.1.0 mysql2: specifier: ^3.11.0 version: 3.11.3 @@ -1181,8 +1178,8 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@powersync/mysql-zongji@0.0.0-dev-20241022103518': - resolution: {integrity: sha512-/udQJ87ArI3mPxDrTJXj1DML4tLyAv4pJ+QFumhIDU/WJVZLNw11JwlKn+EtCOfVzd20B39B2AwKWXi4vTWVtw==} + '@powersync/mysql-zongji@0.0.0-dev-20241023144335': + resolution: {integrity: sha512-77A5ld4Egm0KTHDUzBSP+MHH1+ibp1Es1jlaGZmHWNzmXKNiScd0jxkPDxna9CTfRvOoAu8R0T3MTAuK0aDQpg==} engines: {node: '>=20.0.0'} '@prisma/instrumentation@5.16.1': @@ -4582,7 +4579,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/mysql-zongji@0.0.0-dev-20241022103518': + '@powersync/mysql-zongji@0.0.0-dev-20241023144335': dependencies: '@vlasky/mysql': 2.18.6 big-integer: 1.6.51 From 71f316f39ad11059fe58dbe8fd7b890b65acdc84 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Wed, 23 Oct 2024 21:10:22 +0200 Subject: [PATCH 261/298] Some cleanup --- modules/module-mysql/src/replication/BinLogStream.ts | 3 --- .../module-mysql/src/replication/zongji/zongji.d.ts | 10 +++++----- modules/module-mysql/src/utils/mysql_utils.ts | 2 -- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index 83b222a84..c7ab745fe 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -313,7 +313,6 @@ AND table_type = 'BASE TABLE';`, afterReplicaId: getUuidReplicaIdentityBson(record, table.replicaIdColumns) }); connection.resume(); - // TODO: These metrics can probably be reported in batches Metrics.getInstance().rows_replicated_total.add(1); }) .on('end', async function () { @@ -404,7 +403,6 @@ AND table_type = 'BASE TABLE';`, binLogPositionState.offset = evt.position; break; case zongji_utils.eventIsWriteMutation(evt): - // TODO, can multiple tables be present? const writeTableInfo = evt.tableMap[evt.tableId]; await this.writeChanges(batch, { type: storage.SaveOperationTag.INSERT, @@ -422,7 +420,6 @@ AND table_type = 'BASE TABLE';`, }); break; case zongji_utils.eventIsDeleteMutation(evt): - // TODO, can multiple tables be present? const deleteTableInfo = evt.tableMap[evt.tableId]; await this.writeChanges(batch, { type: storage.SaveOperationTag.DELETE, diff --git a/modules/module-mysql/src/replication/zongji/zongji.d.ts b/modules/module-mysql/src/replication/zongji/zongji.d.ts index 5a7353fe9..9a17f15e9 100644 --- a/modules/module-mysql/src/replication/zongji/zongji.d.ts +++ b/modules/module-mysql/src/replication/zongji/zongji.d.ts @@ -55,10 +55,10 @@ declare module '@powersync/mysql-zongji' { }; export type TableMapEntry = { - columnSchemas: Array; + columnSchemas: ColumnSchema[]; parentSchema: string; tableName: string; - columns: Array; + columns: ColumnDefinition[]; }; export type BaseBinLogEvent = { @@ -96,14 +96,14 @@ declare module '@powersync/mysql-zongji' { tableId: number; numberOfColumns: number; tableMap: Record; - rows: Array>; + rows: Record[]; }; export type BinLogUpdateEvent = Omit & { - rows: Array<{ + rows: { before: Record; after: Record; - }>; + }[]; }; export type BinLogEvent = BinLogRotationEvent | BinLogGTIDLogEvent | BinLogXidEvent | BinLogMutationEvent; diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index b21be9b0f..a71f1c6d0 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -40,7 +40,6 @@ export function createPool(config: types.NormalizedMySQLConnectionConfig, option cert: config.client_certificate }; const hasSSLOptions = Object.values(sslOptions).some((v) => !!v); - // TODO confirm if default options are fine for Powersync use case return mysql.createPool({ host: config.hostname, user: config.username, @@ -48,7 +47,6 @@ export function createPool(config: types.NormalizedMySQLConnectionConfig, option database: config.database, ssl: hasSSLOptions ? sslOptions : undefined, supportBigNumbers: true, - // dateStrings: true, timezone: 'Z', // Ensure no auto timezone manipulation of the dates occur ...(options || {}) }); From 618b10e133b19569f2a68c52bb7f245f7382a33f Mon Sep 17 00:00:00 2001 From: Dylan Vorster Date: Wed, 23 Oct 2024 17:03:01 -0600 Subject: [PATCH 262/298] fix esm stuff --- README.md | 2 +- modules/module-mysql/dev/docker/mysql/docker-compose.yaml | 4 ++-- modules/module-mysql/src/utils/mysql_utils.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b13fc6c77..8b994b142 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

-*[PowerSync](https://www.powersync.com) is a sync engine for building local-first apps with instantly-responsive UI/UX and simplified state transfer. Syncs between SQLite on the client-side and Postgres or MongoDB on the server-side (MySQL coming soon).* +_[PowerSync](https://www.powersync.com) is a sync engine for building local-first apps with instantly-responsive UI/UX and simplified state transfer. Syncs between SQLite on the client-side and Postgres or MongoDB on the server-side (MySQL coming soon)._ # PowerSync Service diff --git a/modules/module-mysql/dev/docker/mysql/docker-compose.yaml b/modules/module-mysql/dev/docker/mysql/docker-compose.yaml index 6016d6a55..50dfd2d2b 100644 --- a/modules/module-mysql/dev/docker/mysql/docker-compose.yaml +++ b/modules/module-mysql/dev/docker/mysql/docker-compose.yaml @@ -7,11 +7,11 @@ services: MYSQL_USER: myuser MYSQL_PASSWORD: mypassword ports: - - "3306:3306" + - '3306:3306' volumes: - ./init-scripts/my.cnf:/etc/mysql/my.cnf - ./init-scripts/mysql.sql:/docker-entrypoint-initdb.d/init_user.sql - mysql_data:/var/lib/mysql volumes: - mysql_data: \ No newline at end of file + mysql_data: diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index a71f1c6d0..99741b92a 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -1,10 +1,10 @@ import { logger } from '@powersync/lib-services-framework'; -import mysql, { Types } from 'mysql2'; +import mysql from 'mysql2'; import mysqlPromise from 'mysql2/promise'; import * as types from '../types/types.js'; export const MySQLTypesMap: { [key: number]: string } = {}; -for (const [name, code] of Object.entries(Types)) { +for (const [name, code] of Object.entries(mysql.Types)) { MySQLTypesMap[code as number] = name; } From 00b079a1ea2d27f46ae56a00e09981981aaefde2 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 28 Oct 2024 11:50:10 +0200 Subject: [PATCH 263/298] Using syncrule id for MySQL serverId --- modules/module-mysql/src/replication/BinLogStream.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index c7ab745fe..98ac8fadd 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -5,7 +5,7 @@ import async from 'async'; import { ColumnDescriptor, framework, getUuidReplicaIdentityBson, Metrics, storage } from '@powersync/service-core'; import mysql, { FieldPacket } from 'mysql2'; -import { BinLogEvent, TableMapEntry } from '@powersync/mysql-zongji'; +import { BinLogEvent, StartOptions, TableMapEntry } from '@powersync/mysql-zongji'; import * as common from '../common/common-index.js'; import * as zongji_utils from './zongji/zongji-utils.js'; import { MySQLConnectionManager } from './MySQLConnectionManager.js'; @@ -466,8 +466,9 @@ AND table_type = 'BASE TABLE';`, excludeEvents: [], includeSchema: { [this.defaultSchema]: includedTables }, filename: binLogPositionState.filename, - position: binLogPositionState.offset - }); + position: binLogPositionState.offset, + serverId: this.storage.group_id + } satisfies StartOptions); // Forever young await new Promise((resolve, reject) => { From 861043136cb0e98fe7e064d7fcf9e3797f6cdd5f Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 28 Oct 2024 12:06:16 +0200 Subject: [PATCH 264/298] Changeset --- .changeset/rotten-pumas-protect.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/rotten-pumas-protect.md diff --git a/.changeset/rotten-pumas-protect.md b/.changeset/rotten-pumas-protect.md new file mode 100644 index 000000000..65fc44e61 --- /dev/null +++ b/.changeset/rotten-pumas-protect.md @@ -0,0 +1,9 @@ +--- +'@powersync/service-core': minor +'@powersync/service-module-mysql': minor +'@powersync/service-sync-rules': minor +--- + +Introduced alpha support for MySQL as a datasource for replication. +Bunch of cleanup + From 8e7d2238bd49316f0e1e39ee0f8af1b4eefd8873 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 28 Oct 2024 13:39:45 +0200 Subject: [PATCH 265/298] Prevent mysql connection manager throwing errors on shutdown. --- .../src/replication/MySQLConnectionManager.ts | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/modules/module-mysql/src/replication/MySQLConnectionManager.ts b/modules/module-mysql/src/replication/MySQLConnectionManager.ts index a2e88a722..b69ddb5ad 100644 --- a/modules/module-mysql/src/replication/MySQLConnectionManager.ts +++ b/modules/module-mysql/src/replication/MySQLConnectionManager.ts @@ -3,6 +3,7 @@ import mysqlPromise from 'mysql2/promise'; import mysql, { RowDataPacket } from 'mysql2'; import * as mysql_utils from '../utils/mysql_utils.js'; import ZongJi from '@powersync/mysql-zongji'; +import { logger } from '@powersync/lib-services-framework'; export class MySQLConnectionManager { /** @@ -16,6 +17,8 @@ export class MySQLConnectionManager { private binlogListeners: ZongJi[] = []; + private isClosed = false; + constructor( public options: NormalizedMySQLConnectionConfig, public poolOptions: mysqlPromise.PoolOptions @@ -87,18 +90,18 @@ export class MySQLConnectionManager { } async end(): Promise { - for (const listener of this.binlogListeners) { - listener.stop(); - } + if (!this.isClosed) { + for (const listener of this.binlogListeners) { + listener.stop(); + } - await new Promise((resolve, reject) => { - this.pool.end((err) => { - if (err) { - reject(err); - } else { - resolve(); - } - }); - }); + try { + await this.promisePool.end(); + this.isClosed = true; + } catch (error) { + // We don't particularly care if any errors are thrown when shutting down the pool + logger.warn('Error shutting down MySQL connection pool', error); + } + } } } From 6d8aec9cf3a751ad3cb2f047e56801222b07968d Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 28 Oct 2024 13:58:14 +0200 Subject: [PATCH 266/298] Improved shutdown logic of binlog stream --- .../module-mysql/src/replication/BinLogStream.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index 98ac8fadd..ad805bf99 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -472,8 +472,20 @@ AND table_type = 'BASE TABLE';`, // Forever young await new Promise((resolve, reject) => { + zongji.on('error', (error) => { + logger.error('Error on Binlog listener:', error); + zongji.stop(); + queue.kill(); + reject(error); + }); + + zongji.on('stopped', () => { + logger.info('Binlog listener stopped. Replication ended.'); + resolve(); + }); + queue.error((error) => { - logger.error('Queue error.', error); + logger.error('Binlog listener queue error:', error); zongji.stop(); queue.kill(); reject(error); From 7c073b1612747c224c95c676e02c01e5597f4d86 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 28 Oct 2024 14:31:42 +0200 Subject: [PATCH 267/298] Renamed Checkpoint to ReplicationCheckpoint --- packages/service-core/src/storage/BucketStorage.ts | 6 +++--- .../src/storage/mongo/MongoSyncBucketStorage.ts | 4 ++-- packages/service-core/src/util/protocol-types.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 9468affa0..5c6b98fe3 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -105,12 +105,12 @@ export interface BucketStorageFactory extends DisposableObserverClient; } -export interface Checkpoint { +export interface ReplicationCheckpoint { readonly checkpoint: util.OpId; readonly lsn: string | null; } -export interface ActiveCheckpoint extends Checkpoint { +export interface ActiveCheckpoint extends ReplicationCheckpoint { hasSyncRules(): boolean; getBucketStorage(): Promise; @@ -222,7 +222,7 @@ export interface SyncRulesBucketStorage callback: (batch: BucketStorageBatch) => Promise ): Promise; - getCheckpoint(): Promise; + getCheckpoint(): Promise; getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules; diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 3fa32cc21..93fb87728 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -9,7 +9,7 @@ import * as util from '../../util/util-index.js'; import { BucketDataBatchOptions, BucketStorageBatch, - Checkpoint, + ReplicationCheckpoint, CompactOptions, DEFAULT_DOCUMENT_BATCH_LIMIT, DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES, @@ -108,7 +108,7 @@ export class MongoSyncBucketStorage return this.parsedSyncRulesCache; } - async getCheckpoint(): Promise { + async getCheckpoint(): Promise { const doc = await this.db.sync_rules.findOne( { _id: this.group_id }, { diff --git a/packages/service-core/src/util/protocol-types.ts b/packages/service-core/src/util/protocol-types.ts index f522d19fb..5c7e47064 100644 --- a/packages/service-core/src/util/protocol-types.ts +++ b/packages/service-core/src/util/protocol-types.ts @@ -88,7 +88,7 @@ export type StreamingSyncLine = */ export type OpId = string; -interface Checkpoint { +export interface Checkpoint { last_op_id: OpId; write_checkpoint?: OpId; buckets: BucketChecksum[]; From e4e87652ca4ca656dec7213260ba89998d8fb8a9 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 31 Oct 2024 09:50:20 +0200 Subject: [PATCH 268/298] Reduce required permissions for replicating from a single database. --- .../src/replication/ChangeStream.ts | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index b952a9d4d..9e8a8acab 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -193,16 +193,21 @@ export class ChangeStream { } } - private getSourceNamespaceFilters() { + private getSourceNamespaceFilters(): { $match: any; multipleDatabases: boolean } { const sourceTables = this.sync_rules.getSourceTables(); let $inFilters: any[] = [{ db: this.defaultDb.databaseName, coll: '_powersync_checkpoints' }]; let $refilters: any[] = []; + let multipleDatabases = false; for (let tablePattern of sourceTables) { if (tablePattern.connectionTag != this.connections.connectionTag) { continue; } + if (tablePattern.schema != this.defaultDb.databaseName) { + multipleDatabases = true; + } + if (tablePattern.isWildcard) { $refilters.push({ db: tablePattern.schema, coll: new RegExp('^' + escapeRegExp(tablePattern.tablePrefix)) }); } else { @@ -213,9 +218,9 @@ export class ChangeStream { } } if ($refilters.length > 0) { - return { $or: [{ ns: { $in: $inFilters } }, ...$refilters] }; + return { $match: { $or: [{ ns: { $in: $inFilters } }, ...$refilters] }, multipleDatabases }; } - return { ns: { $in: $inFilters } }; + return { $match: { ns: { $in: $inFilters } }, multipleDatabases }; } static *getQueryData(results: Iterable): Generator { @@ -399,19 +404,29 @@ export class ChangeStream { // TODO: Use changeStreamSplitLargeEvent + const filters = this.getSourceNamespaceFilters(); + const pipeline: mongo.Document[] = [ { - $match: this.getSourceNamespaceFilters() + $match: filters.$match } ]; - const stream = this.client.watch(pipeline, { + const streamOptions: mongo.ChangeStreamOptions = { startAtOperationTime: startAfter, showExpandedEvents: true, useBigInt64: true, maxAwaitTimeMS: 200, fullDocument: 'updateLookup' - }); + }; + let stream: mongo.ChangeStream; + if (filters.multipleDatabases) { + // Requires readAnyDatabase@admin on Atlas + stream = this.client.watch(pipeline, streamOptions); + } else { + // Same general result, but requires less permissions than the above + stream = this.defaultDb.watch(pipeline, streamOptions); + } if (this.abort_signal.aborted) { stream.close(); From c6307d8c063f7728d727c7fe27b890277b3c1828 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 31 Oct 2024 10:13:56 +0200 Subject: [PATCH 269/298] Add changeset. --- .changeset/fifty-dogs-reply.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fifty-dogs-reply.md diff --git a/.changeset/fifty-dogs-reply.md b/.changeset/fifty-dogs-reply.md new file mode 100644 index 000000000..d19c4b701 --- /dev/null +++ b/.changeset/fifty-dogs-reply.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-module-mongodb': minor +--- + +Reduce permissions required for replicating a single mongodb database From ac03d8f98972f8ec5a68aff4c300705a3db76323 Mon Sep 17 00:00:00 2001 From: stevensJourney <51082125+stevensJourney@users.noreply.github.com> Date: Fri, 1 Nov 2024 11:08:28 +0200 Subject: [PATCH 270/298] fix: Cached Parsed Sync Rules (#114) --- .changeset/heavy-shirts-chew.md | 5 +++ .../storage/mongo/MongoSyncBucketStorage.ts | 16 ++++++-- .../test/src/data_storage.test.ts | 38 ++++++++++++++++++- packages/service-core/test/src/util.ts | 2 +- 4 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 .changeset/heavy-shirts-chew.md diff --git a/.changeset/heavy-shirts-chew.md b/.changeset/heavy-shirts-chew.md new file mode 100644 index 000000000..9bcb5e662 --- /dev/null +++ b/.changeset/heavy-shirts-chew.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-core': patch +--- + +Improved sync rules storage cached parsed sync rules, accommodating different parsing options where necessary. diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 93fb87728..72e007714 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -9,13 +9,13 @@ import * as util from '../../util/util-index.js'; import { BucketDataBatchOptions, BucketStorageBatch, - ReplicationCheckpoint, CompactOptions, DEFAULT_DOCUMENT_BATCH_LIMIT, DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES, FlushedResult, ParseSyncRulesOptions, PersistedSyncRulesContent, + ReplicationCheckpoint, ResolveTableOptions, ResolveTableResult, StartBatchOptions, @@ -53,7 +53,7 @@ export class MongoSyncBucketStorage } }); - private parsedSyncRulesCache: SqlSyncRules | undefined; + private parsedSyncRulesCache: {parsed: SqlSyncRules, options: ParseSyncRulesOptions} | undefined; private writeCheckpointAPI: WriteCheckpointAPI; constructor( @@ -104,8 +104,16 @@ export class MongoSyncBucketStorage } getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules { - this.parsedSyncRulesCache ??= this.sync_rules.parsed(options).sync_rules; - return this.parsedSyncRulesCache; + const {parsed, options: cachedOptions} = this.parsedSyncRulesCache ?? {}; + /** + * Check if the cached sync rules, if present, had the same options. + * Parse sync rules if the options are different or if there is no cached value. + */ + if (!parsed || options.defaultSchema != cachedOptions?.defaultSchema ) { + this.parsedSyncRulesCache = {parsed: this.sync_rules.parsed(options).sync_rules, options}; + } + + return this.parsedSyncRulesCache!.parsed; } async getCheckpoint(): Promise { diff --git a/packages/service-core/test/src/data_storage.test.ts b/packages/service-core/test/src/data_storage.test.ts index 776043b03..df8249b6a 100644 --- a/packages/service-core/test/src/data_storage.test.ts +++ b/packages/service-core/test/src/data_storage.test.ts @@ -1,4 +1,5 @@ import { BucketDataBatchOptions, SaveOperationTag } from '@/storage/BucketStorage.js'; +import { getUuidReplicaIdentityBson } from '@/util/util-index.js'; import { RequestParameters } from '@powersync/service-sync-rules'; import { describe, expect, test } from 'vitest'; import { fromAsync, oneFromAsync } from './stream_utils.js'; @@ -13,7 +14,6 @@ import { StorageFactory, testRules } from './util.js'; -import { getUuidReplicaIdentityBson } from '@/util/util-index.js'; const TEST_TABLE = makeTestTable('test', ['id']); @@ -1460,4 +1460,40 @@ bucket_definitions: replication_size_bytes: 0 }); }); + + test('invalidate cached parsed sync rules', async () => { + const sync_rules_content = testRules( + ` +bucket_definitions: + by_workspace: + parameters: + - SELECT id as workspace_id FROM workspace WHERE + workspace."userId" = token_parameters.user_id + data: [] + ` + ); + + const bucketStorageFactory = await factory(); + const syncBucketStorage = bucketStorageFactory.getInstance(sync_rules_content); + + const parsedSchema1 = syncBucketStorage.getParsedSyncRules({ + defaultSchema: 'public' + }); + + const parsedSchema2 = syncBucketStorage.getParsedSyncRules({ + defaultSchema: 'public' + }); + + // These should be cached, this will be the same instance + expect(parsedSchema2).equals(parsedSchema1); + expect(parsedSchema1.getSourceTables()[0].schema).equals('public'); + + const parsedSchema3 = syncBucketStorage.getParsedSyncRules({ + defaultSchema: 'databasename' + }); + + // The cache should not be used + expect(parsedSchema3).not.equals(parsedSchema2); + expect(parsedSchema3.getSourceTables()[0].schema).equals('databasename'); + }); } diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 98cd613e2..421879d3c 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -62,7 +62,7 @@ export function testRules(content: string): PersistedSyncRulesContent { parsed(options) { return { id: 1, - sync_rules: SqlSyncRules.fromYaml(content, PARSE_OPTIONS), + sync_rules: SqlSyncRules.fromYaml(content, options), slot_name: 'test' }; }, From 5f31083241747f5050e8d353c287cf16f83d92c8 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Tue, 5 Nov 2024 17:16:50 +0200 Subject: [PATCH 271/298] MySQL Type Consolidation (#115) MySQL: - Added generator function for MySQL serverId - Logging cleanup - Using JsonContainer for Json values in MySQL - Unified type mappings for replicated and snapshot mysql data types - Added test running config to github actions - Mapping all integer types to bigint - Handling Set data types as json array - Added test for float mapping --- .changeset/lemon-terms-play.md | 8 + .github/workflows/test.yml | 65 ++++ .../module-mysql/dev/config/sync_rules.yaml | 6 +- modules/module-mysql/package.json | 3 +- .../src/common/mysql-to-sqlite.ts | 155 +++++++- .../src/common/read-executed-gtid.ts | 3 - .../src/replication/BinLogStream.ts | 50 +-- .../src/replication/MySQLConnectionManager.ts | 4 +- modules/module-mysql/src/utils/mysql_utils.ts | 19 +- .../test/src/BinLogStream.test.ts | 332 ++++++++++++++++++ .../test/src/BinlogStreamUtils.ts | 157 +++++++++ .../test/src/MysqlTypeMappings.test.ts | 322 +++++++++++++++++ modules/module-mysql/test/src/env.ts | 2 +- modules/module-mysql/test/src/util.ts | 27 +- modules/module-mysql/test/tsconfig.json | 2 +- pnpm-lock.yaml | 13 +- service/src/runners/unified-runner.ts | 4 +- 17 files changed, 1105 insertions(+), 67 deletions(-) create mode 100644 .changeset/lemon-terms-play.md create mode 100644 modules/module-mysql/test/src/BinLogStream.test.ts create mode 100644 modules/module-mysql/test/src/BinlogStreamUtils.ts create mode 100644 modules/module-mysql/test/src/MysqlTypeMappings.test.ts diff --git a/.changeset/lemon-terms-play.md b/.changeset/lemon-terms-play.md new file mode 100644 index 000000000..218d1ebff --- /dev/null +++ b/.changeset/lemon-terms-play.md @@ -0,0 +1,8 @@ +--- +'@powersync/service-module-mysql': minor +--- + +Generate random serverId based on syncrule id for MySQL replication client +Consolidated type mappings between snapshot and replicated values +Enabled MySQL tests in CI + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3705cd600..0edb07512 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -143,3 +143,68 @@ jobs: - name: Test run: pnpm test --filter='./modules/module-postgres' + + run-mysql-tests: + name: MySQL Test + runs-on: ubuntu-latest + needs: run-core-tests + + strategy: + fail-fast: false + matrix: + mysql-version: [ 8.0, 8.4 ] + + steps: + - uses: actions/checkout@v4 + + - name: Start MySQL + run: | + docker run \ + --name MySQLTestDatabase \ + -e MYSQL_ROOT_PASSWORD=mypassword \ + -e MYSQL_DATABASE=mydatabase \ + -p 3306:3306 \ + -d mysql:${{ matrix.mysql-version }} \ + --log-bin=/var/lib/mysql/mysql-bin.log \ + --gtid_mode=ON \ + --enforce_gtid_consistency=ON + + - name: Start MongoDB + uses: supercharge/mongodb-github-action@1.8.0 + with: + mongodb-version: '6.0' + mongodb-replica-set: test-rs + + - name: Setup NodeJS + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + version: 9 + run_install: false + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install + + - name: Build + shell: bash + run: pnpm build + + - name: Test + run: pnpm test --filter='./modules/module-mysql' diff --git a/modules/module-mysql/dev/config/sync_rules.yaml b/modules/module-mysql/dev/config/sync_rules.yaml index eb74a7198..5c0eb9932 100644 --- a/modules/module-mysql/dev/config/sync_rules.yaml +++ b/modules/module-mysql/dev/config/sync_rules.yaml @@ -3,10 +3,8 @@ # Note that changes to this file are not watched. # The service needs to be restarted for changes to take effect. -# Note that specifying the schema is currently required due to the default -# schema being specified as `public`, but in mysql the schema is the database name bucket_definitions: global: data: - - SELECT * FROM mydatabase.lists - - SELECT * FROM mydatabase.todos + - SELECT * FROM lists + - SELECT * FROM todos diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 4d16d74a8..18611a52c 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -32,7 +32,8 @@ "@powersync/service-core": "workspace:*", "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", - "@powersync/mysql-zongji": "0.0.0-dev-20241023144335", + "@powersync/service-jsonbig": "workspace:*", + "@powersync/mysql-zongji": "0.0.0-dev-20241031142605", "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", diff --git a/modules/module-mysql/src/common/mysql-to-sqlite.ts b/modules/module-mysql/src/common/mysql-to-sqlite.ts index 29fbfe0fc..8cc2487d8 100644 --- a/modules/module-mysql/src/common/mysql-to-sqlite.ts +++ b/modules/module-mysql/src/common/mysql-to-sqlite.ts @@ -2,16 +2,155 @@ import * as sync_rules from '@powersync/service-sync-rules'; import { ExpressionType } from '@powersync/service-sync-rules'; import { ColumnDescriptor } from '@powersync/service-core'; import mysql from 'mysql2'; +import { JSONBig, JsonContainer } from '@powersync/service-jsonbig'; +import { ColumnDefinition, TableMapEntry } from '@powersync/mysql-zongji'; -export function toSQLiteRow(row: Record, columns?: Map): sync_rules.SqliteRow { +export enum ADDITIONAL_MYSQL_TYPES { + DATETIME2 = 18, + TIMESTAMP2 = 17, + BINARY = 100, + VARBINARY = 101, + TEXT = 102 +} + +export const MySQLTypesMap: { [key: number]: string } = {}; +for (const [name, code] of Object.entries(mysql.Types)) { + MySQLTypesMap[code as number] = name; +} +for (const [name, code] of Object.entries(ADDITIONAL_MYSQL_TYPES)) { + MySQLTypesMap[code as number] = name; +} + +export function toColumnDescriptors(columns: mysql.FieldPacket[]): Map; +export function toColumnDescriptors(tableMap: TableMapEntry): Map; + +export function toColumnDescriptors(columns: mysql.FieldPacket[] | TableMapEntry): Map { + const columnMap = new Map(); + if (Array.isArray(columns)) { + for (const column of columns) { + columnMap.set(column.name, toColumnDescriptorFromFieldPacket(column)); + } + } else { + for (const column of columns.columns) { + columnMap.set(column.name, toColumnDescriptorFromDefinition(column)); + } + } + + return columnMap; +} + +export function toColumnDescriptorFromFieldPacket(column: mysql.FieldPacket): ColumnDescriptor { + let typeId = column.type!; + const BINARY_FLAG = 128; + const MYSQL_ENUM_FLAG = 256; + const MYSQL_SET_FLAG = 2048; + + switch (column.type) { + case mysql.Types.STRING: + if (((column.flags as number) & BINARY_FLAG) !== 0) { + typeId = ADDITIONAL_MYSQL_TYPES.BINARY; + } else if (((column.flags as number) & MYSQL_ENUM_FLAG) !== 0) { + typeId = mysql.Types.ENUM; + } else if (((column.flags as number) & MYSQL_SET_FLAG) !== 0) { + typeId = mysql.Types.SET; + } + break; + + case mysql.Types.VAR_STRING: + typeId = ((column.flags as number) & BINARY_FLAG) !== 0 ? ADDITIONAL_MYSQL_TYPES.VARBINARY : column.type; + break; + case mysql.Types.BLOB: + typeId = ((column.flags as number) & BINARY_FLAG) === 0 ? ADDITIONAL_MYSQL_TYPES.TEXT : column.type; + break; + } + + const columnType = MySQLTypesMap[typeId]; + + return { + name: column.name, + type: columnType, + typeId: typeId + }; +} + +export function toColumnDescriptorFromDefinition(column: ColumnDefinition): ColumnDescriptor { + let typeId = column.type; + + switch (column.type) { + case mysql.Types.STRING: + typeId = !column.charset ? ADDITIONAL_MYSQL_TYPES.BINARY : column.type; + break; + case mysql.Types.VAR_STRING: + case mysql.Types.VARCHAR: + typeId = !column.charset ? ADDITIONAL_MYSQL_TYPES.VARBINARY : column.type; + break; + case mysql.Types.BLOB: + typeId = column.charset ? ADDITIONAL_MYSQL_TYPES.TEXT : column.type; + break; + } + + const columnType = MySQLTypesMap[typeId]; + + return { + name: column.name, + type: columnType, + typeId: typeId + }; +} + +export function toSQLiteRow(row: Record, columns: Map): sync_rules.SqliteRow { for (let key in row) { - if (row[key] instanceof Date) { - const column = columns?.get(key); - if (column?.typeId == mysql.Types.DATE) { - // Only parse the date part - row[key] = row[key].toISOString().split('T')[0]; - } else { - row[key] = row[key].toISOString(); + // We are very much expecting the column to be there + const column = columns.get(key)!; + + if (row[key] !== null) { + switch (column.typeId) { + case mysql.Types.DATE: + // Only parse the date part + row[key] = row[key].toISOString().split('T')[0]; + break; + case mysql.Types.DATETIME: + case ADDITIONAL_MYSQL_TYPES.DATETIME2: + case mysql.Types.TIMESTAMP: + case ADDITIONAL_MYSQL_TYPES.TIMESTAMP2: + row[key] = row[key].toISOString(); + break; + case mysql.Types.JSON: + if (typeof row[key] === 'string') { + row[key] = new JsonContainer(row[key]); + } + break; + case mysql.Types.BIT: + case mysql.Types.BLOB: + case mysql.Types.TINY_BLOB: + case mysql.Types.MEDIUM_BLOB: + case mysql.Types.LONG_BLOB: + case ADDITIONAL_MYSQL_TYPES.BINARY: + case ADDITIONAL_MYSQL_TYPES.VARBINARY: + row[key] = new Uint8Array(Object.values(row[key])); + break; + case mysql.Types.LONGLONG: + if (typeof row[key] === 'string') { + row[key] = BigInt(row[key]); + } else if (typeof row[key] === 'number') { + // Zongji returns BIGINT as a number when it can be represented as a number + row[key] = BigInt(row[key]); + } + break; + case mysql.Types.TINY: + case mysql.Types.SHORT: + case mysql.Types.LONG: + case mysql.Types.INT24: + // Handle all integer values a BigInt + if (typeof row[key] === 'number') { + row[key] = BigInt(row[key]); + } + break; + case mysql.Types.SET: + // Convert to JSON array from string + const values = row[key].split(','); + row[key] = JSONBig.stringify(values); + break; } } } diff --git a/modules/module-mysql/src/common/read-executed-gtid.ts b/modules/module-mysql/src/common/read-executed-gtid.ts index 7f224c5b9..801fba011 100644 --- a/modules/module-mysql/src/common/read-executed-gtid.ts +++ b/modules/module-mysql/src/common/read-executed-gtid.ts @@ -4,7 +4,6 @@ import { gte } from 'semver'; import { ReplicatedGTID } from './ReplicatedGTID.js'; import { getMySQLVersion } from './check-source-configuration.js'; -import { logger } from '@powersync/lib-services-framework'; /** * Gets the current master HEAD GTID @@ -33,8 +32,6 @@ export async function readExecutedGtid(connection: mysqlPromise.Connection): Pro offset: parseInt(binlogStatus.Position) }; - logger.info('Succesfully read executed GTID', { position }); - return new ReplicatedGTID({ // The head always points to the next position to start replication from position, diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index ad805bf99..a7acb8f32 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -9,9 +9,9 @@ import { BinLogEvent, StartOptions, TableMapEntry } from '@powersync/mysql-zongj import * as common from '../common/common-index.js'; import * as zongji_utils from './zongji/zongji-utils.js'; import { MySQLConnectionManager } from './MySQLConnectionManager.js'; -import { isBinlogStillAvailable, ReplicatedGTID } from '../common/common-index.js'; +import { isBinlogStillAvailable, ReplicatedGTID, toColumnDescriptors } from '../common/common-index.js'; import mysqlPromise from 'mysql2/promise'; -import { MySQLTypesMap } from '../utils/mysql_utils.js'; +import { createRandomServerId } from '../utils/mysql_utils.js'; export interface BinLogStreamOptions { connections: MySQLConnectionManager; @@ -221,7 +221,13 @@ AND table_type = 'BASE TABLE';`, // Check if the binlog is still available. If it isn't we need to snapshot again. const connection = await this.connections.getConnection(); try { - return await isBinlogStillAvailable(connection, lastKnowGTID.position.filename); + const isAvailable = await isBinlogStillAvailable(connection, lastKnowGTID.position.filename); + if (!isAvailable) { + logger.info( + `Binlog file ${lastKnowGTID.position.filename} is no longer available, starting initial replication again.` + ); + } + return isAvailable; } finally { connection.release(); } @@ -245,7 +251,7 @@ AND table_type = 'BASE TABLE';`, const connection = await this.connections.getStreamingConnection(); const promiseConnection = (connection as mysql.Connection).promise(); const headGTID = await common.readExecutedGtid(promiseConnection); - logger.info(`Using snapshot checkpoint GTID:: '${headGTID}'`); + logger.info(`Using snapshot checkpoint GTID: '${headGTID}'`); try { logger.info(`Starting initial replication`); await promiseConnection.query( @@ -285,7 +291,7 @@ AND table_type = 'BASE TABLE';`, logger.info(`Replicating ${table.qualifiedName}`); // TODO count rows and log progress at certain batch sizes - const columns = new Map(); + let columns: Map; return new Promise((resolve, reject) => { // MAX_EXECUTION_TIME(0) hint disables execution timeout for this query connection @@ -295,10 +301,7 @@ AND table_type = 'BASE TABLE';`, }) .on('fields', (fields: FieldPacket[]) => { // Map the columns and their types - fields.forEach((field) => { - const columnType = MySQLTypesMap[field.type as number]; - columns.set(field.name, { name: field.name, type: columnType, typeId: field.type }); - }); + columns = toColumnDescriptors(fields); }) .on('result', async (row) => { connection.pause(); @@ -363,10 +366,14 @@ AND table_type = 'BASE TABLE';`, async streamChanges() { // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); + const serverId = createRandomServerId(this.storage.group_id); + logger.info(`Starting replication. Created replica client with serverId:${serverId}`); const connection = await this.connections.getConnection(); const { checkpoint_lsn } = await this.storage.getStatus(); - logger.info(`Last known LSN from storage: ${checkpoint_lsn}`); + if (checkpoint_lsn) { + logger.info(`Existing checkpoint found: ${checkpoint_lsn}`); + } const fromGTID = checkpoint_lsn ? common.ReplicatedGTID.fromSerialized(checkpoint_lsn) @@ -447,7 +454,7 @@ AND table_type = 'BASE TABLE';`, zongji.on('binlog', (evt: BinLogEvent) => { if (!this.stopped) { - logger.info(`Pushing Binlog event ${evt.getEventName()}`); + logger.info(`Received Binlog event:${evt.getEventName()}`); queue.push(evt); } else { logger.info(`Replication is busy stopping, ignoring event ${evt.getEventName()}`); @@ -458,16 +465,18 @@ AND table_type = 'BASE TABLE';`, // Powersync is shutting down, don't start replicating return; } + + logger.info(`Reading binlog from: ${binLogPositionState.filename}:${binLogPositionState.offset}`); + // Only listen for changes to tables in the sync rules const includedTables = [...this.tableCache.values()].map((table) => table.table); - logger.info(`Starting replication from ${binLogPositionState.filename}:${binLogPositionState.offset}`); zongji.start({ includeEvents: ['tablemap', 'writerows', 'updaterows', 'deleterows', 'xid', 'rotate', 'gtidlog'], excludeEvents: [], includeSchema: { [this.defaultSchema]: includedTables }, filename: binLogPositionState.filename, position: binLogPositionState.offset, - serverId: this.storage.group_id + serverId: serverId } satisfies StartOptions); // Forever young @@ -516,10 +525,7 @@ AND table_type = 'BASE TABLE';`, tableEntry: TableMapEntry; } ): Promise { - const columns = new Map(); - msg.tableEntry.columns.forEach((column) => { - columns.set(column.name, { name: column.name, typeId: column.type }); - }); + const columns = toColumnDescriptors(msg.tableEntry); for (const [index, row] of msg.data.entries()) { await this.writeChange(batch, { @@ -560,8 +566,10 @@ AND table_type = 'BASE TABLE';`, Metrics.getInstance().rows_replicated_total.add(1); // "before" may be null if the replica id columns are unchanged // It's fine to treat that the same as an insert. - const beforeUpdated = payload.previous_data ? common.toSQLiteRow(payload.previous_data) : undefined; - const after = common.toSQLiteRow(payload.data); + const beforeUpdated = payload.previous_data + ? common.toSQLiteRow(payload.previous_data, payload.columns) + : undefined; + const after = common.toSQLiteRow(payload.data, payload.columns); return await batch.save({ tag: storage.SaveOperationTag.UPDATE, @@ -570,13 +578,13 @@ AND table_type = 'BASE TABLE';`, beforeReplicaId: beforeUpdated ? getUuidReplicaIdentityBson(beforeUpdated, payload.sourceTable.replicaIdColumns) : undefined, - after: common.toSQLiteRow(payload.data), + after: common.toSQLiteRow(payload.data, payload.columns), afterReplicaId: getUuidReplicaIdentityBson(after, payload.sourceTable.replicaIdColumns) }); case storage.SaveOperationTag.DELETE: Metrics.getInstance().rows_replicated_total.add(1); - const beforeDeleted = common.toSQLiteRow(payload.data); + const beforeDeleted = common.toSQLiteRow(payload.data, payload.columns); return await batch.save({ tag: storage.SaveOperationTag.DELETE, diff --git a/modules/module-mysql/src/replication/MySQLConnectionManager.ts b/modules/module-mysql/src/replication/MySQLConnectionManager.ts index b69ddb5ad..fd8cef605 100644 --- a/modules/module-mysql/src/replication/MySQLConnectionManager.ts +++ b/modules/module-mysql/src/replication/MySQLConnectionManager.ts @@ -1,6 +1,6 @@ import { NormalizedMySQLConnectionConfig } from '../types/types.js'; import mysqlPromise from 'mysql2/promise'; -import mysql, { RowDataPacket } from 'mysql2'; +import mysql, { FieldPacket, RowDataPacket } from 'mysql2'; import * as mysql_utils from '../utils/mysql_utils.js'; import ZongJi from '@powersync/mysql-zongji'; import { logger } from '@powersync/lib-services-framework'; @@ -61,7 +61,7 @@ export class MySQLConnectionManager { * @param query * @param params */ - async query(query: string, params?: any[]) { + async query(query: string, params?: any[]): Promise<[RowDataPacket[], FieldPacket[]]> { return this.promisePool.query(query, params); } diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql_utils.ts index 99741b92a..2e65d47c2 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql_utils.ts @@ -3,11 +3,6 @@ import mysql from 'mysql2'; import mysqlPromise from 'mysql2/promise'; import * as types from '../types/types.js'; -export const MySQLTypesMap: { [key: number]: string } = {}; -for (const [name, code] of Object.entries(mysql.Types)) { - MySQLTypesMap[code as number] = name; -} - export type RetriedQueryOptions = { connection: mysqlPromise.Connection; query: string; @@ -47,7 +42,21 @@ export function createPool(config: types.NormalizedMySQLConnectionConfig, option database: config.database, ssl: hasSSLOptions ? sslOptions : undefined, supportBigNumbers: true, + decimalNumbers: true, timezone: 'Z', // Ensure no auto timezone manipulation of the dates occur + jsonStrings: true, // Return JSON columns as strings ...(options || {}) }); } + +/** + * Return a random server id for a given sync rule id. + * Expected format is: 00 + * The max value for server id in MySQL is 2^32 - 1. + * We use the GTID format to keep track of our position in the binlog, no state is kept by the MySQL server, therefore + * it is ok to use a randomised server id every time. + * @param syncRuleId + */ +export function createRandomServerId(syncRuleId: number): number { + return Number.parseInt(`${syncRuleId}00${Math.floor(Math.random() * 10000)}`); +} diff --git a/modules/module-mysql/test/src/BinLogStream.test.ts b/modules/module-mysql/test/src/BinLogStream.test.ts new file mode 100644 index 000000000..7aa19cdd4 --- /dev/null +++ b/modules/module-mysql/test/src/BinLogStream.test.ts @@ -0,0 +1,332 @@ +import { putOp, removeOp } from '@core-tests/stream_utils.js'; +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; +import { BucketStorageFactory, Metrics } from '@powersync/service-core'; +import { describe, expect, test } from 'vitest'; +import { binlogStreamTest } from './BinlogStreamUtils.js'; + +type StorageFactory = () => Promise; + +const BASIC_SYNC_RULES = ` +bucket_definitions: + global: + data: + - SELECT id, description FROM "test_data" +`; + +describe( + ' Binlog stream - mongodb', + function () { + defineBinlogStreamTests(MONGO_STORAGE_FACTORY); + }, + { timeout: 20_000 } +); + +function defineBinlogStreamTests(factory: StorageFactory) { + test( + 'Replicate basic values', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(` + bucket_definitions: + global: + data: + - SELECT id, description, num FROM "test_data"`); + + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, num BIGINT)` + ); + + await context.replicateSnapshot(); + + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + context.startStreaming(); + await connectionManager.query(`INSERT INTO test_data(description, num) VALUES('test1', 1152921504606846976)`); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test1' AND num = 1152921504606846976` + ); + const testId = result.test_id; + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1', num: 1152921504606846976n })]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + expect(endRowCount - startRowCount).toEqual(1); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); + + test( + 'replicating case sensitive table', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(` + bucket_definitions: + global: + data: + - SELECT id, description FROM "test_DATA" + `); + + await connectionManager.query( + `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); + + await context.replicateSnapshot(); + + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + context.startStreaming(); + + await connectionManager.query(`INSERT INTO test_DATA(description) VALUES('test1')`); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_DATA WHERE description = 'test1'` + ); + const testId = result.test_id; + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([putOp('test_DATA', { id: testId, description: 'test1' })]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + expect(endRowCount - startRowCount).toEqual(1); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); + + // TODO: Not supported yet + // test( + // 'replicating TRUNCATE', + // binlogStreamTest(factory, async (context) => { + // const { connectionManager } = context; + // const syncRuleContent = ` + // bucket_definitions: + // global: + // data: + // - SELECT id, description FROM "test_data" + // by_test_data: + // parameters: SELECT id FROM test_data WHERE id = token_parameters.user_id + // data: [] + // `; + // await context.updateSyncRules(syncRuleContent); + // await connectionManager.query(`DROP TABLE IF EXISTS test_data`); + // await connectionManager.query( + // `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)` + // ); + // + // await context.replicateSnapshot(); + // context.startStreaming(); + // + // const [{ test_id }] = pgwireRows( + // await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1') returning id as test_id`) + // ); + // await connectionManager.query(`TRUNCATE test_data`); + // + // const data = await context.getBucketData('global[]'); + // + // expect(data).toMatchObject([ + // putOp('test_data', { id: test_id, description: 'test1' }), + // removeOp('test_data', test_id) + // ]); + // }) + // ); + + test( + 'replicating changing primary key', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); + + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); + + await context.replicateSnapshot(); + context.startStreaming(); + + await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); + const [[result1]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test1'` + ); + const testId1 = result1.test_id; + + await connectionManager.query(`UPDATE test_data SET id = UUID(), description = 'test2a' WHERE id = '${testId1}'`); + const [[result2]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test2a'` + ); + const testId2 = result2.test_id; + + // This update may fail replicating with: + // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} + await connectionManager.query(`UPDATE test_data SET description = 'test2b' WHERE id = '${testId2}'`); + + // Re-use old id again + await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId1}', 'test1b')`); + await connectionManager.query(`UPDATE test_data SET description = 'test1c' WHERE id = '${testId1}'`); + + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([ + // Initial insert + putOp('test_data', { id: testId1, description: 'test1' }), + // Update id, then description + removeOp('test_data', testId1), + putOp('test_data', { id: testId2, description: 'test2a' }), + putOp('test_data', { id: testId2, description: 'test2b' }), + // Re-use old id + putOp('test_data', { id: testId1, description: 'test1b' }), + putOp('test_data', { id: testId1, description: 'test1c' }) + ]); + }) + ); + + test( + 'initial sync', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); + + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); + + await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'test1'` + ); + const testId = result.test_id; + + await context.replicateSnapshot(); + + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1' })]); + }) + ); + + test( + 'snapshot with date values', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(` + bucket_definitions: + global: + data: + - SELECT * FROM "test_data" + `); + + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, date DATE, datetime DATETIME, timestamp TIMESTAMP)` + ); + + await connectionManager.query(` + INSERT INTO test_data(description, date, datetime, timestamp) VALUES('testDates', '2023-03-06', '2023-03-06 15:47', '2023-03-06 15:47') + `); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'testDates'` + ); + const testId = result.test_id; + + await context.replicateSnapshot(); + + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([ + putOp('test_data', { + id: testId, + description: 'testDates', + date: `2023-03-06`, + datetime: '2023-03-06T15:47:00.000Z', + timestamp: '2023-03-06T15:47:00.000Z' + }) + ]); + }) + ); + + test( + 'replication with date values', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(` + bucket_definitions: + global: + data: + - SELECT * FROM "test_data" + `); + + await connectionManager.query( + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, date DATE, datetime DATETIME, timestamp TIMESTAMP)` + ); + + await context.replicateSnapshot(); + + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + context.startStreaming(); + + await connectionManager.query(` + INSERT INTO test_data(description, date, datetime, timestamp) VALUES('testDates', '2023-03-06', '2023-03-06 15:47', '2023-03-06 15:47') + `); + const [[result]] = await connectionManager.query( + `SELECT id AS test_id FROM test_data WHERE description = 'testDates'` + ); + const testId = result.test_id; + + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([ + putOp('test_data', { + id: testId, + description: 'testDates', + date: `2023-03-06`, + datetime: '2023-03-06T15:47:00.000Z', + timestamp: '2023-03-06T15:47:00.000Z' + }) + ]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + expect(endRowCount - startRowCount).toEqual(1); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); + + test( + 'table not in sync rules', + binlogStreamTest(factory, async (context) => { + const { connectionManager } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); + + await connectionManager.query( + `CREATE TABLE test_donotsync (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` + ); + + await context.replicateSnapshot(); + + const startRowCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const startTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + context.startStreaming(); + + await connectionManager.query(`INSERT INTO test_donotsync(description) VALUES('test1')`); + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([]); + const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0; + const endTxCount = + (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; + + // There was a transaction, but we should not replicate any actual data + expect(endRowCount - startRowCount).toEqual(0); + expect(endTxCount - startTxCount).toEqual(1); + }) + ); +} diff --git a/modules/module-mysql/test/src/BinlogStreamUtils.ts b/modules/module-mysql/test/src/BinlogStreamUtils.ts new file mode 100644 index 000000000..c08f22c60 --- /dev/null +++ b/modules/module-mysql/test/src/BinlogStreamUtils.ts @@ -0,0 +1,157 @@ +import { + ActiveCheckpoint, + BucketStorageFactory, + OpId, + OplogEntry, + SyncRulesBucketStorage +} from '@powersync/service-core'; +import { TEST_CONNECTION_OPTIONS, clearTestDb } from './util.js'; +import { fromAsync } from '@core-tests/stream_utils.js'; +import { BinLogStream, BinLogStreamOptions } from '@module/replication/BinLogStream.js'; +import { MySQLConnectionManager } from '@module/replication/MySQLConnectionManager.js'; +import mysqlPromise from 'mysql2/promise'; +import { readExecutedGtid } from '@module/common/read-executed-gtid.js'; +import { logger } from '@powersync/lib-services-framework'; + +/** + * Tests operating on the binlog stream need to configure the stream and manage asynchronous + * replication, which gets a little tricky. + * + * This wraps a test in a function that configures all the context, and tears it down afterward. + */ +export function binlogStreamTest( + factory: () => Promise, + test: (context: BinlogStreamTestContext) => Promise +): () => Promise { + return async () => { + const f = await factory(); + const connectionManager = new MySQLConnectionManager(TEST_CONNECTION_OPTIONS, {}); + + const connection = await connectionManager.getConnection(); + await clearTestDb(connection); + connection.release(); + const context = new BinlogStreamTestContext(f, connectionManager); + try { + await test(context); + } finally { + await context.dispose(); + } + }; +} + +export class BinlogStreamTestContext { + private _binlogStream?: BinLogStream; + private abortController = new AbortController(); + private streamPromise?: Promise; + public storage?: SyncRulesBucketStorage; + private replicationDone = false; + + constructor( + public factory: BucketStorageFactory, + public connectionManager: MySQLConnectionManager + ) {} + + async dispose() { + this.abortController.abort(); + await this.streamPromise; + await this.connectionManager.end(); + } + + get connectionTag() { + return this.connectionManager.connectionTag; + } + + async updateSyncRules(content: string): Promise { + const syncRules = await this.factory.updateSyncRules({ content: content }); + this.storage = this.factory.getInstance(syncRules); + return this.storage!; + } + + get binlogStream(): BinLogStream { + if (this.storage == null) { + throw new Error('updateSyncRules() first'); + } + if (this._binlogStream) { + return this._binlogStream; + } + const options: BinLogStreamOptions = { + storage: this.storage, + connections: this.connectionManager, + abortSignal: this.abortController.signal + }; + this._binlogStream = new BinLogStream(options); + return this._binlogStream!; + } + + async replicateSnapshot() { + await this.binlogStream.initReplication(); + this.replicationDone = true; + } + + startStreaming() { + if (!this.replicationDone) { + throw new Error('Call replicateSnapshot() before startStreaming()'); + } + this.streamPromise = this.binlogStream.streamChanges(); + } + + async getCheckpoint(options?: { timeout?: number }): Promise { + const connection = await this.connectionManager.getConnection(); + let checkpoint = await Promise.race([ + getClientCheckpoint(connection, this.factory, { timeout: options?.timeout ?? 60_000 }), + this.streamPromise + ]); + connection.release(); + if (typeof checkpoint == undefined) { + // This indicates an issue with the test setup - streamingPromise completed instead + // of getClientCheckpoint() + throw new Error('Test failure - streamingPromise completed'); + } + return checkpoint as string; + } + + async getBucketsDataBatch(buckets: Record, options?: { timeout?: number }) { + const checkpoint = await this.getCheckpoint(options); + const map = new Map(Object.entries(buckets)); + return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map)); + } + + async getBucketData(bucket: string, start = '0', options?: { timeout?: number }): Promise { + const checkpoint = await this.getCheckpoint(options); + const map = new Map([[bucket, start]]); + const batch = this.storage!.getBucketDataBatch(checkpoint, map); + const batches = await fromAsync(batch); + return batches[0]?.batch.data ?? []; + } +} + +export async function getClientCheckpoint( + connection: mysqlPromise.Connection, + bucketStorage: BucketStorageFactory, + options?: { timeout?: number } +): Promise { + const start = Date.now(); + const gtid = await readExecutedGtid(connection); + // This old API needs a persisted checkpoint id. + // Since we don't use LSNs anymore, the only way to get that is to wait. + + const timeout = options?.timeout ?? 50_000; + let lastCp: ActiveCheckpoint | null = null; + + logger.info('Expected Checkpoint: ' + gtid.comparable); + while (Date.now() - start < timeout) { + const cp = await bucketStorage.getActiveCheckpoint(); + lastCp = cp; + //logger.info('Last Checkpoint: ' + lastCp.lsn); + if (!cp.hasSyncRules()) { + throw new Error('No sync rules available'); + } + if (cp.lsn && cp.lsn >= gtid.comparable) { + return cp.checkpoint; + } + + await new Promise((resolve) => setTimeout(resolve, 30)); + } + + throw new Error(`Timeout while waiting for checkpoint ${gtid.comparable}. Last checkpoint: ${lastCp?.lsn}`); +} diff --git a/modules/module-mysql/test/src/MysqlTypeMappings.test.ts b/modules/module-mysql/test/src/MysqlTypeMappings.test.ts new file mode 100644 index 000000000..9cebdccd2 --- /dev/null +++ b/modules/module-mysql/test/src/MysqlTypeMappings.test.ts @@ -0,0 +1,322 @@ +import { SqliteRow } from '@powersync/service-sync-rules'; +import { afterAll, describe, expect, test } from 'vitest'; +import { clearTestDb, TEST_CONNECTION_OPTIONS } from './util.js'; +import { eventIsWriteMutation, eventIsXid } from '@module/replication/zongji/zongji-utils.js'; +import * as common from '@module/common/common-index.js'; +import ZongJi, { BinLogEvent } from '@powersync/mysql-zongji'; +import { MySQLConnectionManager } from '@module/replication/MySQLConnectionManager.js'; +import { toColumnDescriptors } from '@module/common/common-index.js'; + +describe('MySQL Data Types', () => { + const connectionManager = new MySQLConnectionManager(TEST_CONNECTION_OPTIONS, {}); + + afterAll(async () => { + await connectionManager.end(); + }); + + async function setupTable() { + const connection = await connectionManager.getConnection(); + await clearTestDb(connection); + await connection.query(`CREATE TABLE test_data ( + tinyint_col TINYINT, + smallint_col SMALLINT, + mediumint_col MEDIUMINT, + int_col INT, + integer_col INTEGER, + bigint_col BIGINT, + float_col FLOAT, + double_col DOUBLE, + decimal_col DECIMAL(10,2), + numeric_col NUMERIC(10,2), + bit_col BIT(8), + boolean_col BOOLEAN, + serial_col SERIAL, + + date_col DATE, + datetime_col DATETIME(3), + timestamp_col TIMESTAMP(3), + time_col TIME, + year_col YEAR, + + char_col CHAR(10), + varchar_col VARCHAR(255), + binary_col BINARY(16), + varbinary_col VARBINARY(256), + tinyblob_col TINYBLOB, + blob_col BLOB, + mediumblob_col MEDIUMBLOB, + longblob_col LONGBLOB, + tinytext_col TINYTEXT, + text_col TEXT, + mediumtext_col MEDIUMTEXT, + longtext_col LONGTEXT, + enum_col ENUM('value1', 'value2', 'value3'), + set_col SET('value1', 'value2', 'value3'), + + json_col JSON, + + geometry_col GEOMETRY, + point_col POINT, + linestring_col LINESTRING, + polygon_col POLYGON, + multipoint_col MULTIPOINT, + multilinestring_col MULTILINESTRING, + multipolygon_col MULTIPOLYGON, + geometrycollection_col GEOMETRYCOLLECTION + )`); + + connection.release(); + } + + test('Number types mappings', async () => { + await setupTable(); + await connectionManager.query(` +INSERT INTO test_data ( + tinyint_col, + smallint_col, + mediumint_col, + int_col, + integer_col, + bigint_col, + double_col, + decimal_col, + numeric_col, + bit_col, + boolean_col + -- serial_col is auto-incremented and can be left out +) VALUES ( + 127, -- TINYINT maximum value + 32767, -- SMALLINT maximum value + 8388607, -- MEDIUMINT maximum value + 2147483647, -- INT maximum value + 2147483647, -- INTEGER maximum value + 9223372036854775807, -- BIGINT maximum value + 3.1415926535, -- DOUBLE example + 12345.67, -- DECIMAL(10,2) example + 12345.67, -- NUMERIC(10,2) example + b'10101010', -- BIT(8) example in binary notation + TRUE -- BOOLEAN value (alias for TINYINT(1)) + -- serial_col is auto-incremented +)`); + + const databaseRows = await getDatabaseRows(connectionManager, 'test_data'); + const replicatedRows = await getReplicatedRows(); + + const expectedResult = { + tinyint_col: 127n, + smallint_col: 32767n, + mediumint_col: 8388607n, + int_col: 2147483647n, + integer_col: 2147483647n, + bigint_col: 9223372036854775807n, + double_col: 3.1415926535, + decimal_col: 12345.67, + numeric_col: 12345.67, + bit_col: new Uint8Array([0b10101010]).valueOf(), + boolean_col: 1n, + serial_col: 1n + }; + expect(databaseRows[0]).toMatchObject(expectedResult); + expect(replicatedRows[0]).toMatchObject(expectedResult); + }); + + test('Float type mapping', async () => { + await setupTable(); + const expectedFloatValue = 3.14; + await connectionManager.query(`INSERT INTO test_data (float_col) VALUES (${expectedFloatValue})`); + + const databaseRows = await getDatabaseRows(connectionManager, 'test_data'); + const replicatedRows = await getReplicatedRows(); + + const allowedPrecision = 0.0001; + + const actualFloatValueDB = databaseRows[0].float_col; + let difference = Math.abs((actualFloatValueDB as number) - expectedFloatValue); + expect(difference).toBeLessThan(allowedPrecision); + + const actualFloatValueReplicated = replicatedRows[0].float_col; + difference = Math.abs((actualFloatValueReplicated as number) - expectedFloatValue); + expect(difference).toBeLessThan(allowedPrecision); + }); + + test('Character types mappings', async () => { + await setupTable(); + await connectionManager.query(` +INSERT INTO test_data ( + char_col, + varchar_col, + binary_col, + varbinary_col, + tinyblob_col, + blob_col, + mediumblob_col, + longblob_col, + tinytext_col, + text_col, + mediumtext_col, + longtext_col, + enum_col +) VALUES ( + 'CharData', -- CHAR(10) with padding spaces + 'Variable character data',-- VARCHAR(255) + 'ShortBin', -- BINARY(16) + 'VariableBinaryData', -- VARBINARY(256) + 'TinyBlobData', -- TINYBLOB + 'BlobData', -- BLOB + 'MediumBlobData', -- MEDIUMBLOB + 'LongBlobData', -- LONGBLOB + 'TinyTextData', -- TINYTEXT + 'TextData', -- TEXT + 'MediumTextData', -- MEDIUMTEXT + 'LongTextData', -- LONGTEXT + 'value1' -- ENUM('value1', 'value2', 'value3') +);`); + + const databaseRows = await getDatabaseRows(connectionManager, 'test_data'); + const replicatedRows = await getReplicatedRows(); + const expectedResult = { + char_col: 'CharData', + varchar_col: 'Variable character data', + binary_col: new Uint8Array([83, 104, 111, 114, 116, 66, 105, 110, 0, 0, 0, 0, 0, 0, 0, 0]), // Pad with 0 + varbinary_col: new Uint8Array([ + 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61 + ]), + tinyblob_col: new Uint8Array([0x54, 0x69, 0x6e, 0x79, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x61, 0x74, 0x61]), + blob_col: new Uint8Array([0x42, 0x6c, 0x6f, 0x62, 0x44, 0x61, 0x74, 0x61]), + mediumblob_col: new Uint8Array([ + 0x4d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x61, 0x74, 0x61 + ]), + longblob_col: new Uint8Array([0x4c, 0x6f, 0x6e, 0x67, 0x42, 0x6c, 0x6f, 0x62, 0x44, 0x61, 0x74, 0x61]), + tinytext_col: 'TinyTextData', + text_col: 'TextData', + mediumtext_col: 'MediumTextData', + longtext_col: 'LongTextData', + enum_col: 'value1' + }; + + expect(databaseRows[0]).toMatchObject(expectedResult); + expect(replicatedRows[0]).toMatchObject(expectedResult); + }); + + test('Date types mappings', async () => { + await setupTable(); + await connectionManager.query(` + INSERT INTO test_data(date_col, datetime_col, timestamp_col, time_col, year_col) + VALUES('2023-03-06', '2023-03-06 15:47', '2023-03-06 15:47', '15:47:00', '2023'); + `); + + const databaseRows = await getDatabaseRows(connectionManager, 'test_data'); + const replicatedRows = await getReplicatedRows(); + const expectedResult = { + date_col: '2023-03-06', + datetime_col: '2023-03-06T15:47:00.000Z', + timestamp_col: '2023-03-06T15:47:00.000Z', + time_col: '15:47:00', + year_col: 2023 + }; + + expect(databaseRows[0]).toMatchObject(expectedResult); + expect(replicatedRows[0]).toMatchObject(expectedResult); + }); + + test('Date types edge cases mappings', async () => { + await setupTable(); + + await connectionManager.query(`INSERT INTO test_data(timestamp_col) VALUES('1970-01-01 00:00:01')`); + await connectionManager.query(`INSERT INTO test_data(timestamp_col) VALUES('2038-01-19 03:14:07.499')`); + await connectionManager.query(`INSERT INTO test_data(datetime_col) VALUES('1000-01-01 00:00:00')`); + await connectionManager.query(`INSERT INTO test_data(datetime_col) VALUES('9999-12-31 23:59:59.499')`); + + const databaseRows = await getDatabaseRows(connectionManager, 'test_data'); + const replicatedRows = await getReplicatedRows(4); + const expectedResults = [ + { timestamp_col: '1970-01-01T00:00:01.000Z' }, + { timestamp_col: '2038-01-19T03:14:07.499Z' }, + { datetime_col: '1000-01-01T00:00:00.000Z' }, + { datetime_col: '9999-12-31T23:59:59.499Z' } + ]; + + for (let i = 0; i < expectedResults.length; i++) { + expect(databaseRows[i]).toMatchObject(expectedResults[i]); + expect(replicatedRows[i]).toMatchObject(expectedResults[i]); + } + }); + + test('Json types mappings', async () => { + await setupTable(); + + const expectedJSON = { name: 'John Doe', age: 30, married: true }; + const expectedSet = ['value1', 'value3']; + + // For convenience, we map the SET data type to a JSON Array + await connectionManager.query( + `INSERT INTO test_data (json_col, set_col) VALUES ('${JSON.stringify(expectedJSON)}', '${expectedSet.join(',')}')` + ); + + const databaseRows = await getDatabaseRows(connectionManager, 'test_data'); + const replicatedRows = await getReplicatedRows(); + + const actualDBJSONValue = JSON.parse(databaseRows[0].json_col as string); + const actualReplicatedJSONValue = JSON.parse(replicatedRows[0].json_col as string); + expect(actualDBJSONValue).toEqual(expectedJSON); + expect(actualReplicatedJSONValue).toEqual(expectedJSON); + + const actualDBSetValue = JSON.parse(databaseRows[0].set_col as string); + const actualReplicatedSetValue = JSON.parse(replicatedRows[0].set_col as string); + expect(actualDBSetValue).toEqual(expectedSet); + expect(actualReplicatedSetValue).toEqual(expectedSet); + }); +}); + +async function getDatabaseRows(connection: MySQLConnectionManager, tableName: string): Promise { + const [results, fields] = await connection.query(`SELECT * FROM ${tableName}`); + const columns = toColumnDescriptors(fields); + return results.map((row) => common.toSQLiteRow(row, columns)); +} + +/** + * Return all the inserts from the first transaction in the binlog stream. + */ +async function getReplicatedRows(expectedTransactionsCount?: number): Promise { + let transformed: SqliteRow[] = []; + const zongji = new ZongJi({ + host: TEST_CONNECTION_OPTIONS.hostname, + user: TEST_CONNECTION_OPTIONS.username, + password: TEST_CONNECTION_OPTIONS.password, + timeZone: 'Z' // Ensure no auto timezone manipulation of the dates occur + }); + + const completionPromise = new Promise((resolve, reject) => { + zongji.on('binlog', (evt: BinLogEvent) => { + try { + if (eventIsWriteMutation(evt)) { + const tableMapEntry = evt.tableMap[evt.tableId]; + const columns = toColumnDescriptors(tableMapEntry); + const records = evt.rows.map((row: Record) => common.toSQLiteRow(row, columns)); + transformed.push(...records); + } else if (eventIsXid(evt)) { + if (expectedTransactionsCount !== undefined) { + expectedTransactionsCount--; + if (expectedTransactionsCount == 0) { + zongji.stop(); + resolve(transformed); + } + } else { + zongji.stop(); + resolve(transformed); + } + } + } catch (e) { + reject(e); + } + }); + }); + + zongji.start({ + includeEvents: ['tablemap', 'writerows', 'xid'], + filename: 'mysql-bin.000001', + position: 0 + }); + + return completionPromise; +} diff --git a/modules/module-mysql/test/src/env.ts b/modules/module-mysql/test/src/env.ts index 3dad20a22..05fc76c42 100644 --- a/modules/module-mysql/test/src/env.ts +++ b/modules/module-mysql/test/src/env.ts @@ -1,7 +1,7 @@ import { utils } from '@powersync/lib-services-framework'; export const env = utils.collectEnvironmentVariables({ - MYSQL_TEST_URI: utils.type.string.default('mysql://myuser:mypassword@localhost:3306/mydatabase'), + MYSQL_TEST_URI: utils.type.string.default('mysql://root:mypassword@localhost:3306/mydatabase'), CI: utils.type.boolean.default('false'), SLOW_TESTS: utils.type.boolean.default('false') }); diff --git a/modules/module-mysql/test/src/util.ts b/modules/module-mysql/test/src/util.ts index a489e0b1b..135cb7e08 100644 --- a/modules/module-mysql/test/src/util.ts +++ b/modules/module-mysql/test/src/util.ts @@ -5,7 +5,13 @@ import mysqlPromise from 'mysql2/promise'; import { connectMongo } from '@core-tests/util.js'; import { getMySQLVersion } from '@module/common/check-source-configuration.js'; import { gte } from 'semver'; -import { RowDataPacket } from 'mysql2'; + +export const TEST_URI = env.MYSQL_TEST_URI; + +export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ + type: 'mysql', + uri: TEST_URI +}); // The metrics need to be initialized before they can be used await Metrics.initialise({ @@ -15,13 +21,6 @@ await Metrics.initialise({ }); Metrics.getInstance().resetCounters(); -export const TEST_URI = env.MYSQL_TEST_URI; - -export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({ - type: 'mysql', - uri: TEST_URI -}); - export type StorageFactory = () => Promise; export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { @@ -37,7 +36,7 @@ export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { return new MongoBucketStorage(db, { slot_name_prefix: 'test_' }); }; -export async function clearAndRecreateTestDb(connection: mysqlPromise.Connection) { +export async function clearTestDb(connection: mysqlPromise.Connection) { const version = await getMySQLVersion(connection); if (gte(version, '8.4.0')) { await connection.query('RESET BINARY LOGS AND GTIDS'); @@ -45,11 +44,7 @@ export async function clearAndRecreateTestDb(connection: mysqlPromise.Connection await connection.query('RESET MASTER'); } - // await connection.query(`DROP DATABASE IF EXISTS ${TEST_CONNECTION_OPTIONS.database}`); - // - // await connection.query(`CREATE DATABASE IF NOT EXISTS ${TEST_CONNECTION_OPTIONS.database}`); - - const [result] = await connection.query( + const [result] = await connection.query( `SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = '${TEST_CONNECTION_OPTIONS.database}'` ); @@ -60,3 +55,7 @@ export async function clearAndRecreateTestDb(connection: mysqlPromise.Connection } } } + +export function connectMySQLPool(): mysqlPromise.Pool { + return mysqlPromise.createPool(TEST_URI); +} diff --git a/modules/module-mysql/test/tsconfig.json b/modules/module-mysql/test/tsconfig.json index 18898c4ee..5257b2739 100644 --- a/modules/module-mysql/test/tsconfig.json +++ b/modules/module-mysql/test/tsconfig.json @@ -13,7 +13,7 @@ "@core-tests/*": ["../../../packages/service-core/test/src/*"] } }, - "include": ["src"], + "include": ["src", "../src/replication/zongji/zongji.d.ts"], "references": [ { "path": "../" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4f1bd0e4..cd892d264 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,11 +143,14 @@ importers: specifier: workspace:* version: link:../../libs/lib-services '@powersync/mysql-zongji': - specifier: 0.0.0-dev-20241023144335 - version: 0.0.0-dev-20241023144335 + specifier: 0.0.0-dev-20241031142605 + version: 0.0.0-dev-20241031142605 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core + '@powersync/service-jsonbig': + specifier: workspace:* + version: link:../../packages/jsonbig '@powersync/service-sync-rules': specifier: workspace:* version: link:../../packages/sync-rules @@ -1178,8 +1181,8 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@powersync/mysql-zongji@0.0.0-dev-20241023144335': - resolution: {integrity: sha512-77A5ld4Egm0KTHDUzBSP+MHH1+ibp1Es1jlaGZmHWNzmXKNiScd0jxkPDxna9CTfRvOoAu8R0T3MTAuK0aDQpg==} + '@powersync/mysql-zongji@0.0.0-dev-20241031142605': + resolution: {integrity: sha512-LATx5xfJjXcbvW3s9yCy2tlhhsgOuaUoUk0/EH67sgbTQQFZTfBVO/2gzdJH5GvGgONdqoPBQ7skWNwBF/dEXw==} engines: {node: '>=20.0.0'} '@prisma/instrumentation@5.16.1': @@ -4579,7 +4582,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/mysql-zongji@0.0.0-dev-20241023144335': + '@powersync/mysql-zongji@0.0.0-dev-20241031142605': dependencies: '@vlasky/mysql': 2.18.6 big-integer: 1.6.51 diff --git a/service/src/runners/unified-runner.ts b/service/src/runners/unified-runner.ts index 687e8f38c..4997d8933 100644 --- a/service/src/runners/unified-runner.ts +++ b/service/src/runners/unified-runner.ts @@ -31,9 +31,9 @@ export const startUnifiedRunner = async (runnerConfig: core.utils.RunnerConfig) const moduleManager = container.getImplementation(core.modules.ModuleManager); await moduleManager.initialize(serviceContext); - logger.info('Starting service'); + logger.info('Starting service...'); await serviceContext.lifeCycleEngine.start(); - logger.info('service started'); + logger.info('Service started'); await container.probes.ready(); From 6ed725d65821db9d7f6a1fbfc8c4cd11615f7634 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 5 Nov 2024 17:23:58 +0200 Subject: [PATCH 272/298] Properly replicate additional MongoDB types: Decimal, RegExp, MinKey, MaxKey. --- modules/module-mongodb/package.json | 2 +- .../src/api/MongoRouteAPIAdapter.ts | 8 +++++ .../src/replication/MongoRelation.ts | 12 ++++++++ .../test/src/mongo_test.test.ts | 30 ++++++++++++++----- modules/module-mongodb/vitest.config.ts | 8 ++++- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/modules/module-mongodb/package.json b/modules/module-mongodb/package.json index 16b65a4ab..d928ec513 100644 --- a/modules/module-mongodb/package.json +++ b/modules/module-mongodb/package.json @@ -13,7 +13,7 @@ "build": "tsc -b", "build:tests": "tsc -b test/tsconfig.json", "clean": "rm -rf ./dist && tsc -b --clean", - "test": "vitest --no-threads" + "test": "vitest" }, "exports": { ".": { diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index dd611ce9b..b9b740e24 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -313,6 +313,14 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { return 'Binary'; } else if (data instanceof mongo.Long) { return 'Long'; + } else if (data instanceof RegExp) { + return 'RegExp'; + } else if (data instanceof mongo.MinKey) { + return 'MinKey'; + } else if (data instanceof mongo.MaxKey) { + return 'MaxKey'; + } else if (data instanceof mongo.Decimal128) { + return 'Decimal'; } else if (Array.isArray(data)) { return 'Array'; } else if (data instanceof Uint8Array) { diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index 267674895..1a418a6ae 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -62,6 +62,12 @@ export function toMongoSyncRulesValue(data: any): SqliteValue { return new Uint8Array(data.buffer); } else if (data instanceof mongo.Long) { return data.toBigInt(); + } else if (data instanceof mongo.Decimal128) { + return data.toString(); + } else if (data instanceof mongo.MinKey || data instanceof mongo.MaxKey) { + return null; + } else if (data instanceof RegExp) { + return JSON.stringify({ pattern: data.source, options: data.flags }); } else if (Array.isArray(data)) { // We may be able to avoid some parse + stringify cycles here for JsonSqliteContainer. return JSONBig.stringify(data.map((element) => filterJsonData(element))); @@ -112,6 +118,12 @@ function filterJsonData(data: any, depth = 0): any { return undefined; } else if (data instanceof mongo.Long) { return data.toBigInt(); + } else if (data instanceof mongo.Decimal128) { + return data.toString(); + } else if (data instanceof mongo.MinKey || data instanceof mongo.MaxKey) { + return data._bsontype; + } else if (data instanceof mongo.BSONRegExp) { + return JSON.stringify({ pattern: data.pattern, options: data.options }); } else if (Array.isArray(data)) { return data.map((element) => filterJsonData(element, depth + 1)); } else if (ArrayBuffer.isView(data)) { diff --git a/modules/module-mongodb/test/src/mongo_test.test.ts b/modules/module-mongodb/test/src/mongo_test.test.ts index ed03ebcd9..c3b2b15c5 100644 --- a/modules/module-mongodb/test/src/mongo_test.test.ts +++ b/modules/module-mongodb/test/src/mongo_test.test.ts @@ -23,14 +23,18 @@ describe('mongo data types', () => { int2: 1000, int4: 1000000, int8: 9007199254740993n, - float: 3.14 + float: 3.14, + decimal: new mongo.Decimal128('3.14') }, { _id: 2 as any, nested: { test: 'thing' } }, { _id: 3 as any, date: new Date('2023-03-06 15:47+02') }, { _id: 4 as any, timestamp: mongo.Timestamp.fromBits(123, 456), - objectId: mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb') + objectId: mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb'), + regexp: new mongo.BSONRegExp('test', 'i'), + minKey: new mongo.MinKey(), + maxKey: new mongo.MaxKey() } ]); } @@ -47,14 +51,18 @@ describe('mongo data types', () => { int2: [1000], int4: [1000000], int8: [9007199254740993n], - float: [3.14] + float: [3.14], + decimal: [new mongo.Decimal128('3.14')] }, { _id: 2 as any, nested: [{ test: 'thing' }] }, { _id: 3 as any, date: [new Date('2023-03-06 15:47+02')] }, { _id: 10 as any, timestamp: [mongo.Timestamp.fromBits(123, 456)], - objectId: [mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')] + objectId: [mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')], + regexp: [new mongo.BSONRegExp('test', 'i')], + minKey: [new mongo.MinKey()], + maxKey: [new mongo.MaxKey()] } ]); } @@ -70,7 +78,8 @@ describe('mongo data types', () => { int4: 1000000n, int8: 9007199254740993n, float: 3.14, - null: null + null: null, + decimal: '3.14' }); expect(transformed[1]).toMatchObject({ _id: 2n, @@ -85,7 +94,10 @@ describe('mongo data types', () => { expect(transformed[3]).toMatchObject({ _id: 4n, objectId: '66e834cc91d805df11fa0ecb', - timestamp: 1958505087099n + timestamp: 1958505087099n, + regexp: '{"pattern":"test","options":"i"}', + minKey: null, + maxKey: null }); } @@ -220,7 +232,7 @@ describe('mongo data types', () => { const schema = await adapter.getConnectionSchema(); const dbSchema = schema.filter((s) => s.name == TEST_CONNECTION_OPTIONS.database)[0]; expect(dbSchema).not.toBeNull(); - expect(dbSchema.tables).toEqual([ + expect(dbSchema.tables).toMatchObject([ { name: 'test_data', columns: [ @@ -228,13 +240,17 @@ describe('mongo data types', () => { { name: 'bool', sqlite_type: 4, internal_type: 'Boolean' }, { name: 'bytea', sqlite_type: 1, internal_type: 'Binary' }, { name: 'date', sqlite_type: 2, internal_type: 'Date' }, + { name: 'decimal', sqlite_type: 2, internal_type: 'Decimal' }, { name: 'float', sqlite_type: 8, internal_type: 'Double' }, { name: 'int2', sqlite_type: 4, internal_type: 'Integer' }, { name: 'int4', sqlite_type: 4, internal_type: 'Integer' }, { name: 'int8', sqlite_type: 4, internal_type: 'Long' }, + { name: 'maxKey', sqlite_type: 0, internal_type: 'MaxKey' }, + { name: 'minKey', sqlite_type: 0, internal_type: 'MinKey' }, { name: 'nested', sqlite_type: 2, internal_type: 'Object' }, { name: 'null', sqlite_type: 0, internal_type: 'Null' }, { name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' }, + { name: 'regexp', sqlite_type: 2, internal_type: 'RegExp' }, { name: 'text', sqlite_type: 2, internal_type: 'String' }, { name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' }, { name: 'uuid', sqlite_type: 2, internal_type: 'UUID' } diff --git a/modules/module-mongodb/vitest.config.ts b/modules/module-mongodb/vitest.config.ts index b392696b7..7a39c1f71 100644 --- a/modules/module-mongodb/vitest.config.ts +++ b/modules/module-mongodb/vitest.config.ts @@ -4,6 +4,12 @@ import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ plugins: [tsconfigPaths()], test: { - setupFiles: './test/src/setup.ts' + setupFiles: './test/src/setup.ts', + poolOptions: { + threads: { + singleThread: true + } + }, + pool: 'threads' } }); From 14bdceba654b60a62268a918a508762a4641c577 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 5 Nov 2024 17:26:38 +0200 Subject: [PATCH 273/298] Test mongodb on GA. --- .github/workflows/test.yml | 53 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3705cd600..5d904012e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -143,3 +143,56 @@ jobs: - name: Test run: pnpm test --filter='./modules/module-postgres' + + run-mongodb-tests: + name: MongoDB Test + runs-on: ubuntu-latest + needs: run-core-tests + + strategy: + fail-fast: false + matrix: + mongodb-version: ['6.0', '7.0'] + + steps: + - uses: actions/checkout@v4 + + - name: Start MongoDB + uses: supercharge/mongodb-github-action@1.8.0 + with: + mongodb-version: ${{ matrix.mongodb-version }} + mongodb-replica-set: test-rs + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - uses: pnpm/action-setup@v4 + name: Install pnpm + with: + version: 9 + run_install: false + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install + + - name: Build + shell: bash + run: pnpm build + + - name: Test + run: pnpm test --filter='./modules/module-mongodb' From 3bca30f35aeacfcde164d65f6e9092e7c7af87c0 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 5 Nov 2024 17:42:43 +0200 Subject: [PATCH 274/298] Also test with MongoDB 8.0. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fedcf60e5..5ef3c3f1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -217,7 +217,7 @@ jobs: strategy: fail-fast: false matrix: - mongodb-version: ['6.0', '7.0'] + mongodb-version: ['6.0', '7.0', '8.0'] steps: - uses: actions/checkout@v4 From 1e50d6b06c396aac80af9555961dc3b26dd7d83f Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 6 Nov 2024 13:04:54 +0200 Subject: [PATCH 275/298] Support compacting specific buckets when compacting manually. --- .../src/entry/commands/compact-action.ts | 13 +++++++++--- .../service-core/src/storage/BucketStorage.ts | 2 ++ .../src/storage/mongo/MongoCompactor.ts | 20 +++++++++++++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/service-core/src/entry/commands/compact-action.ts b/packages/service-core/src/entry/commands/compact-action.ts index 128c54841..12327fbaa 100644 --- a/packages/service-core/src/entry/commands/compact-action.ts +++ b/packages/service-core/src/entry/commands/compact-action.ts @@ -21,12 +21,19 @@ const HEAP_LIMIT = v8.getHeapStatistics().heap_size_limit; const COMPACT_MEMORY_LIMIT_MB = Math.min(HEAP_LIMIT / 1024 / 1024 - 128, 1024); export function registerCompactAction(program: Command) { - const compactCommand = program.command(COMMAND_NAME); + const compactCommand = program + .command(COMMAND_NAME) + .option(`-b, --buckets [buckets]`, 'Bucket or bucket definition name (optional, comma-separate multiple names)'); wrapConfigCommand(compactCommand); return compactCommand.description('Compact storage').action(async (options) => { - logger.info('Compacting storage...'); + const buckets = options.buckets?.split(','); + if (buckets != null) { + logger.info('Compacting storage for all buckets...'); + } else { + logger.info(`Compacting storage for ${buckets.join(', ')}...`); + } const runnerConfig = extractRunnerOptions(options); const configuration = await utils.loadConfig(runnerConfig); logger.info('Successfully loaded configuration...'); @@ -46,7 +53,7 @@ export function registerCompactAction(program: Command) { } using p = bucketStorage.getInstance(active); logger.info('Performing compaction...'); - await p.compact({ memoryLimitMB: COMPACT_MEMORY_LIMIT_MB }); + await p.compact({ memoryLimitMB: COMPACT_MEMORY_LIMIT_MB, compactBuckets: buckets }); logger.info('Successfully compacted storage.'); } catch (e) { logger.error(`Failed to compact: ${e.toString()}`); diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 5c6b98fe3..96f16ad56 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -472,6 +472,8 @@ export interface CompactOptions { * If specified, compact only the specific buckets. * * If not specified, compacts all buckets. + * + * These can be individual bucket names, or bucket definition names. */ compactBuckets?: string[]; } diff --git a/packages/service-core/src/storage/mongo/MongoCompactor.ts b/packages/service-core/src/storage/mongo/MongoCompactor.ts index 07c946bae..102e1daf0 100644 --- a/packages/service-core/src/storage/mongo/MongoCompactor.ts +++ b/packages/service-core/src/storage/mongo/MongoCompactor.ts @@ -94,17 +94,33 @@ export class MongoCompactor { let currentState: CurrentBucketState | null = null; + let bucketLower: string | MinKey; + let bucketUpper: string | MaxKey; + + if (bucket == null) { + bucketLower = new MinKey(); + bucketUpper = new MaxKey(); + } else if (bucket.includes('[')) { + // Exact bucket name + bucketLower = bucket; + bucketUpper = bucket; + } else { + // Bucket definition name + bucketLower = `${bucket}[`; + bucketUpper = `${bucket}[\uFFFF`; + } + // Constant lower bound const lowerBound: BucketDataKey = { g: this.group_id, - b: bucket ?? (new MinKey() as any), + b: bucketLower as string, o: new MinKey() as any }; // Upper bound is adjusted for each batch let upperBound: BucketDataKey = { g: this.group_id, - b: bucket ?? (new MaxKey() as any), + b: bucketUpper as string, o: new MaxKey() as any }; From c75f480e62679fcd6771c193c3518757358dfe00 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 6 Nov 2024 18:02:02 +0200 Subject: [PATCH 276/298] Add configuration for using mongodb postimages. --- .../src/api/MongoRouteAPIAdapter.ts | 3 +- .../src/replication/ChangeStream.ts | 124 +++++++++++++++--- .../src/replication/MongoRelation.ts | 3 +- .../src/replication/replication-utils.ts | 3 +- modules/module-mongodb/src/types/types.ts | 10 +- 5 files changed, 122 insertions(+), 21 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index b9b740e24..5acad6eb0 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -7,6 +7,7 @@ import { MongoManager } from '../replication/MongoManager.js'; import { constructAfterRecord, createCheckpoint } from '../replication/MongoRelation.js'; import * as types from '../types/types.js'; import { escapeRegExp } from '../utils.js'; +import { CHECKPOINTS_COLLECTION } from '../replication/replication-utils.js'; export class MongoRouteAPIAdapter implements api.RouteAPI { protected client: mongo.MongoClient; @@ -202,7 +203,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { let tables: service_types.TableSchema[] = []; for (let collection of collections) { - if (['_powersync_checkpoints'].includes(collection.name)) { + if ([CHECKPOINTS_COLLECTION].includes(collection.name)) { continue; } if (collection.name.startsWith('system.')) { diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 9e8a8acab..f7a7676b0 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -11,6 +11,7 @@ import { mongoLsnToTimestamp } from './MongoRelation.js'; import { escapeRegExp } from '../utils.js'; +import { CHECKPOINTS_COLLECTION } from './replication-utils.js'; export const ZERO_LSN = '0000000000000000'; @@ -70,7 +71,17 @@ export class ChangeStream { return this.abort_signal.aborted; } - async getQualifiedTableNames( + private get postImages() { + return this.connections.options.postImages; + } + + /** + * This resolves a pattern, persists the related metadata, and returns + * the resulting SourceTables. + * + * This implicitly checks the collection postImage configuration. + */ + async resolveQualifiedTableNames( batch: storage.BucketStorageBatch, tablePattern: TablePattern ): Promise { @@ -94,10 +105,14 @@ export class ChangeStream { { name: nameFilter }, - { nameOnly: true } + { nameOnly: false } ) .toArray(); + if (!tablePattern.isWildcard && collections.length == 0) { + logger.warn(`Collection ${schema}.${tablePattern.name} not found`); + } + for (let collection of collections) { const table = await this.handleRelation( batch, @@ -108,7 +123,7 @@ export class ChangeStream { replicationColumns: [{ name: '_id' }] } as SourceEntityDescriptor, // This is done as part of the initial setup - snapshot is handled elsewhere - { snapshot: false } + { snapshot: false, collectionInfo: collection } ); result.push(table); @@ -165,14 +180,20 @@ export class ChangeStream { await this.storage.startBatch( { zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName }, async (batch) => { + // Start by resolving all tables. + // This checks postImage configuration, and that should fail as + // earlier as possible. + let allSourceTables: SourceTable[] = []; for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, table, session); - await batch.markSnapshotDone([table], ZERO_LSN); + const tables = await this.resolveQualifiedTableNames(batch, tablePattern); + allSourceTables.push(...tables); + } - await touch(); - } + for (let table of allSourceTables) { + await this.snapshotTable(batch, table, session); + await batch.markSnapshotDone([table], ZERO_LSN); + + await touch(); } const snapshotTime = session.clusterTime?.clusterTime ?? startTime; @@ -193,10 +214,24 @@ export class ChangeStream { } } + private async setupCheckpointsCollection() { + const collection = await this.getCollectionInfo(this.defaultDb.databaseName, CHECKPOINTS_COLLECTION); + if (collection == null) { + await this.defaultDb.createCollection(CHECKPOINTS_COLLECTION, { + changeStreamPreAndPostImages: { enabled: true } + }); + } else if (this.postImages != 'updateLookup' && collection.options?.changeStreamPreAndPostImages?.enabled != true) { + await this.defaultDb.command({ + collMod: CHECKPOINTS_COLLECTION, + changeStreamPreAndPostImages: { enabled: true } + }); + } + } + private getSourceNamespaceFilters(): { $match: any; multipleDatabases: boolean } { const sourceTables = this.sync_rules.getSourceTables(); - let $inFilters: any[] = [{ db: this.defaultDb.databaseName, coll: '_powersync_checkpoints' }]; + let $inFilters: any[] = [{ db: this.defaultDb.databaseName, coll: CHECKPOINTS_COLLECTION }]; let $refilters: any[] = []; let multipleDatabases = false; for (let tablePattern of sourceTables) { @@ -278,14 +313,60 @@ export class ChangeStream { if (existing != null) { return existing; } - return this.handleRelation(batch, descriptor, { snapshot: false }); + + // Note: collection may have been dropped at this point, so we handle + // missing values. + const collection = await this.getCollectionInfo(descriptor.schema, descriptor.name); + + return this.handleRelation(batch, descriptor, { snapshot: false, collectionInfo: collection }); + } + + private async getCollectionInfo(db: string, name: string): Promise { + const collection = ( + await this.client + .db(db) + .listCollections( + { + name: name + }, + { nameOnly: false } + ) + .toArray() + )[0]; + return collection; + } + + private async checkPostImages(db: string, collectionInfo: mongo.CollectionInfo) { + if (this.postImages == 'updateLookup') { + // Nothing to check + return; + } + + const enabled = collectionInfo.options?.changeStreamPreAndPostImages?.enabled == true; + + if (!enabled && this.postImages == 'autoConfigure') { + await this.client.db(db).command({ + collMod: collectionInfo.name, + changeStreamPreAndPostImages: { enabled: true } + }); + logger.info(`Enabled postImages on ${db}.${collectionInfo.name}`); + } else if (!enabled) { + throw new Error(`postImages not enabled on ${db}.${collectionInfo.name}`); + } } async handleRelation( batch: storage.BucketStorageBatch, descriptor: SourceEntityDescriptor, - options: { snapshot: boolean } + options: { snapshot: boolean; collectionInfo: mongo.CollectionInfo | undefined } ) { + if (options.collectionInfo != null) { + await this.checkPostImages(descriptor.schema, options.collectionInfo); + } else { + // If collectionInfo is null, the collection may have been dropped. + // Ignore the postImages check in this case. + } + const snapshot = options.snapshot; if (!descriptor.objectId && typeof descriptor.objectId != 'string') { throw new Error('objectId expected'); @@ -388,6 +469,7 @@ export class ChangeStream { async initReplication() { const result = await this.initSlot(); + await this.setupCheckpointsCollection(); if (result.needsInitialSync) { await this.startInitialReplication(); } @@ -412,12 +494,23 @@ export class ChangeStream { } ]; + let fullDocument: 'required' | 'updateLookup'; + + if (this.connections.options.postImages == 'updateLookup') { + fullDocument = 'updateLookup'; + } else { + // 'on' or 'autoConfigure' + // Configuration happens during snapshot + fullDocument = 'required'; + } + console.log({ fullDocument }); + const streamOptions: mongo.ChangeStreamOptions = { startAtOperationTime: startAfter, showExpandedEvents: true, useBigInt64: true, maxAwaitTimeMS: 200, - fullDocument: 'updateLookup' + fullDocument: fullDocument }; let stream: mongo.ChangeStream; if (filters.multipleDatabases) { @@ -461,7 +554,7 @@ export class ChangeStream { (changeDocument.operationType == 'insert' || changeDocument.operationType == 'update' || changeDocument.operationType == 'replace') && - changeDocument.ns.coll == '_powersync_checkpoints' + changeDocument.ns.coll == CHECKPOINTS_COLLECTION ) { const lsn = getMongoLsn(changeDocument.clusterTime!); if (waitForCheckpointLsn != null && lsn >= waitForCheckpointLsn) { @@ -499,7 +592,8 @@ export class ChangeStream { this.relation_cache.delete(tableFrom.objectId); } // Here we do need to snapshot the new table - await this.handleRelation(batch, relTo, { snapshot: true }); + const collection = await this.getCollectionInfo(relTo.schema, relTo.name); + await this.handleRelation(batch, relTo, { snapshot: true, collectionInfo: collection }); } } }); diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index 1a418a6ae..b027b4e98 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -2,6 +2,7 @@ import { storage } from '@powersync/service-core'; import { SqliteRow, SqliteValue, toSyncRulesRow } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; import { JSONBig, JsonContainer } from '@powersync/service-jsonbig'; +import { CHECKPOINTS_COLLECTION } from './replication-utils.js'; export function getMongoRelation(source: mongo.ChangeStreamNameSpace): storage.SourceEntityDescriptor { return { @@ -145,7 +146,7 @@ function filterJsonData(data: any, depth = 0): any { export async function createCheckpoint(client: mongo.MongoClient, db: mongo.Db): Promise { const session = client.startSession(); try { - const result = await db.collection('_powersync_checkpoints').findOneAndUpdate( + const result = await db.collection(CHECKPOINTS_COLLECTION).findOneAndUpdate( { _id: 'checkpoint' as any }, diff --git a/modules/module-mongodb/src/replication/replication-utils.ts b/modules/module-mongodb/src/replication/replication-utils.ts index 71a521d77..5370fdd5e 100644 --- a/modules/module-mongodb/src/replication/replication-utils.ts +++ b/modules/module-mongodb/src/replication/replication-utils.ts @@ -1,6 +1,7 @@ -import * as mongo from 'mongodb'; import { MongoManager } from './MongoManager.js'; +export const CHECKPOINTS_COLLECTION = '_powersync_checkpoints'; + export async function checkSourceConfiguration(connectionManager: MongoManager): Promise { const db = connectionManager.db; const hello = await db.command({ hello: 1 }); diff --git a/modules/module-mongodb/src/types/types.ts b/modules/module-mongodb/src/types/types.ts index ac7873876..e53c0419e 100644 --- a/modules/module-mongodb/src/types/types.ts +++ b/modules/module-mongodb/src/types/types.ts @@ -13,6 +13,8 @@ export interface NormalizedMongoConnectionConfig { username?: string; password?: string; + + postImages: 'on' | 'autoConfigure' | 'updateLookup'; } export const MongoConnectionConfig = service_types.configFile.DataSourceConfig.and( @@ -25,7 +27,9 @@ export const MongoConnectionConfig = service_types.configFile.DataSourceConfig.a uri: t.string, username: t.string.optional(), password: t.string.optional(), - database: t.string.optional() + database: t.string.optional(), + + postImages: t.literal('on').or(t.literal('autoConfigure')).or(t.literal('updateLookup')).optional() }) ); @@ -48,10 +52,10 @@ export function normalizeConnectionConfig(options: MongoConnectionConfig): Norma const base = normalizeMongoConfig(options); return { + ...base, id: options.id ?? 'default', tag: options.tag ?? 'default', - - ...base + postImages: options.postImages ?? 'updateLookup' }; } From e4c60cdff4d229b9d7263772bc6eff3e771cef62 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 7 Nov 2024 12:29:33 +0200 Subject: [PATCH 277/298] Increase timeout between writes in tests. --- .../test/src/change_stream.test.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 2b5c949d4..06d72ce96 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -16,13 +16,9 @@ bucket_definitions: - SELECT _id as id, description FROM "test_data" `; -describe( - 'change stream - mongodb', - function () { - defineChangeStreamTests(MONGO_STORAGE_FACTORY); - }, - { timeout: 20_000 } -); +describe('change stream - mongodb', { timeout: 20_000 }, function () { + defineChangeStreamTests(MONGO_STORAGE_FACTORY); +}); function defineChangeStreamTests(factory: StorageFactory) { test( @@ -46,11 +42,11 @@ bucket_definitions: const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); const test_id = result.insertedId; - await setTimeout(10); + await setTimeout(30); await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); - await setTimeout(10); + await setTimeout(30); await collection.replaceOne({ _id: test_id }, { description: 'test3' }); - await setTimeout(10); + await setTimeout(30); await collection.deleteOne({ _id: test_id }); const data = await context.getBucketData('global[]'); @@ -213,7 +209,6 @@ bucket_definitions: await context.replicateSnapshot(); context.startStreaming(); - console.log('insert1', db.databaseName); const collection = db.collection('test_data1'); const result = await collection.insertOne({ description: 'test1' }); const test_id = result.insertedId.toHexString(); From 9c3e7d6432705d03437e674aeb653c62da1ee8dc Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 7 Nov 2024 12:40:06 +0200 Subject: [PATCH 278/298] Use "await using" to simplify tests. --- .../test/src/change_stream.test.ts | 406 +++++++++--------- .../test/src/change_stream_utils.ts | 42 +- 2 files changed, 212 insertions(+), 236 deletions(-) diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 06d72ce96..dd96b9126 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -3,7 +3,7 @@ import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; import { BucketStorageFactory } from '@powersync/service-core'; import * as crypto from 'crypto'; import { describe, expect, test } from 'vitest'; -import { changeStreamTest } from './change_stream_utils.js'; +import { ChangeStreamTestContext } from './change_stream_utils.js'; import * as mongo from 'mongodb'; import { setTimeout } from 'node:timers/promises'; @@ -21,152 +21,143 @@ describe('change stream - mongodb', { timeout: 20_000 }, function () { }); function defineChangeStreamTests(factory: StorageFactory) { - test( - 'replicating basic values', - changeStreamTest(factory, async (context) => { - const { db } = context; - await context.updateSyncRules(` + test('replicating basic values', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(` bucket_definitions: global: data: - SELECT _id as id, description, num FROM "test_data"`); - db.createCollection('test_data', { - changeStreamPreAndPostImages: { enabled: true } - }); - const collection = db.collection('test_data'); - - await context.replicateSnapshot(); - - context.startStreaming(); - - const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); - const test_id = result.insertedId; - await setTimeout(30); - await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); - await setTimeout(30); - await collection.replaceOne({ _id: test_id }, { description: 'test3' }); - await setTimeout(30); - await collection.deleteOne({ _id: test_id }); - - const data = await context.getBucketData('global[]'); - - expect(data).toMatchObject([ - putOp('test_data', { id: test_id.toHexString(), description: 'test1', num: 1152921504606846976n }), - putOp('test_data', { id: test_id.toHexString(), description: 'test2', num: 1152921504606846976n }), - putOp('test_data', { id: test_id.toHexString(), description: 'test3' }), - removeOp('test_data', test_id.toHexString()) - ]); - }) - ); - - test( - 'no fullDocument available', - changeStreamTest(factory, async (context) => { - const { db, client } = context; - await context.updateSyncRules(` + db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: true } + }); + const collection = db.collection('test_data'); + + await context.replicateSnapshot(); + + context.startStreaming(); + + const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); + const test_id = result.insertedId; + await setTimeout(30); + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); + await setTimeout(30); + await collection.replaceOne({ _id: test_id }, { description: 'test3' }); + await setTimeout(30); + await collection.deleteOne({ _id: test_id }); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id.toHexString(), description: 'test1', num: 1152921504606846976n }), + putOp('test_data', { id: test_id.toHexString(), description: 'test2', num: 1152921504606846976n }), + putOp('test_data', { id: test_id.toHexString(), description: 'test3' }), + removeOp('test_data', test_id.toHexString()) + ]); + }); + + test('no fullDocument available', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db, client } = context; + await context.updateSyncRules(` bucket_definitions: global: data: - SELECT _id as id, description, num FROM "test_data"`); - db.createCollection('test_data', { - changeStreamPreAndPostImages: { enabled: false } + db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: false } + }); + const collection = db.collection('test_data'); + + await context.replicateSnapshot(); + + context.startStreaming(); + + const session = client.startSession(); + let test_id: mongo.ObjectId | undefined; + try { + await session.withTransaction(async () => { + const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }, { session }); + test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }, { session }); + await collection.replaceOne({ _id: test_id }, { description: 'test3' }, { session }); + await collection.deleteOne({ _id: test_id }, { session }); }); - const collection = db.collection('test_data'); - - await context.replicateSnapshot(); - - context.startStreaming(); - - const session = client.startSession(); - let test_id: mongo.ObjectId | undefined; - try { - await session.withTransaction(async () => { - const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }, { session }); - test_id = result.insertedId; - await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }, { session }); - await collection.replaceOne({ _id: test_id }, { description: 'test3' }, { session }); - await collection.deleteOne({ _id: test_id }, { session }); - }); - } finally { - await session.endSession(); - } - - const data = await context.getBucketData('global[]'); - - expect(data).toMatchObject([ - putOp('test_data', { id: test_id!.toHexString(), description: 'test1', num: 1152921504606846976n }), - // fullDocument is not available at the point this is replicated, resulting in it treated as a remove - removeOp('test_data', test_id!.toHexString()), - putOp('test_data', { id: test_id!.toHexString(), description: 'test3' }), - removeOp('test_data', test_id!.toHexString()) - ]); - }) - ); - - test( - 'replicating case sensitive table', - changeStreamTest(factory, async (context) => { - const { db } = context; - await context.updateSyncRules(` + } finally { + await session.endSession(); + } + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id!.toHexString(), description: 'test1', num: 1152921504606846976n }), + // fullDocument is not available at the point this is replicated, resulting in it treated as a remove + removeOp('test_data', test_id!.toHexString()), + putOp('test_data', { id: test_id!.toHexString(), description: 'test3' }), + removeOp('test_data', test_id!.toHexString()) + ]); + }); + + test('replicating case sensitive table', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(` bucket_definitions: global: data: - SELECT _id as id, description FROM "test_DATA" `); - await context.replicateSnapshot(); + await context.replicateSnapshot(); - context.startStreaming(); + context.startStreaming(); - const collection = db.collection('test_DATA'); - const result = await collection.insertOne({ description: 'test1' }); - const test_id = result.insertedId.toHexString(); + const collection = db.collection('test_DATA'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); - const data = await context.getBucketData('global[]'); + const data = await context.getBucketData('global[]'); - expect(data).toMatchObject([putOp('test_DATA', { id: test_id, description: 'test1' })]); - }) - ); + expect(data).toMatchObject([putOp('test_DATA', { id: test_id, description: 'test1' })]); + }); - test( - 'replicating large values', - changeStreamTest(factory, async (context) => { - const { db } = context; - await context.updateSyncRules(` + test('replicating large values', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(` bucket_definitions: global: data: - SELECT _id as id, name, description FROM "test_data" `); - await context.replicateSnapshot(); - context.startStreaming(); + await context.replicateSnapshot(); + context.startStreaming(); - const largeDescription = crypto.randomBytes(20_000).toString('hex'); + const largeDescription = crypto.randomBytes(20_000).toString('hex'); - const collection = db.collection('test_data'); - const result = await collection.insertOne({ name: 'test1', description: largeDescription }); - const test_id = result.insertedId; + const collection = db.collection('test_data'); + const result = await collection.insertOne({ name: 'test1', description: largeDescription }); + const test_id = result.insertedId; - await collection.updateOne({ _id: test_id }, { $set: { name: 'test2' } }); + await collection.updateOne({ _id: test_id }, { $set: { name: 'test2' } }); - const data = await context.getBucketData('global[]'); - expect(data.slice(0, 1)).toMatchObject([ - putOp('test_data', { id: test_id.toHexString(), name: 'test1', description: largeDescription }) - ]); - expect(data.slice(1)).toMatchObject([ - putOp('test_data', { id: test_id.toHexString(), name: 'test2', description: largeDescription }) - ]); - }) - ); + const data = await context.getBucketData('global[]'); + expect(data.slice(0, 1)).toMatchObject([ + putOp('test_data', { id: test_id.toHexString(), name: 'test1', description: largeDescription }) + ]); + expect(data.slice(1)).toMatchObject([ + putOp('test_data', { id: test_id.toHexString(), name: 'test2', description: largeDescription }) + ]); + }); - test( - 'replicating dropCollection', - changeStreamTest(factory, async (context) => { - const { db } = context; - const syncRuleContent = ` + test('replicating dropCollection', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + const syncRuleContent = ` bucket_definitions: global: data: @@ -175,127 +166,118 @@ bucket_definitions: parameters: SELECT _id as id FROM test_data WHERE id = token_parameters.user_id data: [] `; - await context.updateSyncRules(syncRuleContent); - await context.replicateSnapshot(); - context.startStreaming(); - - const collection = db.collection('test_data'); - const result = await collection.insertOne({ description: 'test1' }); - const test_id = result.insertedId.toHexString(); - - await collection.drop(); - - const data = await context.getBucketData('global[]'); - - expect(data).toMatchObject([ - putOp('test_data', { id: test_id, description: 'test1' }), - removeOp('test_data', test_id) - ]); - }) - ); - - test( - 'replicating renameCollection', - changeStreamTest(factory, async (context) => { - const { db } = context; - const syncRuleContent = ` + await context.updateSyncRules(syncRuleContent); + await context.replicateSnapshot(); + context.startStreaming(); + + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); + + await collection.drop(); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id, description: 'test1' }), + removeOp('test_data', test_id) + ]); + }); + + test('replicating renameCollection', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + const syncRuleContent = ` bucket_definitions: global: data: - SELECT _id as id, description FROM "test_data1" - SELECT _id as id, description FROM "test_data2" `; - await context.updateSyncRules(syncRuleContent); - await context.replicateSnapshot(); - context.startStreaming(); + await context.updateSyncRules(syncRuleContent); + await context.replicateSnapshot(); + context.startStreaming(); - const collection = db.collection('test_data1'); - const result = await collection.insertOne({ description: 'test1' }); - const test_id = result.insertedId.toHexString(); + const collection = db.collection('test_data1'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); - await collection.rename('test_data2'); + await collection.rename('test_data2'); - const data = await context.getBucketData('global[]'); + const data = await context.getBucketData('global[]'); - expect(data).toMatchObject([ - putOp('test_data1', { id: test_id, description: 'test1' }), - removeOp('test_data1', test_id), - putOp('test_data2', { id: test_id, description: 'test1' }) - ]); - }) - ); + expect(data).toMatchObject([ + putOp('test_data1', { id: test_id, description: 'test1' }), + removeOp('test_data1', test_id), + putOp('test_data2', { id: test_id, description: 'test1' }) + ]); + }); - test( - 'initial sync', - changeStreamTest(factory, async (context) => { - const { db } = context; - await context.updateSyncRules(BASIC_SYNC_RULES); + test('initial sync', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); - const collection = db.collection('test_data'); - const result = await collection.insertOne({ description: 'test1' }); - const test_id = result.insertedId.toHexString(); + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId.toHexString(); - await context.replicateSnapshot(); - context.startStreaming(); + await context.replicateSnapshot(); + context.startStreaming(); - const data = await context.getBucketData('global[]'); - expect(data).toMatchObject([putOp('test_data', { id: test_id, description: 'test1' })]); - }) - ); + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([putOp('test_data', { id: test_id, description: 'test1' })]); + }); // Not correctly implemented yet - test.skip( - 'large record', - changeStreamTest(factory, async (context) => { - await context.updateSyncRules(`bucket_definitions: + test.skip('large record', async () => { + await using context = await ChangeStreamTestContext.open(factory); + await context.updateSyncRules(`bucket_definitions: global: data: - SELECT _id as id, description, other FROM "test_data"`); - const { db } = context; - - await context.replicateSnapshot(); - - // 16MB - const largeDescription = crypto.randomBytes(8_000_000 - 100).toString('hex'); - - const collection = db.collection('test_data'); - const result = await collection.insertOne({ description: largeDescription }); - const test_id = result.insertedId; - - await collection.updateOne({ _id: test_id }, { $set: { name: 't2' } }); - context.startStreaming(); - - const data = await context.getBucketData('global[]'); - expect(data.length).toEqual(2); - const row = JSON.parse(data[0].data as string); - delete row.description; - expect(row).toEqual({ id: test_id.toHexString() }); - delete data[0].data; - expect(data[0]).toMatchObject({ - object_id: test_id.toHexString(), - object_type: 'test_data', - op: 'PUT', - op_id: '1' - }); - }) - ); + const { db } = context; + + await context.replicateSnapshot(); + + // 16MB + const largeDescription = crypto.randomBytes(8_000_000 - 100).toString('hex'); + + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: largeDescription }); + const test_id = result.insertedId; + + await collection.updateOne({ _id: test_id }, { $set: { name: 't2' } }); + context.startStreaming(); + + const data = await context.getBucketData('global[]'); + expect(data.length).toEqual(2); + const row = JSON.parse(data[0].data as string); + delete row.description; + expect(row).toEqual({ id: test_id.toHexString() }); + delete data[0].data; + expect(data[0]).toMatchObject({ + object_id: test_id.toHexString(), + object_type: 'test_data', + op: 'PUT', + op_id: '1' + }); + }); - test( - 'table not in sync rules', - changeStreamTest(factory, async (context) => { - const { db } = context; - await context.updateSyncRules(BASIC_SYNC_RULES); + test('table not in sync rules', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(BASIC_SYNC_RULES); - await context.replicateSnapshot(); + await context.replicateSnapshot(); - context.startStreaming(); + context.startStreaming(); - const collection = db.collection('test_donotsync'); - const result = await collection.insertOne({ description: 'test' }); + const collection = db.collection('test_donotsync'); + const result = await collection.insertOne({ description: 'test' }); - const data = await context.getBucketData('global[]'); + const data = await context.getBucketData('global[]'); - expect(data).toMatchObject([]); - }) - ); + expect(data).toMatchObject([]); + }); } diff --git a/modules/module-mongodb/test/src/change_stream_utils.ts b/modules/module-mongodb/test/src/change_stream_utils.ts index e533c56ca..2739d1939 100644 --- a/modules/module-mongodb/test/src/change_stream_utils.ts +++ b/modules/module-mongodb/test/src/change_stream_utils.ts @@ -7,36 +7,26 @@ import { ChangeStream, ChangeStreamOptions } from '@module/replication/ChangeStr import * as mongo from 'mongodb'; import { createCheckpoint } from '@module/replication/MongoRelation.js'; -/** - * Tests operating on the mongo change stream need to configure the stream and manage asynchronous - * replication, which gets a little tricky. - * - * This wraps a test in a function that configures all the context, and tears it down afterwards. - */ -export function changeStreamTest( - factory: () => Promise, - test: (context: ChangeStreamTestContext) => Promise -): () => Promise { - return async () => { - const f = await factory(); - const connectionManager = new MongoManager(TEST_CONNECTION_OPTIONS); - - await clearTestDb(connectionManager.db); - const context = new ChangeStreamTestContext(f, connectionManager); - try { - await test(context); - } finally { - await context.dispose(); - } - }; -} - export class ChangeStreamTestContext { private _walStream?: ChangeStream; private abortController = new AbortController(); private streamPromise?: Promise; public storage?: SyncRulesBucketStorage; + /** + * Tests operating on the mongo change stream need to configure the stream and manage asynchronous + * replication, which gets a little tricky. + * + * This configures all the context, and tears it down afterwards. + */ + static async open(factory: () => Promise) { + const f = await factory(); + const connectionManager = new MongoManager(TEST_CONNECTION_OPTIONS); + + await clearTestDb(connectionManager.db); + return new ChangeStreamTestContext(f, connectionManager); + } + constructor( public factory: BucketStorageFactory, public connectionManager: MongoManager @@ -48,6 +38,10 @@ export class ChangeStreamTestContext { await this.connectionManager.destroy(); } + async [Symbol.asyncDispose]() { + await this.dispose(); + } + get client() { return this.connectionManager.client; } From a6cd6f90a55a15432793f0527ab1402a77ffedfd Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 7 Nov 2024 12:50:16 +0200 Subject: [PATCH 279/298] Add postImage tests. --- .../test/src/change_stream.test.ts | 97 ++++++++++++++++++- .../test/src/change_stream_utils.ts | 5 +- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index dd96b9126..f32e227e6 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -2,7 +2,7 @@ import { putOp, removeOp } from '@core-tests/stream_utils.js'; import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; import { BucketStorageFactory } from '@powersync/service-core'; import * as crypto from 'crypto'; -import { describe, expect, test } from 'vitest'; +import { afterEach, beforeEach, describe, expect, test } from 'vitest'; import { ChangeStreamTestContext } from './change_stream_utils.js'; import * as mongo from 'mongodb'; import { setTimeout } from 'node:timers/promises'; @@ -58,8 +58,8 @@ bucket_definitions: ]); }); - test('no fullDocument available', async () => { - await using context = await ChangeStreamTestContext.open(factory); + test('updateLookup - no fullDocument available', async () => { + await using context = await ChangeStreamTestContext.open(factory, { postImages: 'updateLookup' }); const { db, client } = context; await context.updateSyncRules(` bucket_definitions: @@ -101,6 +101,97 @@ bucket_definitions: ]); }); + test('postImages - autoConfigure', async () => { + // Similar to the above test, but with postImages enabled. + // This resolves the consistency issue. + await using context = await ChangeStreamTestContext.open(factory, { postImages: 'autoConfigure' }); + const { db, client } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_data"`); + + db.createCollection('test_data', { + // enabled: false here, but autoConfigure will enable it. + changeStreamPreAndPostImages: { enabled: false } + }); + const collection = db.collection('test_data'); + + await context.replicateSnapshot(); + + context.startStreaming(); + + const session = client.startSession(); + let test_id: mongo.ObjectId | undefined; + try { + await session.withTransaction(async () => { + const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }, { session }); + test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }, { session }); + await collection.replaceOne({ _id: test_id }, { description: 'test3' }, { session }); + await collection.deleteOne({ _id: test_id }, { session }); + }); + } finally { + await session.endSession(); + } + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id!.toHexString(), description: 'test1', num: 1152921504606846976n }), + // The postImage helps us get this data + putOp('test_data', { id: test_id!.toHexString(), description: 'test2', num: 1152921504606846976n }), + putOp('test_data', { id: test_id!.toHexString(), description: 'test3' }), + removeOp('test_data', test_id!.toHexString()) + ]); + }); + + test('postImages - on', async () => { + // Similar to postImages - autoConfigure, but does not auto-configure. + // changeStreamPreAndPostImages must be manually configured. + await using context = await ChangeStreamTestContext.open(factory, { postImages: 'on' }); + const { db, client } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_data"`); + + db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: true } + }); + const collection = db.collection('test_data'); + + await context.replicateSnapshot(); + + context.startStreaming(); + + const session = client.startSession(); + let test_id: mongo.ObjectId | undefined; + try { + await session.withTransaction(async () => { + const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }, { session }); + test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }, { session }); + await collection.replaceOne({ _id: test_id }, { description: 'test3' }, { session }); + await collection.deleteOne({ _id: test_id }, { session }); + }); + } finally { + await session.endSession(); + } + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id!.toHexString(), description: 'test1', num: 1152921504606846976n }), + // The postImage helps us get this data + putOp('test_data', { id: test_id!.toHexString(), description: 'test2', num: 1152921504606846976n }), + putOp('test_data', { id: test_id!.toHexString(), description: 'test3' }), + removeOp('test_data', test_id!.toHexString()) + ]); + }); + test('replicating case sensitive table', async () => { await using context = await ChangeStreamTestContext.open(factory); const { db } = context; diff --git a/modules/module-mongodb/test/src/change_stream_utils.ts b/modules/module-mongodb/test/src/change_stream_utils.ts index 2739d1939..76735d380 100644 --- a/modules/module-mongodb/test/src/change_stream_utils.ts +++ b/modules/module-mongodb/test/src/change_stream_utils.ts @@ -6,6 +6,7 @@ import { MongoManager } from '@module/replication/MongoManager.js'; import { ChangeStream, ChangeStreamOptions } from '@module/replication/ChangeStream.js'; import * as mongo from 'mongodb'; import { createCheckpoint } from '@module/replication/MongoRelation.js'; +import { NormalizedMongoConnectionConfig } from '@module/types/types.js'; export class ChangeStreamTestContext { private _walStream?: ChangeStream; @@ -19,9 +20,9 @@ export class ChangeStreamTestContext { * * This configures all the context, and tears it down afterwards. */ - static async open(factory: () => Promise) { + static async open(factory: () => Promise, options?: Partial) { const f = await factory(); - const connectionManager = new MongoManager(TEST_CONNECTION_OPTIONS); + const connectionManager = new MongoManager({ ...TEST_CONNECTION_OPTIONS, ...options }); await clearTestDb(connectionManager.db); return new ChangeStreamTestContext(f, connectionManager); From 0b9d0e70d63561d48feea6bb7e51f7eb881ee707 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 7 Nov 2024 13:41:22 +0200 Subject: [PATCH 280/298] Test and fix wildcard collections. --- .../src/replication/ChangeStream.ts | 9 +++- .../test/src/change_stream.test.ts | 49 +++++++++++++++---- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index f7a7676b0..afdc33cad 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -244,7 +244,10 @@ export class ChangeStream { } if (tablePattern.isWildcard) { - $refilters.push({ db: tablePattern.schema, coll: new RegExp('^' + escapeRegExp(tablePattern.tablePrefix)) }); + $refilters.push({ + 'ns.db': tablePattern.schema, + 'ns.coll': new RegExp('^' + escapeRegExp(tablePattern.tablePrefix)) + }); } else { $inFilters.push({ db: tablePattern.schema, @@ -284,6 +287,8 @@ export class ChangeStream { throw new Error(`Aborted initial replication`); } + at += 1; + const record = constructAfterRecord(document); // This auto-flushes when the batch reaches its size limit @@ -303,6 +308,7 @@ export class ChangeStream { } await batch.flush(); + logger.info(`Replicated ${at} documents for ${table.qualifiedName}`); } private async getRelation( @@ -503,7 +509,6 @@ export class ChangeStream { // Configuration happens during snapshot fullDocument = 'required'; } - console.log({ fullDocument }); const streamOptions: mongo.ChangeStreamOptions = { startAtOperationTime: startAfter, diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index f32e227e6..63b96bf05 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -2,10 +2,10 @@ import { putOp, removeOp } from '@core-tests/stream_utils.js'; import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; import { BucketStorageFactory } from '@powersync/service-core'; import * as crypto from 'crypto'; -import { afterEach, beforeEach, describe, expect, test } from 'vitest'; -import { ChangeStreamTestContext } from './change_stream_utils.js'; import * as mongo from 'mongodb'; import { setTimeout } from 'node:timers/promises'; +import { describe, expect, test } from 'vitest'; +import { ChangeStreamTestContext } from './change_stream_utils.js'; type StorageFactory = () => Promise; @@ -30,8 +30,8 @@ bucket_definitions: data: - SELECT _id as id, description, num FROM "test_data"`); - db.createCollection('test_data', { - changeStreamPreAndPostImages: { enabled: true } + await db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: false } }); const collection = db.collection('test_data'); @@ -58,6 +58,38 @@ bucket_definitions: ]); }); + test('replicating wildcard', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_%"`); + + await db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: false } + }); + const collection = db.collection('test_data'); + + const result = await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); + const test_id = result.insertedId; + + await context.replicateSnapshot(); + + context.startStreaming(); + + await setTimeout(30); + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { id: test_id.toHexString(), description: 'test1', num: 1152921504606846976n }), + putOp('test_data', { id: test_id.toHexString(), description: 'test2', num: 1152921504606846976n }) + ]); + }); + test('updateLookup - no fullDocument available', async () => { await using context = await ChangeStreamTestContext.open(factory, { postImages: 'updateLookup' }); const { db, client } = context; @@ -67,13 +99,12 @@ bucket_definitions: data: - SELECT _id as id, description, num FROM "test_data"`); - db.createCollection('test_data', { + await db.createCollection('test_data', { changeStreamPreAndPostImages: { enabled: false } }); const collection = db.collection('test_data'); await context.replicateSnapshot(); - context.startStreaming(); const session = client.startSession(); @@ -112,7 +143,7 @@ bucket_definitions: data: - SELECT _id as id, description, num FROM "test_data"`); - db.createCollection('test_data', { + await db.createCollection('test_data', { // enabled: false here, but autoConfigure will enable it. changeStreamPreAndPostImages: { enabled: false } }); @@ -158,7 +189,7 @@ bucket_definitions: data: - SELECT _id as id, description, num FROM "test_data"`); - db.createCollection('test_data', { + await db.createCollection('test_data', { changeStreamPreAndPostImages: { enabled: true } }); const collection = db.collection('test_data'); @@ -355,7 +386,7 @@ bucket_definitions: }); }); - test('table not in sync rules', async () => { + test('collection not in sync rules', async () => { await using context = await ChangeStreamTestContext.open(factory); const { db } = context; await context.updateSyncRules(BASIC_SYNC_RULES); From ef85649c328b3aea1a65ce35fc7f455623c40ae6 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 7 Nov 2024 14:41:18 +0200 Subject: [PATCH 281/298] Invalidate changestream if postImage is not available. --- .../src/replication/ChangeStream.ts | 25 +++++++- .../replication/ChangeStreamReplicationJob.ts | 10 ++-- .../test/src/change_stream.test.ts | 57 +++++++++++++++++++ 3 files changed, 87 insertions(+), 5 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index afdc33cad..ada11ac73 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -25,7 +25,15 @@ interface InitResult { needsInitialSync: boolean; } -export class MissingReplicationSlotError extends Error { +/** + * Thrown when the change stream is not valid anymore, and replication + * must be restarted. + * + * Possible reasons: + * * Some change stream documents do not have postImages. + * * startAfter/resumeToken is not valid anymore. + */ +export class ChangeStreamInvalidatedError extends Error { constructor(message: string) { super(message); } @@ -482,6 +490,21 @@ export class ChangeStream { } async streamChanges() { + try { + await this.streamChangesInternal(); + } catch (e) { + if ( + e instanceof mongo.MongoServerError && + e.codeName == 'NoMatchingDocument' && + e.errmsg?.includes('post-image was not found') + ) { + throw new ChangeStreamInvalidatedError(e.errmsg); + } + throw e; + } + } + + async streamChangesInternal() { // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); diff --git a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts index 06583d571..78842fd36 100644 --- a/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts +++ b/modules/module-mongodb/src/replication/ChangeStreamReplicationJob.ts @@ -1,5 +1,5 @@ import { container } from '@powersync/lib-services-framework'; -import { MissingReplicationSlotError, ChangeStream } from './ChangeStream.js'; +import { ChangeStreamInvalidatedError, ChangeStream } from './ChangeStream.js'; import { replication } from '@powersync/service-core'; import { ConnectionManagerFactory } from './ConnectionManagerFactory.js'; @@ -40,7 +40,7 @@ export class ChangeStreamReplicationJob extends replication.AbstractReplicationJ }); this.logger.error(`Replication failed`, e); - if (e instanceof MissingReplicationSlotError) { + if (e instanceof ChangeStreamInvalidatedError) { // This stops replication on this slot, and creates a new slot await this.options.storage.factory.slotRemoved(this.slotName); } @@ -84,8 +84,10 @@ export class ChangeStreamReplicationJob extends replication.AbstractReplicationJ // Without this additional log, the cause may not be visible in the logs. this.logger.error(`cause`, e.cause); } - if (e instanceof mongo.MongoError && e.hasErrorLabel('NonResumableChangeStreamError')) { - throw new MissingReplicationSlotError(e.message); + if (e instanceof ChangeStreamInvalidatedError) { + throw e; + } else if (e instanceof mongo.MongoError && e.hasErrorLabel('NonResumableChangeStreamError')) { + throw new ChangeStreamInvalidatedError(e.message); } else { // Report the error if relevant, before retrying container.reporter.captureException(e, { diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 63b96bf05..5bb19f71e 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -402,4 +402,61 @@ bucket_definitions: expect(data).toMatchObject([]); }); + + test('postImages - new collection with postImages enabled', async () => { + await using context = await ChangeStreamTestContext.open(factory, { postImages: 'autoConfigure' }); + const { db } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description FROM "test_%"`); + + await context.replicateSnapshot(); + + await db.createCollection('test_data', { + // enabled: true here - everything should work + changeStreamPreAndPostImages: { enabled: true } + }); + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); + + context.startStreaming(); + + const data = await context.getBucketData('global[]'); + expect(data).toMatchObject([ + putOp('test_data', { id: test_id!.toHexString(), description: 'test1' }), + putOp('test_data', { id: test_id!.toHexString(), description: 'test2' }) + ]); + }); + + test.only('postImages - new collection with postImages disabled', async () => { + await using context = await ChangeStreamTestContext.open(factory, { postImages: 'autoConfigure' }); + const { db } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description FROM "test_data%"`); + + await context.replicateSnapshot(); + + await db.createCollection('test_data', { + // enabled: false here, but autoConfigure will enable it. + // Unfortunately, that is too late, and replication must be restarted. + changeStreamPreAndPostImages: { enabled: false } + }); + const collection = db.collection('test_data'); + const result = await collection.insertOne({ description: 'test1' }); + const test_id = result.insertedId; + await collection.updateOne({ _id: test_id }, { $set: { description: 'test2' } }); + + context.startStreaming(); + + await expect(() => context.getBucketData('global[]')).rejects.toMatchObject({ + message: expect.stringContaining('stream was configured to require a post-image for all update events') + }); + }); } From 0206aaab0264362a4cff4c514b510836ade107b9 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 7 Nov 2024 14:52:43 +0200 Subject: [PATCH 282/298] Rename post_images config option. --- .../src/replication/ChangeStream.ts | 26 +++++++----- modules/module-mongodb/src/types/types.ts | 42 +++++++++++++++++-- .../test/src/change_stream.test.ts | 13 +++--- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index ada11ac73..250217c95 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -12,6 +12,7 @@ import { } from './MongoRelation.js'; import { escapeRegExp } from '../utils.js'; import { CHECKPOINTS_COLLECTION } from './replication-utils.js'; +import { PostImagesOption } from '../types/types.js'; export const ZERO_LSN = '0000000000000000'; @@ -79,8 +80,12 @@ export class ChangeStream { return this.abort_signal.aborted; } - private get postImages() { - return this.connections.options.postImages; + private get usePostImages() { + return this.connections.options.postImages != PostImagesOption.OFF; + } + + private get configurePostImages() { + return this.connections.options.postImages == PostImagesOption.AUTO_CONFIGURE; } /** @@ -228,7 +233,7 @@ export class ChangeStream { await this.defaultDb.createCollection(CHECKPOINTS_COLLECTION, { changeStreamPreAndPostImages: { enabled: true } }); - } else if (this.postImages != 'updateLookup' && collection.options?.changeStreamPreAndPostImages?.enabled != true) { + } else if (this.usePostImages && collection.options?.changeStreamPreAndPostImages?.enabled != true) { await this.defaultDb.command({ collMod: CHECKPOINTS_COLLECTION, changeStreamPreAndPostImages: { enabled: true } @@ -351,14 +356,14 @@ export class ChangeStream { } private async checkPostImages(db: string, collectionInfo: mongo.CollectionInfo) { - if (this.postImages == 'updateLookup') { + if (!this.usePostImages) { // Nothing to check return; } const enabled = collectionInfo.options?.changeStreamPreAndPostImages?.enabled == true; - if (!enabled && this.postImages == 'autoConfigure') { + if (!enabled && this.configurePostImages) { await this.client.db(db).command({ collMod: collectionInfo.name, changeStreamPreAndPostImages: { enabled: true } @@ -525,12 +530,13 @@ export class ChangeStream { let fullDocument: 'required' | 'updateLookup'; - if (this.connections.options.postImages == 'updateLookup') { - fullDocument = 'updateLookup'; - } else { - // 'on' or 'autoConfigure' - // Configuration happens during snapshot + if (this.usePostImages) { + // 'read_only' or 'auto_configure' + // Configuration happens during snapshot, or when we see new + // collections. fullDocument = 'required'; + } else { + fullDocument = 'updateLookup'; } const streamOptions: mongo.ChangeStreamOptions = { diff --git a/modules/module-mongodb/src/types/types.ts b/modules/module-mongodb/src/types/types.ts index e53c0419e..1498193f5 100644 --- a/modules/module-mongodb/src/types/types.ts +++ b/modules/module-mongodb/src/types/types.ts @@ -4,6 +4,42 @@ import * as t from 'ts-codec'; export const MONGO_CONNECTION_TYPE = 'mongodb' as const; +export enum PostImagesOption { + /** + * Use fullDocument: updateLookup on the changeStream. + * + * This does not guarantee consistency - operations may + * arrive out of order, especially when there is replication lag. + * + * This is the default option for backwards-compatability. + */ + OFF = 'off', + + /** + * Use fullDocument: required on the changeStream. + * + * Collections are automatically configured with: + * `changeStreamPreAndPostImages: { enabled: true }` + * + * This is the recommended behavior for new instances. + */ + AUTO_CONFIGURE = 'auto_configure', + + /** + * + * Use fullDocument: required on the changeStream. + * + * Collections are not automatically configured. Each + * collection must be configured configured manually before + * replicating with: + * + * `changeStreamPreAndPostImages: { enabled: true }` + * + * Use when the collMod permission is not available. + */ + READ_ONLY = 'read_only' +} + export interface NormalizedMongoConnectionConfig { id: string; tag: string; @@ -14,7 +50,7 @@ export interface NormalizedMongoConnectionConfig { username?: string; password?: string; - postImages: 'on' | 'autoConfigure' | 'updateLookup'; + postImages: PostImagesOption; } export const MongoConnectionConfig = service_types.configFile.DataSourceConfig.and( @@ -29,7 +65,7 @@ export const MongoConnectionConfig = service_types.configFile.DataSourceConfig.a password: t.string.optional(), database: t.string.optional(), - postImages: t.literal('on').or(t.literal('autoConfigure')).or(t.literal('updateLookup')).optional() + post_images: t.literal('off').or(t.literal('auto_configure')).or(t.literal('read_only')).optional() }) ); @@ -55,7 +91,7 @@ export function normalizeConnectionConfig(options: MongoConnectionConfig): Norma ...base, id: options.id ?? 'default', tag: options.tag ?? 'default', - postImages: options.postImages ?? 'updateLookup' + postImages: (options.post_images as PostImagesOption | undefined) ?? PostImagesOption.OFF }; } diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index 5bb19f71e..bff0a6857 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -6,6 +6,7 @@ import * as mongo from 'mongodb'; import { setTimeout } from 'node:timers/promises'; import { describe, expect, test } from 'vitest'; import { ChangeStreamTestContext } from './change_stream_utils.js'; +import { PostImagesOption } from '@module/types/types.js'; type StorageFactory = () => Promise; @@ -91,7 +92,7 @@ bucket_definitions: }); test('updateLookup - no fullDocument available', async () => { - await using context = await ChangeStreamTestContext.open(factory, { postImages: 'updateLookup' }); + await using context = await ChangeStreamTestContext.open(factory, { postImages: PostImagesOption.OFF }); const { db, client } = context; await context.updateSyncRules(` bucket_definitions: @@ -135,7 +136,7 @@ bucket_definitions: test('postImages - autoConfigure', async () => { // Similar to the above test, but with postImages enabled. // This resolves the consistency issue. - await using context = await ChangeStreamTestContext.open(factory, { postImages: 'autoConfigure' }); + await using context = await ChangeStreamTestContext.open(factory, { postImages: PostImagesOption.AUTO_CONFIGURE }); const { db, client } = context; await context.updateSyncRules(` bucket_definitions: @@ -181,7 +182,7 @@ bucket_definitions: test('postImages - on', async () => { // Similar to postImages - autoConfigure, but does not auto-configure. // changeStreamPreAndPostImages must be manually configured. - await using context = await ChangeStreamTestContext.open(factory, { postImages: 'on' }); + await using context = await ChangeStreamTestContext.open(factory, { postImages: PostImagesOption.READ_ONLY }); const { db, client } = context; await context.updateSyncRules(` bucket_definitions: @@ -404,7 +405,7 @@ bucket_definitions: }); test('postImages - new collection with postImages enabled', async () => { - await using context = await ChangeStreamTestContext.open(factory, { postImages: 'autoConfigure' }); + await using context = await ChangeStreamTestContext.open(factory, { postImages: PostImagesOption.AUTO_CONFIGURE }); const { db } = context; await context.updateSyncRules(` bucket_definitions: @@ -432,8 +433,8 @@ bucket_definitions: ]); }); - test.only('postImages - new collection with postImages disabled', async () => { - await using context = await ChangeStreamTestContext.open(factory, { postImages: 'autoConfigure' }); + test('postImages - new collection with postImages disabled', async () => { + await using context = await ChangeStreamTestContext.open(factory, { postImages: PostImagesOption.AUTO_CONFIGURE }); const { db } = context; await context.updateSyncRules(` bucket_definitions: From 7ba4d1137c26f76475de3eea1d341edcda08ab82 Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Fri, 8 Nov 2024 09:02:19 +0200 Subject: [PATCH 283/298] Parse returned mysql schema result (#122) * Bugfix:Parse returned schema info as an object. Previously this was the default, but since a recent change to the mysql connection options, all JSON fields are now returned as strings. --- .changeset/healthy-rules-arrive.md | 5 +++++ .../src/api/MySQLRouteAPIAdapter.ts | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 .changeset/healthy-rules-arrive.md diff --git a/.changeset/healthy-rules-arrive.md b/.changeset/healthy-rules-arrive.md new file mode 100644 index 000000000..a64c34b1b --- /dev/null +++ b/.changeset/healthy-rules-arrive.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-module-mysql': patch +--- + +Fixed mysql schema json parsing diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index eb9c272af..dafcbf828 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -11,7 +11,7 @@ import { toExpressionTypeFromMySQLType } from '../common/common-index.js'; type SchemaResult = { schema_name: string; table_name: string; - columns: Array<{ data_type: string; column_name: string }>; + columns: string; }; export class MySQLRouteAPIAdapter implements api.RouteAPI { @@ -326,16 +326,18 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { name: result.schema_name, tables: [] }); + + const columns = JSON.parse(result.columns).map((column: { data_type: string; column_name: string }) => ({ + name: column.column_name, + type: column.data_type, + sqlite_type: toExpressionTypeFromMySQLType(column.data_type).typeFlags, + internal_type: column.data_type, + pg_type: column.data_type + })); schema.tables.push({ name: result.table_name, - columns: result.columns.map((column) => ({ - name: column.column_name, - type: column.data_type, - sqlite_type: toExpressionTypeFromMySQLType(column.data_type).typeFlags, - internal_type: column.data_type, - pg_type: column.data_type - })) + columns: columns }); return hash; From 3395de6bdf5f6bfb2fa079207d5604da12a1ced7 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Sat, 9 Nov 2024 15:57:25 +0200 Subject: [PATCH 284/298] More MongoDB type fixes and tests. --- .../src/replication/MongoRelation.ts | 6 +-- .../test/src/mongo_test.test.ts | 45 +++++++++++++++++-- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index 1a418a6ae..7670daec1 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -121,9 +121,9 @@ function filterJsonData(data: any, depth = 0): any { } else if (data instanceof mongo.Decimal128) { return data.toString(); } else if (data instanceof mongo.MinKey || data instanceof mongo.MaxKey) { - return data._bsontype; - } else if (data instanceof mongo.BSONRegExp) { - return JSON.stringify({ pattern: data.pattern, options: data.options }); + return null; + } else if (data instanceof RegExp) { + return { pattern: data.source, options: data.flags }; } else if (Array.isArray(data)) { return data.map((element) => filterJsonData(element, depth + 1)); } else if (ArrayBuffer.isView(data)) { diff --git a/modules/module-mongodb/test/src/mongo_test.test.ts b/modules/module-mongodb/test/src/mongo_test.test.ts index c3b2b15c5..16cca3be7 100644 --- a/modules/module-mongodb/test/src/mongo_test.test.ts +++ b/modules/module-mongodb/test/src/mongo_test.test.ts @@ -34,7 +34,18 @@ describe('mongo data types', () => { objectId: mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb'), regexp: new mongo.BSONRegExp('test', 'i'), minKey: new mongo.MinKey(), - maxKey: new mongo.MaxKey() + maxKey: new mongo.MaxKey(), + symbol: new mongo.BSONSymbol('test'), + js: new mongo.Code('testcode'), + js2: new mongo.Code('testcode', { foo: 'bar' }), + pointer: new mongo.DBRef('mycollection', mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')), + pointer2: new mongo.DBRef( + 'mycollection', + mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb'), + 'mydb', + { foo: 'bar' } + ), + undefined: undefined } ]); } @@ -62,7 +73,11 @@ describe('mongo data types', () => { objectId: [mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb')], regexp: [new mongo.BSONRegExp('test', 'i')], minKey: [new mongo.MinKey()], - maxKey: [new mongo.MaxKey()] + maxKey: [new mongo.MaxKey()], + symbol: [new mongo.BSONSymbol('test')], + js: [new mongo.Code('testcode')], + pointer: [new mongo.DBRef('mycollection', mongo.ObjectId.createFromHexString('66e834cc91d805df11fa0ecb'))], + undefined: [undefined] } ]); } @@ -97,7 +112,13 @@ describe('mongo data types', () => { timestamp: 1958505087099n, regexp: '{"pattern":"test","options":"i"}', minKey: null, - maxKey: null + maxKey: null, + symbol: 'test', + js: '{"code":"testcode","scope":null}', + js2: '{"code":"testcode","scope":{"foo":"bar"}}', + pointer: '{"collection":"mycollection","oid":"66e834cc91d805df11fa0ecb","fields":{}}', + pointer2: '{"collection":"mycollection","oid":"66e834cc91d805df11fa0ecb","db":"mydb","fields":{"foo":"bar"}}', + undefined: null }); } @@ -130,7 +151,14 @@ describe('mongo data types', () => { expect(transformed[3]).toMatchObject({ _id: 10n, objectId: '["66e834cc91d805df11fa0ecb"]', - timestamp: '[1958505087099]' + timestamp: '[1958505087099]', + regexp: '[{"pattern":"test","options":"i"}]', + symbol: '["test"]', + js: '[{"code":"testcode","scope":null}]', + pointer: '[{"collection":"mycollection","oid":"66e834cc91d805df11fa0ecb","fields":{}}]', + minKey: '[null]', + maxKey: '[null]', + undefined: '[null]' }); } @@ -245,14 +273,23 @@ describe('mongo data types', () => { { name: 'int2', sqlite_type: 4, internal_type: 'Integer' }, { name: 'int4', sqlite_type: 4, internal_type: 'Integer' }, { name: 'int8', sqlite_type: 4, internal_type: 'Long' }, + // We can fix these later + { name: 'js', sqlite_type: 2, internal_type: 'Object' }, + { name: 'js2', sqlite_type: 2, internal_type: 'Object' }, { name: 'maxKey', sqlite_type: 0, internal_type: 'MaxKey' }, { name: 'minKey', sqlite_type: 0, internal_type: 'MinKey' }, { name: 'nested', sqlite_type: 2, internal_type: 'Object' }, { name: 'null', sqlite_type: 0, internal_type: 'Null' }, { name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' }, + // We can fix these later + { name: 'pointer', sqlite_type: 2, internal_type: 'Object' }, + { name: 'pointer2', sqlite_type: 2, internal_type: 'Object' }, { name: 'regexp', sqlite_type: 2, internal_type: 'RegExp' }, + // Can fix this later + { name: 'symbol', sqlite_type: 2, internal_type: 'String' }, { name: 'text', sqlite_type: 2, internal_type: 'String' }, { name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' }, + { name: 'undefined', sqlite_type: 0, internal_type: 'Null' }, { name: 'uuid', sqlite_type: 2, internal_type: 'UUID' } ] } From c8799232b28aa7b58d955826a3a50e6b7d31323e Mon Sep 17 00:00:00 2001 From: Roland Teichert Date: Mon, 11 Nov 2024 14:13:17 +0200 Subject: [PATCH 285/298] Handle improperly formatted mysql version strings (#124) Handled MySQL version checks better and enabled tests for MySQL 5.7 --- .changeset/gentle-icons-try.md | 5 ++ .github/workflows/test.yml | 5 +- .../src/api/MySQLRouteAPIAdapter.ts | 4 +- .../module-mysql/src/common/ReplicatedGTID.ts | 2 +- .../src/common/check-source-configuration.ts | 19 +++-- .../src/common/get-replication-columns.ts | 2 +- .../src/common/read-executed-gtid.ts | 14 ++-- .../src/replication/BinLogStream.ts | 2 +- .../src/replication/MySQLConnectionManager.ts | 2 +- .../utils/{mysql_utils.ts => mysql-utils.ts} | 22 ++++++ .../test/src/BinLogStream.test.ts | 76 ++++++------------- ...ppings.test.ts => mysql-to-sqlite.test.ts} | 0 .../module-mysql/test/src/mysql-utils.test.ts | 17 +++++ modules/module-mysql/test/src/util.ts | 9 +-- 14 files changed, 94 insertions(+), 85 deletions(-) create mode 100644 .changeset/gentle-icons-try.md rename modules/module-mysql/src/utils/{mysql_utils.ts => mysql-utils.ts} (75%) rename modules/module-mysql/test/src/{MysqlTypeMappings.test.ts => mysql-to-sqlite.test.ts} (100%) create mode 100644 modules/module-mysql/test/src/mysql-utils.test.ts diff --git a/.changeset/gentle-icons-try.md b/.changeset/gentle-icons-try.md new file mode 100644 index 000000000..103672131 --- /dev/null +++ b/.changeset/gentle-icons-try.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-module-mysql': patch +--- + +Fixed MySQL version checking to better handle non-semantic version strings diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef8991994..9f5fbd055 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -152,7 +152,7 @@ jobs: strategy: fail-fast: false matrix: - mysql-version: [8.0, 8.4] + mysql-version: [5.7, 8.0, 8.4] steps: - uses: actions/checkout@v4 @@ -167,7 +167,8 @@ jobs: -d mysql:${{ matrix.mysql-version }} \ --log-bin=/var/lib/mysql/mysql-bin.log \ --gtid_mode=ON \ - --enforce_gtid_consistency=ON + --enforce_gtid_consistency=ON \ + --server-id=1 - name: Start MongoDB uses: supercharge/mongodb-github-action@1.8.0 diff --git a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts index dafcbf828..faa140adc 100644 --- a/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts +++ b/modules/module-mysql/src/api/MySQLRouteAPIAdapter.ts @@ -4,7 +4,7 @@ import * as sync_rules from '@powersync/service-sync-rules'; import * as service_types from '@powersync/service-types'; import mysql from 'mysql2/promise'; import * as common from '../common/common-index.js'; -import * as mysql_utils from '../utils/mysql_utils.js'; +import * as mysql_utils from '../utils/mysql-utils.js'; import * as types from '../types/types.js'; import { toExpressionTypeFromMySQLType } from '../common/common-index.js'; @@ -326,7 +326,7 @@ export class MySQLRouteAPIAdapter implements api.RouteAPI { name: result.schema_name, tables: [] }); - + const columns = JSON.parse(result.columns).map((column: { data_type: string; column_name: string }) => ({ name: column.column_name, type: column.data_type, diff --git a/modules/module-mysql/src/common/ReplicatedGTID.ts b/modules/module-mysql/src/common/ReplicatedGTID.ts index 7d28b0d3a..d51d43a73 100644 --- a/modules/module-mysql/src/common/ReplicatedGTID.ts +++ b/modules/module-mysql/src/common/ReplicatedGTID.ts @@ -1,6 +1,6 @@ import mysql from 'mysql2/promise'; import * as uuid from 'uuid'; -import * as mysql_utils from '../utils/mysql_utils.js'; +import * as mysql_utils from '../utils/mysql-utils.js'; export type BinLogPosition = { filename: string; diff --git a/modules/module-mysql/src/common/check-source-configuration.ts b/modules/module-mysql/src/common/check-source-configuration.ts index 6572a1066..6319fc3b7 100644 --- a/modules/module-mysql/src/common/check-source-configuration.ts +++ b/modules/module-mysql/src/common/check-source-configuration.ts @@ -1,8 +1,16 @@ import mysqlPromise from 'mysql2/promise'; -import * as mysql_utils from '../utils/mysql_utils.js'; +import * as mysql_utils from '../utils/mysql-utils.js'; + +const MIN_SUPPORTED_VERSION = '5.7.0'; export async function checkSourceConfiguration(connection: mysqlPromise.Connection): Promise { const errors: string[] = []; + + const version = await mysql_utils.getMySQLVersion(connection); + if (!mysql_utils.isVersionAtLeast(version, MIN_SUPPORTED_VERSION)) { + errors.push(`MySQL versions older than ${MIN_SUPPORTED_VERSION} are not supported. Your version is: ${version}.`); + } + const [[result]] = await mysql_utils.retriedQuery({ connection, query: ` @@ -48,12 +56,3 @@ export async function checkSourceConfiguration(connection: mysqlPromise.Connecti return errors; } - -export async function getMySQLVersion(connection: mysqlPromise.Connection): Promise { - const [[versionResult]] = await mysql_utils.retriedQuery({ - connection, - query: `SELECT VERSION() as version` - }); - - return versionResult.version as string; -} diff --git a/modules/module-mysql/src/common/get-replication-columns.ts b/modules/module-mysql/src/common/get-replication-columns.ts index 19d5ca059..fa0eb8fde 100644 --- a/modules/module-mysql/src/common/get-replication-columns.ts +++ b/modules/module-mysql/src/common/get-replication-columns.ts @@ -1,6 +1,6 @@ import { storage } from '@powersync/service-core'; import mysqlPromise from 'mysql2/promise'; -import * as mysql_utils from '../utils/mysql_utils.js'; +import * as mysql_utils from '../utils/mysql-utils.js'; export type GetReplicationColumnsOptions = { connection: mysqlPromise.Connection; diff --git a/modules/module-mysql/src/common/read-executed-gtid.ts b/modules/module-mysql/src/common/read-executed-gtid.ts index 801fba011..9f60c3362 100644 --- a/modules/module-mysql/src/common/read-executed-gtid.ts +++ b/modules/module-mysql/src/common/read-executed-gtid.ts @@ -1,26 +1,22 @@ import mysqlPromise from 'mysql2/promise'; -import * as mysql_utils from '../utils/mysql_utils.js'; -import { gte } from 'semver'; - +import * as mysql_utils from '../utils/mysql-utils.js'; import { ReplicatedGTID } from './ReplicatedGTID.js'; -import { getMySQLVersion } from './check-source-configuration.js'; /** * Gets the current master HEAD GTID */ export async function readExecutedGtid(connection: mysqlPromise.Connection): Promise { - const version = await getMySQLVersion(connection); + const version = await mysql_utils.getMySQLVersion(connection); + let binlogStatus: mysqlPromise.RowDataPacket; - if (gte(version, '8.4.0')) { - // Get the BinLog status + if (mysql_utils.isVersionAtLeast(version, '8.4.0')) { + // Syntax for the below query changed in 8.4.0 const [[binLogResult]] = await mysql_utils.retriedQuery({ connection, query: `SHOW BINARY LOG STATUS` }); binlogStatus = binLogResult; } else { - // TODO Check if this works for version 5.7 - // Get the BinLog status const [[binLogResult]] = await mysql_utils.retriedQuery({ connection, query: `SHOW MASTER STATUS` diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index a7acb8f32..44e594f6d 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -11,7 +11,7 @@ import * as zongji_utils from './zongji/zongji-utils.js'; import { MySQLConnectionManager } from './MySQLConnectionManager.js'; import { isBinlogStillAvailable, ReplicatedGTID, toColumnDescriptors } from '../common/common-index.js'; import mysqlPromise from 'mysql2/promise'; -import { createRandomServerId } from '../utils/mysql_utils.js'; +import { createRandomServerId } from '../utils/mysql-utils.js'; export interface BinLogStreamOptions { connections: MySQLConnectionManager; diff --git a/modules/module-mysql/src/replication/MySQLConnectionManager.ts b/modules/module-mysql/src/replication/MySQLConnectionManager.ts index fd8cef605..3693b9ce2 100644 --- a/modules/module-mysql/src/replication/MySQLConnectionManager.ts +++ b/modules/module-mysql/src/replication/MySQLConnectionManager.ts @@ -1,7 +1,7 @@ import { NormalizedMySQLConnectionConfig } from '../types/types.js'; import mysqlPromise from 'mysql2/promise'; import mysql, { FieldPacket, RowDataPacket } from 'mysql2'; -import * as mysql_utils from '../utils/mysql_utils.js'; +import * as mysql_utils from '../utils/mysql-utils.js'; import ZongJi from '@powersync/mysql-zongji'; import { logger } from '@powersync/lib-services-framework'; diff --git a/modules/module-mysql/src/utils/mysql_utils.ts b/modules/module-mysql/src/utils/mysql-utils.ts similarity index 75% rename from modules/module-mysql/src/utils/mysql_utils.ts rename to modules/module-mysql/src/utils/mysql-utils.ts index 2e65d47c2..a2279c234 100644 --- a/modules/module-mysql/src/utils/mysql_utils.ts +++ b/modules/module-mysql/src/utils/mysql-utils.ts @@ -2,6 +2,7 @@ import { logger } from '@powersync/lib-services-framework'; import mysql from 'mysql2'; import mysqlPromise from 'mysql2/promise'; import * as types from '../types/types.js'; +import { coerce, gte } from 'semver'; export type RetriedQueryOptions = { connection: mysqlPromise.Connection; @@ -60,3 +61,24 @@ export function createPool(config: types.NormalizedMySQLConnectionConfig, option export function createRandomServerId(syncRuleId: number): number { return Number.parseInt(`${syncRuleId}00${Math.floor(Math.random() * 10000)}`); } + +export async function getMySQLVersion(connection: mysqlPromise.Connection): Promise { + const [[versionResult]] = await retriedQuery({ + connection, + query: `SELECT VERSION() as version` + }); + + return versionResult.version as string; +} + +/** + * Check if the current MySQL version is newer or equal to the target version. + * @param version + * @param minimumVersion + */ +export function isVersionAtLeast(version: string, minimumVersion: string): boolean { + const coercedVersion = coerce(version); + const coercedMinimumVersion = coerce(minimumVersion); + + return gte(coercedVersion!, coercedMinimumVersion!, { loose: true }); +} diff --git a/modules/module-mysql/test/src/BinLogStream.test.ts b/modules/module-mysql/test/src/BinLogStream.test.ts index 7aa19cdd4..44240d461 100644 --- a/modules/module-mysql/test/src/BinLogStream.test.ts +++ b/modules/module-mysql/test/src/BinLogStream.test.ts @@ -3,6 +3,7 @@ import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; import { BucketStorageFactory, Metrics } from '@powersync/service-core'; import { describe, expect, test } from 'vitest'; import { binlogStreamTest } from './BinlogStreamUtils.js'; +import { v4 as uuid } from 'uuid'; type StorageFactory = () => Promise; @@ -32,9 +33,7 @@ function defineBinlogStreamTests(factory: StorageFactory) { data: - SELECT id, description, num FROM "test_data"`); - await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, num BIGINT)` - ); + await connectionManager.query(`CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description TEXT, num BIGINT)`); await context.replicateSnapshot(); @@ -44,11 +43,10 @@ function defineBinlogStreamTests(factory: StorageFactory) { (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0; context.startStreaming(); - await connectionManager.query(`INSERT INTO test_data(description, num) VALUES('test1', 1152921504606846976)`); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test1' AND num = 1152921504606846976` + const testId = uuid(); + await connectionManager.query( + `INSERT INTO test_data(id, description, num) VALUES('${testId}', 'test1', 1152921504606846976)` ); - const testId = result.test_id; const data = await context.getBucketData('global[]'); expect(data).toMatchObject([putOp('test_data', { id: testId, description: 'test1', num: 1152921504606846976n })]); @@ -71,9 +69,7 @@ function defineBinlogStreamTests(factory: StorageFactory) { - SELECT id, description FROM "test_DATA" `); - await connectionManager.query( - `CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); + await connectionManager.query(`CREATE TABLE test_DATA (id CHAR(36) PRIMARY KEY, description text)`); await context.replicateSnapshot(); @@ -84,11 +80,8 @@ function defineBinlogStreamTests(factory: StorageFactory) { context.startStreaming(); - await connectionManager.query(`INSERT INTO test_DATA(description) VALUES('test1')`); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_DATA WHERE description = 'test1'` - ); - const testId = result.test_id; + const testId = uuid(); + await connectionManager.query(`INSERT INTO test_DATA(id, description) VALUES('${testId}','test1')`); const data = await context.getBucketData('global[]'); @@ -144,24 +137,18 @@ function defineBinlogStreamTests(factory: StorageFactory) { const { connectionManager } = context; await context.updateSyncRules(BASIC_SYNC_RULES); - await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); + await connectionManager.query(`CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description text)`); await context.replicateSnapshot(); context.startStreaming(); - await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); - const [[result1]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test1'` - ); - const testId1 = result1.test_id; + const testId1 = uuid(); + await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId1}','test1')`); - await connectionManager.query(`UPDATE test_data SET id = UUID(), description = 'test2a' WHERE id = '${testId1}'`); - const [[result2]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test2a'` + const testId2 = uuid(); + await connectionManager.query( + `UPDATE test_data SET id = '${testId2}', description = 'test2a' WHERE id = '${testId1}'` ); - const testId2 = result2.test_id; // This update may fail replicating with: // Error: Update on missing record public.test_data:074a601e-fc78-4c33-a15d-f89fdd4af31d :: {"g":1,"t":"651e9fbe9fec6155895057ec","k":"1a0b34da-fb8c-5e6f-8421-d7a3c5d4df4f"} @@ -192,15 +179,10 @@ function defineBinlogStreamTests(factory: StorageFactory) { const { connectionManager } = context; await context.updateSyncRules(BASIC_SYNC_RULES); - await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); + await connectionManager.query(`CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description text)`); - await connectionManager.query(`INSERT INTO test_data(description) VALUES('test1')`); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'test1'` - ); - const testId = result.test_id; + const testId = uuid(); + await connectionManager.query(`INSERT INTO test_data(id, description) VALUES('${testId}','test1')`); await context.replicateSnapshot(); @@ -221,16 +203,13 @@ function defineBinlogStreamTests(factory: StorageFactory) { `); await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, date DATE, datetime DATETIME, timestamp TIMESTAMP)` + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description TEXT, date DATE, datetime DATETIME, timestamp TIMESTAMP)` ); + const testId = uuid(); await connectionManager.query(` - INSERT INTO test_data(description, date, datetime, timestamp) VALUES('testDates', '2023-03-06', '2023-03-06 15:47', '2023-03-06 15:47') + INSERT INTO test_data(id, description, date, datetime, timestamp) VALUES('${testId}','testDates', '2023-03-06', '2023-03-06 15:47', '2023-03-06 15:47') `); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'testDates'` - ); - const testId = result.test_id; await context.replicateSnapshot(); @@ -259,7 +238,7 @@ function defineBinlogStreamTests(factory: StorageFactory) { `); await connectionManager.query( - `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description TEXT, date DATE, datetime DATETIME, timestamp TIMESTAMP)` + `CREATE TABLE test_data (id CHAR(36) PRIMARY KEY, description TEXT, date DATE, datetime DATETIME, timestamp TIMESTAMP)` ); await context.replicateSnapshot(); @@ -271,13 +250,10 @@ function defineBinlogStreamTests(factory: StorageFactory) { context.startStreaming(); + const testId = uuid(); await connectionManager.query(` - INSERT INTO test_data(description, date, datetime, timestamp) VALUES('testDates', '2023-03-06', '2023-03-06 15:47', '2023-03-06 15:47') + INSERT INTO test_data(id, description, date, datetime, timestamp) VALUES('${testId}','testDates', '2023-03-06', '2023-03-06 15:47', '2023-03-06 15:47') `); - const [[result]] = await connectionManager.query( - `SELECT id AS test_id FROM test_data WHERE description = 'testDates'` - ); - const testId = result.test_id; const data = await context.getBucketData('global[]'); expect(data).toMatchObject([ @@ -303,9 +279,7 @@ function defineBinlogStreamTests(factory: StorageFactory) { const { connectionManager } = context; await context.updateSyncRules(BASIC_SYNC_RULES); - await connectionManager.query( - `CREATE TABLE test_donotsync (id CHAR(36) PRIMARY KEY DEFAULT (UUID()), description text)` - ); + await connectionManager.query(`CREATE TABLE test_donotsync (id CHAR(36) PRIMARY KEY, description text)`); await context.replicateSnapshot(); @@ -316,7 +290,7 @@ function defineBinlogStreamTests(factory: StorageFactory) { context.startStreaming(); - await connectionManager.query(`INSERT INTO test_donotsync(description) VALUES('test1')`); + await connectionManager.query(`INSERT INTO test_donotsync(id, description) VALUES('${uuid()}','test1')`); const data = await context.getBucketData('global[]'); expect(data).toMatchObject([]); diff --git a/modules/module-mysql/test/src/MysqlTypeMappings.test.ts b/modules/module-mysql/test/src/mysql-to-sqlite.test.ts similarity index 100% rename from modules/module-mysql/test/src/MysqlTypeMappings.test.ts rename to modules/module-mysql/test/src/mysql-to-sqlite.test.ts diff --git a/modules/module-mysql/test/src/mysql-utils.test.ts b/modules/module-mysql/test/src/mysql-utils.test.ts new file mode 100644 index 000000000..039756267 --- /dev/null +++ b/modules/module-mysql/test/src/mysql-utils.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, test } from 'vitest'; +import { isVersionAtLeast } from '@module/utils/mysql-utils.js'; + +describe('MySQL Utility Tests', () => { + test('Minimum version checking ', () => { + const newerVersion = '8.4.0'; + const olderVersion = '5.7'; + const sameVersion = '8.0'; + // Improperly formatted semantic versions should be handled gracefully if possible + const improperSemver = '5.7.42-0ubuntu0.18.04.1-log'; + + expect(isVersionAtLeast(newerVersion, '8.0')).toBeTruthy(); + expect(isVersionAtLeast(sameVersion, '8.0')).toBeTruthy(); + expect(isVersionAtLeast(olderVersion, '8.0')).toBeFalsy(); + expect(isVersionAtLeast(improperSemver, '5.7')).toBeTruthy(); + }); +}); diff --git a/modules/module-mysql/test/src/util.ts b/modules/module-mysql/test/src/util.ts index 135cb7e08..f87f13e82 100644 --- a/modules/module-mysql/test/src/util.ts +++ b/modules/module-mysql/test/src/util.ts @@ -3,8 +3,7 @@ import { BucketStorageFactory, Metrics, MongoBucketStorage } from '@powersync/se import { env } from './env.js'; import mysqlPromise from 'mysql2/promise'; import { connectMongo } from '@core-tests/util.js'; -import { getMySQLVersion } from '@module/common/check-source-configuration.js'; -import { gte } from 'semver'; +import { getMySQLVersion, isVersionAtLeast } from '@module/utils/mysql-utils.js'; export const TEST_URI = env.MYSQL_TEST_URI; @@ -38,7 +37,7 @@ export const INITIALIZED_MONGO_STORAGE_FACTORY: StorageFactory = async () => { export async function clearTestDb(connection: mysqlPromise.Connection) { const version = await getMySQLVersion(connection); - if (gte(version, '8.4.0')) { + if (isVersionAtLeast(version, '8.4.0')) { await connection.query('RESET BINARY LOGS AND GTIDS'); } else { await connection.query('RESET MASTER'); @@ -55,7 +54,3 @@ export async function clearTestDb(connection: mysqlPromise.Connection) { } } } - -export function connectMySQLPool(): mysqlPromise.Pool { - return mysqlPromise.createPool(TEST_URI); -} From 81b8cbbf52d67de14ff94f7482bd3c9da77230e7 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 12 Nov 2024 14:21:14 +0200 Subject: [PATCH 286/298] Automatically clear errors when restarting replication. --- .../src/replication/ChangeStream.ts | 5 ++- .../test/src/change_stream.test.ts | 38 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 250217c95..290711a50 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -564,7 +564,10 @@ export class ChangeStream { stream.close(); }); - let waitForCheckpointLsn: string | null = null; + // Always start with a checkpoint. + // This helps us to clear erorrs when restarting, even if there is + // no data to replicate. + let waitForCheckpointLsn: string | null = await createCheckpoint(this.client, this.defaultDb); while (true) { if (this.abort_signal.aborted) { diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index bff0a6857..ddab9b8f0 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -4,7 +4,7 @@ import { BucketStorageFactory } from '@powersync/service-core'; import * as crypto from 'crypto'; import * as mongo from 'mongodb'; import { setTimeout } from 'node:timers/promises'; -import { describe, expect, test } from 'vitest'; +import { describe, expect, test, vi } from 'vitest'; import { ChangeStreamTestContext } from './change_stream_utils.js'; import { PostImagesOption } from '@module/types/types.js'; @@ -460,4 +460,40 @@ bucket_definitions: message: expect.stringContaining('stream was configured to require a post-image for all update events') }); }); + + test('recover from error', async () => { + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_data"`); + + await db.createCollection('test_data', { + changeStreamPreAndPostImages: { enabled: false } + }); + + const collection = db.collection('test_data'); + await collection.insertOne({ description: 'test1', num: 1152921504606846976n }); + + await context.replicateSnapshot(); + + // Simulate an error + await context.storage!.reportError(new Error('simulated error')); + expect((await context.factory.getActiveSyncRulesContent())?.last_fatal_error).toEqual('simulated error'); + + // startStreaming() should automatically clear the error. + context.startStreaming(); + + // getBucketData() creates a checkpoint that clears the error, so we don't do that + // Just wait, and check that the error is cleared automatically. + await vi.waitUntil( + async () => { + const error = (await context.factory.getActiveSyncRulesContent())?.last_fatal_error; + return error == null; + }, + { timeout: 2_000 } + ); + }); } From b8ccb407613c104f1d0d4ce50a4f0ea43d5c6213 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 12 Nov 2024 14:32:01 +0200 Subject: [PATCH 287/298] Use commit instead of keepalive. --- modules/module-mongodb/src/replication/ChangeStream.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 290711a50..1972784e4 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -214,9 +214,7 @@ export class ChangeStream { if (snapshotTime != null) { const lsn = getMongoLsn(snapshotTime); logger.info(`Snapshot commit at ${snapshotTime.inspect()} / ${lsn}`); - // keepalive() does an auto-commit if there is data - await batch.flush(); - await batch.keepalive(lsn); + await batch.commit(lsn); } else { throw new Error(`No snapshot clusterTime available.`); } @@ -597,8 +595,7 @@ export class ChangeStream { if (waitForCheckpointLsn != null && lsn >= waitForCheckpointLsn) { waitForCheckpointLsn = null; } - await batch.flush(); - await batch.keepalive(lsn); + await batch.commit(lsn); } else if ( changeDocument.operationType == 'insert' || changeDocument.operationType == 'update' || From b4452aa67257315f3300fdb4be2a78c2222f7fca Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 12 Nov 2024 16:18:58 +0200 Subject: [PATCH 288/298] Fix initial snapshot implementation. --- .../src/replication/ChangeStream.ts | 27 ++--- .../test/src/change_stream_utils.ts | 38 +++++- .../test/src/slow_tests.test.ts | 109 ++++++++++++++++++ 3 files changed, 156 insertions(+), 18 deletions(-) create mode 100644 modules/module-mongodb/test/src/slow_tests.test.ts diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 1972784e4..b21533b7e 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -176,19 +176,22 @@ export class ChangeStream { const sourceTables = this.sync_rules.getSourceTables(); await this.client.connect(); + // We need to get the snapshot time before taking the initial snapshot. const hello = await this.defaultDb.command({ hello: 1 }); - const startTime = hello.lastWrite?.majorityOpTime?.ts as mongo.Timestamp; + const snapshotTime = hello.lastWrite?.majorityOpTime?.ts as mongo.Timestamp; if (hello.msg == 'isdbgrid') { throw new Error('Sharded MongoDB Clusters are not supported yet (including MongoDB Serverless instances).'); } else if (hello.setName == null) { throw new Error('Standalone MongoDB instances are not supported - use a replicaset.'); - } else if (startTime == null) { + } else if (snapshotTime == null) { // Not known where this would happen apart from the above cases throw new Error('MongoDB lastWrite timestamp not found.'); } - const session = await this.client.startSession({ - snapshot: true - }); + // We previously used {snapshot: true} for the snapshot session. + // While it gives nice consistency guarantees, it fails when the + // snapshot takes longer than 5 minutes, due to minSnapshotHistoryWindowInSeconds + // expiring the snapshot. + const session = await this.client.startSession(); try { await this.storage.startBatch( { zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName }, @@ -209,15 +212,9 @@ export class ChangeStream { await touch(); } - const snapshotTime = session.clusterTime?.clusterTime ?? startTime; - - if (snapshotTime != null) { - const lsn = getMongoLsn(snapshotTime); - logger.info(`Snapshot commit at ${snapshotTime.inspect()} / ${lsn}`); - await batch.commit(lsn); - } else { - throw new Error(`No snapshot clusterTime available.`); - } + const lsn = getMongoLsn(snapshotTime); + logger.info(`Snapshot commit at ${snapshotTime.inspect()} / ${lsn}`); + await batch.commit(lsn); } ); } finally { @@ -289,7 +286,7 @@ export class ChangeStream { const db = this.client.db(table.schema); const collection = db.collection(table.table); - const query = collection.find({}, { session }); + const query = collection.find({}, { session, readConcern: { level: 'majority' } }); const cursor = query.stream(); diff --git a/modules/module-mongodb/test/src/change_stream_utils.ts b/modules/module-mongodb/test/src/change_stream_utils.ts index 76735d380..77a5d9647 100644 --- a/modules/module-mongodb/test/src/change_stream_utils.ts +++ b/modules/module-mongodb/test/src/change_stream_utils.ts @@ -91,7 +91,7 @@ export class ChangeStreamTestContext { getClientCheckpoint(this.client, this.db, this.factory, { timeout: options?.timeout ?? 15_000 }), this.streamPromise ]); - if (typeof checkpoint == undefined) { + if (typeof checkpoint == 'undefined') { // This indicates an issue with the test setup - streamingPromise completed instead // of getClientCheckpoint() throw new Error('Test failure - streamingPromise completed'); @@ -105,14 +105,32 @@ export class ChangeStreamTestContext { return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map)); } - async getBucketData(bucket: string, start?: string, options?: { timeout?: number }) { + async getBucketData( + bucket: string, + start?: string, + options?: { timeout?: number; limit?: number; chunkLimitBytes?: number } + ) { start ??= '0'; let checkpoint = await this.getCheckpoint(options); const map = new Map([[bucket, start]]); - const batch = this.storage!.getBucketDataBatch(checkpoint, map); + const batch = this.storage!.getBucketDataBatch(checkpoint, map, { + limit: options?.limit, + chunkLimitBytes: options?.chunkLimitBytes + }); const batches = await fromAsync(batch); return batches[0]?.batch.data ?? []; } + + async getChecksums(buckets: string[], options?: { timeout?: number }) { + let checkpoint = await this.getCheckpoint(options); + return this.storage!.getChecksums(checkpoint, buckets); + } + + async getChecksum(bucket: string, options?: { timeout?: number }) { + let checkpoint = await this.getCheckpoint(options); + const map = await this.storage!.getChecksums(checkpoint, [bucket]); + return map.get(bucket); + } } export async function getClientCheckpoint( @@ -144,3 +162,17 @@ export async function getClientCheckpoint( throw new Error(`Timeout while waiting for checkpoint ${lsn}. Last checkpoint: ${lastCp?.lsn}`); } + +export async function setSnapshotHistorySeconds(client: mongo.MongoClient, seconds: number) { + const { minSnapshotHistoryWindowInSeconds: currentValue } = await client + .db('admin') + .command({ getParameter: 1, minSnapshotHistoryWindowInSeconds: 1 }); + + await client.db('admin').command({ setParameter: 1, minSnapshotHistoryWindowInSeconds: seconds }); + + return { + async [Symbol.asyncDispose]() { + await client.db('admin').command({ setParameter: 1, minSnapshotHistoryWindowInSeconds: currentValue }); + } + }; +} diff --git a/modules/module-mongodb/test/src/slow_tests.test.ts b/modules/module-mongodb/test/src/slow_tests.test.ts new file mode 100644 index 000000000..535e967c4 --- /dev/null +++ b/modules/module-mongodb/test/src/slow_tests.test.ts @@ -0,0 +1,109 @@ +import { MONGO_STORAGE_FACTORY } from '@core-tests/util.js'; +import { BucketStorageFactory } from '@powersync/service-core'; +import * as mongo from 'mongodb'; +import { setTimeout } from 'node:timers/promises'; +import { describe, expect, test } from 'vitest'; +import { ChangeStreamTestContext, setSnapshotHistorySeconds } from './change_stream_utils.js'; +import { env } from './env.js'; + +type StorageFactory = () => Promise; + +const BASIC_SYNC_RULES = ` +bucket_definitions: + global: + data: + - SELECT _id as id, description FROM "test_data" +`; + +describe('change stream slow tests - mongodb', { timeout: 60_000 }, function () { + if (env.CI || env.SLOW_TESTS) { + defineSlowTests(MONGO_STORAGE_FACTORY); + } else { + // Need something in this file. + test('no-op', () => {}); + } +}); + +function defineSlowTests(factory: StorageFactory) { + test('replicating snapshot with lots of data', async () => { + await using context = await ChangeStreamTestContext.open(factory); + // Test with low minSnapshotHistoryWindowInSeconds, to trigger: + // > Read timestamp .. is older than the oldest available timestamp. + // This happened when we had {snapshot: true} in the initial + // snapshot session. + await using _ = await setSnapshotHistorySeconds(context.client, 1); + const { db } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_data1" + - SELECT _id as id, description, num FROM "test_data2" + `); + + const collection1 = db.collection('test_data1'); + const collection2 = db.collection('test_data2'); + + let operations: mongo.AnyBulkWriteOperation[] = []; + for (let i = 0; i < 10_000; i++) { + operations.push({ insertOne: { document: { description: `pre${i}`, num: i } } }); + } + await collection1.bulkWrite(operations); + await collection2.bulkWrite(operations); + + await context.replicateSnapshot(); + context.startStreaming(); + const checksum = await context.getChecksum('global[]'); + expect(checksum).toMatchObject({ + count: 20_000 + }); + }); + + test('writes concurrently with snapshot', async () => { + // If there is an issue with snapshotTime (the start LSN for the + // changestream), we may miss updates, which this test would + // hopefully catch. + + await using context = await ChangeStreamTestContext.open(factory); + const { db } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT _id as id, description, num FROM "test_data" + `); + + const collection = db.collection('test_data'); + + let operations: mongo.AnyBulkWriteOperation[] = []; + for (let i = 0; i < 5_000; i++) { + operations.push({ insertOne: { document: { description: `pre${i}`, num: i } } }); + } + await collection.bulkWrite(operations); + + const snapshotPromise = context.replicateSnapshot(); + + for (let i = 49; i >= 0; i--) { + await collection.updateMany( + { num: { $gte: i * 100, $lt: i * 100 + 100 } }, + { $set: { description: 'updated' + i } } + ); + await setTimeout(20); + } + + await snapshotPromise; + context.startStreaming(); + + const data = await context.getBucketData('global[]', undefined, { limit: 50_000, chunkLimitBytes: 60_000_000 }); + + const preDocuments = data.filter((d) => JSON.parse(d.data! as string).description.startsWith('pre')).length; + const updatedDocuments = data.filter((d) => JSON.parse(d.data! as string).description.startsWith('updated')).length; + + // If the test works properly, preDocuments should be around 2000-3000. + // The total should be around 9000-9900. + // However, it is very sensitive to timing, so we allow a wide range. + // updatedDocuments must be strictly >= 5000, otherwise something broke. + expect(updatedDocuments).toBeGreaterThanOrEqual(5_000); + expect(preDocuments).toBeLessThanOrEqual(5_000); + }); +} From 225af878791c2d979b76df6a457a34c87de0d589 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 13 Nov 2024 12:06:21 +0200 Subject: [PATCH 289/298] Avoid collMod permission on _powersync_checkpoints. --- modules/module-mongodb/src/replication/ChangeStream.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 250217c95..4e3548f12 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -234,8 +234,10 @@ export class ChangeStream { changeStreamPreAndPostImages: { enabled: true } }); } else if (this.usePostImages && collection.options?.changeStreamPreAndPostImages?.enabled != true) { - await this.defaultDb.command({ - collMod: CHECKPOINTS_COLLECTION, + // Drop + create requires less permissions than collMod, + // and we don't care about the data in this collection. + await this.defaultDb.dropCollection(CHECKPOINTS_COLLECTION); + await this.defaultDb.createCollection(CHECKPOINTS_COLLECTION, { changeStreamPreAndPostImages: { enabled: true } }); } From 267ec11b35815a633149762b054c21220d3f7961 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 13 Nov 2024 12:10:54 +0200 Subject: [PATCH 290/298] Minor cleanup. --- modules/module-mongodb/src/replication/MongoRelation.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/module-mongodb/src/replication/MongoRelation.ts b/modules/module-mongodb/src/replication/MongoRelation.ts index cdb9299ed..e2dc675e1 100644 --- a/modules/module-mongodb/src/replication/MongoRelation.ts +++ b/modules/module-mongodb/src/replication/MongoRelation.ts @@ -146,7 +146,10 @@ function filterJsonData(data: any, depth = 0): any { export async function createCheckpoint(client: mongo.MongoClient, db: mongo.Db): Promise { const session = client.startSession(); try { - const result = await db.collection(CHECKPOINTS_COLLECTION).findOneAndUpdate( + // Note: If multiple PowerSync instances are replicating the same source database, + // they'll modify the same checkpoint document. This is fine - it could create + // more replication load than required, but won't break anything. + await db.collection(CHECKPOINTS_COLLECTION).findOneAndUpdate( { _id: 'checkpoint' as any }, @@ -160,7 +163,6 @@ export async function createCheckpoint(client: mongo.MongoClient, db: mongo.Db): } ); const time = session.operationTime!; - // console.log('marked checkpoint at', time, getMongoLsn(time)); // TODO: Use the above when we support custom write checkpoints return getMongoLsn(time); } finally { From c5ca6e728e1f3ab03c4ab4bf86f20ceaae64233c Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 13 Nov 2024 12:45:40 +0200 Subject: [PATCH 291/298] Validate changeStreamPreAndPostImages on existing collections. --- .../src/api/MongoRouteAPIAdapter.ts | 71 ++++-- .../test/src/mongo_test.test.ts | 226 ++++++++++++++---- 2 files changed, 231 insertions(+), 66 deletions(-) diff --git a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts index 5acad6eb0..a0bc519ec 100644 --- a/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts +++ b/modules/module-mongodb/src/api/MongoRouteAPIAdapter.ts @@ -34,6 +34,10 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { await this.client.close(); } + async [Symbol.asyncDispose]() { + await this.shutdown(); + } + async getSourceConfig(): Promise { return this.config; } @@ -77,6 +81,28 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { sqlSyncRules: sync_rules.SqlSyncRules ): Promise { let result: api.PatternResult[] = []; + + const validatePostImages = (schema: string, collection: mongo.CollectionInfo): service_types.ReplicationError[] => { + if (this.config.postImages == types.PostImagesOption.OFF) { + return []; + } else if (!collection.options?.changeStreamPreAndPostImages?.enabled) { + if (this.config.postImages == types.PostImagesOption.READ_ONLY) { + return [ + { level: 'fatal', message: `changeStreamPreAndPostImages not enabled on ${schema}.${collection.name}` } + ]; + } else { + return [ + { + level: 'warning', + message: `changeStreamPreAndPostImages not enabled on ${schema}.${collection.name}, will be enabled automatically` + } + ]; + } + } else { + return []; + } + }; + for (let tablePattern of tablePatterns) { const schema = tablePattern.schema; @@ -101,7 +127,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { { name: nameFilter }, - { nameOnly: true } + { nameOnly: false } ) .toArray(); @@ -117,6 +143,12 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { [], true ); + let errors: service_types.ReplicationError[] = []; + if (collection.type == 'view') { + errors.push({ level: 'warning', message: `Collection ${schema}.${tablePattern.name} is a view` }); + } else { + errors.push(...validatePostImages(schema, collection)); + } const syncData = sqlSyncRules.tableSyncsData(sourceTable); const syncParameters = sqlSyncRules.tableSyncsParameters(sourceTable); patternResult.tables.push({ @@ -125,7 +157,7 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { replication_id: ['_id'], data_queries: syncData, parameter_queries: syncParameters, - errors: [] + errors: errors }); } } else { @@ -141,26 +173,25 @@ export class MongoRouteAPIAdapter implements api.RouteAPI { const syncData = sqlSyncRules.tableSyncsData(sourceTable); const syncParameters = sqlSyncRules.tableSyncsParameters(sourceTable); + const collection = collections[0]; - if (collections.length == 1) { - patternResult.table = { - schema, - name: tablePattern.name, - replication_id: ['_id'], - data_queries: syncData, - parameter_queries: syncParameters, - errors: [] - }; - } else { - patternResult.table = { - schema, - name: tablePattern.name, - replication_id: ['_id'], - data_queries: syncData, - parameter_queries: syncParameters, - errors: [{ level: 'warning', message: `Collection ${schema}.${tablePattern.name} not found` }] - }; + let errors: service_types.ReplicationError[] = []; + if (collections.length != 1) { + errors.push({ level: 'warning', message: `Collection ${schema}.${tablePattern.name} not found` }); + } else if (collection.type == 'view') { + errors.push({ level: 'warning', message: `Collection ${schema}.${tablePattern.name} is a view` }); + } else if (!collection.options?.changeStreamPreAndPostImages?.enabled) { + errors.push(...validatePostImages(schema, collection)); } + + patternResult.table = { + schema, + name: tablePattern.name, + replication_id: ['_id'], + data_queries: syncData, + parameter_queries: syncParameters, + errors + }; } } return result; diff --git a/modules/module-mongodb/test/src/mongo_test.test.ts b/modules/module-mongodb/test/src/mongo_test.test.ts index 16cca3be7..5d30067da 100644 --- a/modules/module-mongodb/test/src/mongo_test.test.ts +++ b/modules/module-mongodb/test/src/mongo_test.test.ts @@ -1,10 +1,11 @@ import { MongoRouteAPIAdapter } from '@module/api/MongoRouteAPIAdapter.js'; import { ChangeStream } from '@module/replication/ChangeStream.js'; import { constructAfterRecord } from '@module/replication/MongoRelation.js'; -import { SqliteRow } from '@powersync/service-sync-rules'; +import { SqliteRow, SqlSyncRules } from '@powersync/service-sync-rules'; import * as mongo from 'mongodb'; import { describe, expect, test } from 'vitest'; import { clearTestDb, connectMongoData, TEST_CONNECTION_OPTIONS } from './util.js'; +import { PostImagesOption } from '@module/types/types.js'; describe('mongo data types', () => { async function setupTable(db: mongo.Db) { @@ -245,58 +246,191 @@ describe('mongo data types', () => { }); test('connection schema', async () => { - const adapter = new MongoRouteAPIAdapter({ + await using adapter = new MongoRouteAPIAdapter({ type: 'mongodb', ...TEST_CONNECTION_OPTIONS }); - try { - const db = adapter.db; - await clearTestDb(db); + const db = adapter.db; + await clearTestDb(db); - const collection = db.collection('test_data'); - await setupTable(db); - await insert(collection); + const collection = db.collection('test_data'); + await setupTable(db); + await insert(collection); + + const schema = await adapter.getConnectionSchema(); + const dbSchema = schema.filter((s) => s.name == TEST_CONNECTION_OPTIONS.database)[0]; + expect(dbSchema).not.toBeNull(); + expect(dbSchema.tables).toMatchObject([ + { + name: 'test_data', + columns: [ + { name: '_id', sqlite_type: 4, internal_type: 'Integer' }, + { name: 'bool', sqlite_type: 4, internal_type: 'Boolean' }, + { name: 'bytea', sqlite_type: 1, internal_type: 'Binary' }, + { name: 'date', sqlite_type: 2, internal_type: 'Date' }, + { name: 'decimal', sqlite_type: 2, internal_type: 'Decimal' }, + { name: 'float', sqlite_type: 8, internal_type: 'Double' }, + { name: 'int2', sqlite_type: 4, internal_type: 'Integer' }, + { name: 'int4', sqlite_type: 4, internal_type: 'Integer' }, + { name: 'int8', sqlite_type: 4, internal_type: 'Long' }, + // We can fix these later + { name: 'js', sqlite_type: 2, internal_type: 'Object' }, + { name: 'js2', sqlite_type: 2, internal_type: 'Object' }, + { name: 'maxKey', sqlite_type: 0, internal_type: 'MaxKey' }, + { name: 'minKey', sqlite_type: 0, internal_type: 'MinKey' }, + { name: 'nested', sqlite_type: 2, internal_type: 'Object' }, + { name: 'null', sqlite_type: 0, internal_type: 'Null' }, + { name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' }, + // We can fix these later + { name: 'pointer', sqlite_type: 2, internal_type: 'Object' }, + { name: 'pointer2', sqlite_type: 2, internal_type: 'Object' }, + { name: 'regexp', sqlite_type: 2, internal_type: 'RegExp' }, + // Can fix this later + { name: 'symbol', sqlite_type: 2, internal_type: 'String' }, + { name: 'text', sqlite_type: 2, internal_type: 'String' }, + { name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' }, + { name: 'undefined', sqlite_type: 0, internal_type: 'Null' }, + { name: 'uuid', sqlite_type: 2, internal_type: 'UUID' } + ] + } + ]); + }); + + test('validate postImages', async () => { + await using adapter = new MongoRouteAPIAdapter({ + type: 'mongodb', + ...TEST_CONNECTION_OPTIONS, + postImages: PostImagesOption.READ_ONLY + }); + const db = adapter.db; + await clearTestDb(db); + + const collection = db.collection('test_data'); + await setupTable(db); + await insert(collection); + + const rules = SqlSyncRules.fromYaml( + ` +bucket_definitions: + global: + data: + - select _id as id, * from test_data - const schema = await adapter.getConnectionSchema(); - const dbSchema = schema.filter((s) => s.name == TEST_CONNECTION_OPTIONS.database)[0]; - expect(dbSchema).not.toBeNull(); - expect(dbSchema.tables).toMatchObject([ + `, + { + ...adapter.getParseSyncRulesOptions(), + // No schema-based validation at this point + schema: undefined + } + ); + const source_table_patterns = rules.getSourceTables(); + const results = await adapter.getDebugTablesInfo(source_table_patterns, rules); + + const result = results[0]; + expect(result).not.toBeNull(); + expect(result.table).toMatchObject({ + schema: 'powersync_test_data', + name: 'test_data', + replication_id: ['_id'], + data_queries: true, + parameter_queries: false, + errors: [ { - name: 'test_data', - columns: [ - { name: '_id', sqlite_type: 4, internal_type: 'Integer' }, - { name: 'bool', sqlite_type: 4, internal_type: 'Boolean' }, - { name: 'bytea', sqlite_type: 1, internal_type: 'Binary' }, - { name: 'date', sqlite_type: 2, internal_type: 'Date' }, - { name: 'decimal', sqlite_type: 2, internal_type: 'Decimal' }, - { name: 'float', sqlite_type: 8, internal_type: 'Double' }, - { name: 'int2', sqlite_type: 4, internal_type: 'Integer' }, - { name: 'int4', sqlite_type: 4, internal_type: 'Integer' }, - { name: 'int8', sqlite_type: 4, internal_type: 'Long' }, - // We can fix these later - { name: 'js', sqlite_type: 2, internal_type: 'Object' }, - { name: 'js2', sqlite_type: 2, internal_type: 'Object' }, - { name: 'maxKey', sqlite_type: 0, internal_type: 'MaxKey' }, - { name: 'minKey', sqlite_type: 0, internal_type: 'MinKey' }, - { name: 'nested', sqlite_type: 2, internal_type: 'Object' }, - { name: 'null', sqlite_type: 0, internal_type: 'Null' }, - { name: 'objectId', sqlite_type: 2, internal_type: 'ObjectId' }, - // We can fix these later - { name: 'pointer', sqlite_type: 2, internal_type: 'Object' }, - { name: 'pointer2', sqlite_type: 2, internal_type: 'Object' }, - { name: 'regexp', sqlite_type: 2, internal_type: 'RegExp' }, - // Can fix this later - { name: 'symbol', sqlite_type: 2, internal_type: 'String' }, - { name: 'text', sqlite_type: 2, internal_type: 'String' }, - { name: 'timestamp', sqlite_type: 4, internal_type: 'Timestamp' }, - { name: 'undefined', sqlite_type: 0, internal_type: 'Null' }, - { name: 'uuid', sqlite_type: 2, internal_type: 'UUID' } - ] + level: 'fatal', + message: 'changeStreamPreAndPostImages not enabled on powersync_test_data.test_data' } - ]); - } finally { - await adapter.shutdown(); - } + ] + }); + }); + + test('validate postImages - auto-configure', async () => { + await using adapter = new MongoRouteAPIAdapter({ + type: 'mongodb', + ...TEST_CONNECTION_OPTIONS, + postImages: PostImagesOption.AUTO_CONFIGURE + }); + const db = adapter.db; + await clearTestDb(db); + + const collection = db.collection('test_data'); + await setupTable(db); + await insert(collection); + + const rules = SqlSyncRules.fromYaml( + ` +bucket_definitions: + global: + data: + - select _id as id, * from test_data + + `, + { + ...adapter.getParseSyncRulesOptions(), + // No schema-based validation at this point + schema: undefined + } + ); + const source_table_patterns = rules.getSourceTables(); + const results = await adapter.getDebugTablesInfo(source_table_patterns, rules); + + const result = results[0]; + expect(result).not.toBeNull(); + expect(result.table).toMatchObject({ + schema: 'powersync_test_data', + name: 'test_data', + replication_id: ['_id'], + data_queries: true, + parameter_queries: false, + errors: [ + { + level: 'warning', + message: + 'changeStreamPreAndPostImages not enabled on powersync_test_data.test_data, will be enabled automatically' + } + ] + }); + }); + + test('validate postImages - off', async () => { + await using adapter = new MongoRouteAPIAdapter({ + type: 'mongodb', + ...TEST_CONNECTION_OPTIONS, + postImages: PostImagesOption.OFF + }); + const db = adapter.db; + await clearTestDb(db); + + const collection = db.collection('test_data'); + await setupTable(db); + await insert(collection); + + const rules = SqlSyncRules.fromYaml( + ` +bucket_definitions: + global: + data: + - select _id as id, * from test_data + + `, + { + ...adapter.getParseSyncRulesOptions(), + // No schema-based validation at this point + schema: undefined + } + ); + const source_table_patterns = rules.getSourceTables(); + const results = await adapter.getDebugTablesInfo(source_table_patterns, rules); + + const result = results[0]; + expect(result).not.toBeNull(); + expect(result.table).toMatchObject({ + schema: 'powersync_test_data', + name: 'test_data', + replication_id: ['_id'], + data_queries: true, + parameter_queries: false, + errors: [] + }); }); }); From 52bc6153110ca7df73cfdea8dcce5ccf3a318a31 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 13 Nov 2024 13:18:41 +0200 Subject: [PATCH 292/298] Avoid current_data document storage for MongoDB. --- .../src/replication/ChangeStream.ts | 203 +++++++++--------- .../src/replication/BinLogStream.ts | 4 +- .../src/replication/WalStream.ts | 104 ++++----- .../service-core/src/storage/BucketStorage.ts | 10 + .../src/storage/mongo/MongoBucketBatch.ts | 128 ++++++----- .../storage/mongo/MongoSyncBucketStorage.ts | 25 +-- .../src/storage/mongo/OperationBatch.ts | 11 +- 7 files changed, 267 insertions(+), 218 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 4e3548f12..da9f9b628 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -191,7 +191,7 @@ export class ChangeStream { }); try { await this.storage.startBatch( - { zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName }, + { zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName, storeCurrentData: false }, async (batch) => { // Start by resolving all tables. // This checks postImage configuration, and that should fail as @@ -515,124 +515,127 @@ export class ChangeStream { // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName }, async (batch) => { - const lastLsn = batch.lastCheckpointLsn; - const startAfter = mongoLsnToTimestamp(lastLsn) ?? undefined; - logger.info(`Resume streaming at ${startAfter?.inspect()} / ${lastLsn}`); + await this.storage.startBatch( + { zeroLSN: ZERO_LSN, defaultSchema: this.defaultDb.databaseName, storeCurrentData: false }, + async (batch) => { + const lastLsn = batch.lastCheckpointLsn; + const startAfter = mongoLsnToTimestamp(lastLsn) ?? undefined; + logger.info(`Resume streaming at ${startAfter?.inspect()} / ${lastLsn}`); - // TODO: Use changeStreamSplitLargeEvent + // TODO: Use changeStreamSplitLargeEvent - const filters = this.getSourceNamespaceFilters(); + const filters = this.getSourceNamespaceFilters(); - const pipeline: mongo.Document[] = [ - { - $match: filters.$match - } - ]; - - let fullDocument: 'required' | 'updateLookup'; - - if (this.usePostImages) { - // 'read_only' or 'auto_configure' - // Configuration happens during snapshot, or when we see new - // collections. - fullDocument = 'required'; - } else { - fullDocument = 'updateLookup'; - } - - const streamOptions: mongo.ChangeStreamOptions = { - startAtOperationTime: startAfter, - showExpandedEvents: true, - useBigInt64: true, - maxAwaitTimeMS: 200, - fullDocument: fullDocument - }; - let stream: mongo.ChangeStream; - if (filters.multipleDatabases) { - // Requires readAnyDatabase@admin on Atlas - stream = this.client.watch(pipeline, streamOptions); - } else { - // Same general result, but requires less permissions than the above - stream = this.defaultDb.watch(pipeline, streamOptions); - } + const pipeline: mongo.Document[] = [ + { + $match: filters.$match + } + ]; - if (this.abort_signal.aborted) { - stream.close(); - return; - } + let fullDocument: 'required' | 'updateLookup'; - this.abort_signal.addEventListener('abort', () => { - stream.close(); - }); + if (this.usePostImages) { + // 'read_only' or 'auto_configure' + // Configuration happens during snapshot, or when we see new + // collections. + fullDocument = 'required'; + } else { + fullDocument = 'updateLookup'; + } - let waitForCheckpointLsn: string | null = null; + const streamOptions: mongo.ChangeStreamOptions = { + startAtOperationTime: startAfter, + showExpandedEvents: true, + useBigInt64: true, + maxAwaitTimeMS: 200, + fullDocument: fullDocument + }; + let stream: mongo.ChangeStream; + if (filters.multipleDatabases) { + // Requires readAnyDatabase@admin on Atlas + stream = this.client.watch(pipeline, streamOptions); + } else { + // Same general result, but requires less permissions than the above + stream = this.defaultDb.watch(pipeline, streamOptions); + } - while (true) { if (this.abort_signal.aborted) { - break; + stream.close(); + return; } - const changeDocument = await stream.tryNext(); + this.abort_signal.addEventListener('abort', () => { + stream.close(); + }); - if (changeDocument == null || this.abort_signal.aborted) { - continue; - } - await touch(); + let waitForCheckpointLsn: string | null = null; - if (startAfter != null && changeDocument.clusterTime?.lte(startAfter)) { - continue; - } + while (true) { + if (this.abort_signal.aborted) { + break; + } - // console.log('event', changeDocument); + const changeDocument = await stream.tryNext(); - if ( - (changeDocument.operationType == 'insert' || - changeDocument.operationType == 'update' || - changeDocument.operationType == 'replace') && - changeDocument.ns.coll == CHECKPOINTS_COLLECTION - ) { - const lsn = getMongoLsn(changeDocument.clusterTime!); - if (waitForCheckpointLsn != null && lsn >= waitForCheckpointLsn) { - waitForCheckpointLsn = null; - } - await batch.flush(); - await batch.keepalive(lsn); - } else if ( - changeDocument.operationType == 'insert' || - changeDocument.operationType == 'update' || - changeDocument.operationType == 'replace' || - changeDocument.operationType == 'delete' - ) { - if (waitForCheckpointLsn == null) { - waitForCheckpointLsn = await createCheckpoint(this.client, this.defaultDb); - } - const rel = getMongoRelation(changeDocument.ns); - const table = await this.getRelation(batch, rel); - if (table.syncAny) { - await this.writeChange(batch, table, changeDocument); + if (changeDocument == null || this.abort_signal.aborted) { + continue; } - } else if (changeDocument.operationType == 'drop') { - const rel = getMongoRelation(changeDocument.ns); - const table = await this.getRelation(batch, rel); - if (table.syncAny) { - await batch.drop([table]); - this.relation_cache.delete(table.objectId); + await touch(); + + if (startAfter != null && changeDocument.clusterTime?.lte(startAfter)) { + continue; } - } else if (changeDocument.operationType == 'rename') { - const relFrom = getMongoRelation(changeDocument.ns); - const relTo = getMongoRelation(changeDocument.to); - const tableFrom = await this.getRelation(batch, relFrom); - if (tableFrom.syncAny) { - await batch.drop([tableFrom]); - this.relation_cache.delete(tableFrom.objectId); + + // console.log('event', changeDocument); + + if ( + (changeDocument.operationType == 'insert' || + changeDocument.operationType == 'update' || + changeDocument.operationType == 'replace') && + changeDocument.ns.coll == CHECKPOINTS_COLLECTION + ) { + const lsn = getMongoLsn(changeDocument.clusterTime!); + if (waitForCheckpointLsn != null && lsn >= waitForCheckpointLsn) { + waitForCheckpointLsn = null; + } + await batch.flush(); + await batch.keepalive(lsn); + } else if ( + changeDocument.operationType == 'insert' || + changeDocument.operationType == 'update' || + changeDocument.operationType == 'replace' || + changeDocument.operationType == 'delete' + ) { + if (waitForCheckpointLsn == null) { + waitForCheckpointLsn = await createCheckpoint(this.client, this.defaultDb); + } + const rel = getMongoRelation(changeDocument.ns); + const table = await this.getRelation(batch, rel); + if (table.syncAny) { + await this.writeChange(batch, table, changeDocument); + } + } else if (changeDocument.operationType == 'drop') { + const rel = getMongoRelation(changeDocument.ns); + const table = await this.getRelation(batch, rel); + if (table.syncAny) { + await batch.drop([table]); + this.relation_cache.delete(table.objectId); + } + } else if (changeDocument.operationType == 'rename') { + const relFrom = getMongoRelation(changeDocument.ns); + const relTo = getMongoRelation(changeDocument.to); + const tableFrom = await this.getRelation(batch, relFrom); + if (tableFrom.syncAny) { + await batch.drop([tableFrom]); + this.relation_cache.delete(tableFrom.objectId); + } + // Here we do need to snapshot the new table + const collection = await this.getCollectionInfo(relTo.schema, relTo.name); + await this.handleRelation(batch, relTo, { snapshot: true, collectionInfo: collection }); } - // Here we do need to snapshot the new table - const collection = await this.getCollectionInfo(relTo.schema, relTo.name); - await this.handleRelation(batch, relTo, { snapshot: true, collectionInfo: collection }); } } - }); + ); } } diff --git a/modules/module-mysql/src/replication/BinLogStream.ts b/modules/module-mysql/src/replication/BinLogStream.ts index 44e594f6d..d5e0c43ad 100644 --- a/modules/module-mysql/src/replication/BinLogStream.ts +++ b/modules/module-mysql/src/replication/BinLogStream.ts @@ -260,7 +260,7 @@ AND table_type = 'BASE TABLE';`, await promiseConnection.query('START TRANSACTION'); const sourceTables = this.syncRules.getSourceTables(); await this.storage.startBatch( - { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, + { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema, storeCurrentData: true }, async (batch) => { for (let tablePattern of sourceTables) { const tables = await this.getQualifiedTableNames(batch, tablePattern); @@ -383,7 +383,7 @@ AND table_type = 'BASE TABLE';`, if (!this.stopped) { await this.storage.startBatch( - { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema }, + { zeroLSN: ReplicatedGTID.ZERO.comparable, defaultSchema: this.defaultSchema, storeCurrentData: true }, async (batch) => { const zongji = this.connections.createBinlogListener(); diff --git a/modules/module-postgres/src/replication/WalStream.ts b/modules/module-postgres/src/replication/WalStream.ts index 06d9868d2..67436646d 100644 --- a/modules/module-postgres/src/replication/WalStream.ts +++ b/modules/module-postgres/src/replication/WalStream.ts @@ -341,17 +341,20 @@ WHERE oid = $1::regclass`, async initialReplication(db: pgwire.PgConnection, lsn: string) { const sourceTables = this.sync_rules.getSourceTables(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA }, async (batch) => { - for (let tablePattern of sourceTables) { - const tables = await this.getQualifiedTableNames(batch, db, tablePattern); - for (let table of tables) { - await this.snapshotTable(batch, db, table); - await batch.markSnapshotDone([table], lsn); - await touch(); + await this.storage.startBatch( + { zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA, storeCurrentData: true }, + async (batch) => { + for (let tablePattern of sourceTables) { + const tables = await this.getQualifiedTableNames(batch, db, tablePattern); + for (let table of tables) { + await this.snapshotTable(batch, db, table); + await batch.markSnapshotDone([table], lsn); + await touch(); + } } + await batch.commit(lsn); } - await batch.commit(lsn); - }); + ); } static *getQueryData(results: Iterable): Generator { @@ -577,55 +580,58 @@ WHERE oid = $1::regclass`, // Auto-activate as soon as initial replication is done await this.storage.autoActivate(); - await this.storage.startBatch({ zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA }, async (batch) => { - // Replication never starts in the middle of a transaction - let inTx = false; - let count = 0; + await this.storage.startBatch( + { zeroLSN: ZERO_LSN, defaultSchema: POSTGRES_DEFAULT_SCHEMA, storeCurrentData: true }, + async (batch) => { + // Replication never starts in the middle of a transaction + let inTx = false; + let count = 0; - for await (const chunk of replicationStream.pgoutputDecode()) { - await touch(); + for await (const chunk of replicationStream.pgoutputDecode()) { + await touch(); - if (this.abort_signal.aborted) { - break; - } + if (this.abort_signal.aborted) { + break; + } - // chunkLastLsn may come from normal messages in the chunk, - // or from a PrimaryKeepalive message. - const { messages, lastLsn: chunkLastLsn } = chunk; - for (const msg of messages) { - if (msg.tag == 'relation') { - await this.handleRelation(batch, getPgOutputRelation(msg), true); - } else if (msg.tag == 'begin') { - inTx = true; - } else if (msg.tag == 'commit') { - Metrics.getInstance().transactions_replicated_total.add(1); - inTx = false; - await batch.commit(msg.lsn!); - await this.ack(msg.lsn!, replicationStream); - } else { - if (count % 100 == 0) { - logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); - } + // chunkLastLsn may come from normal messages in the chunk, + // or from a PrimaryKeepalive message. + const { messages, lastLsn: chunkLastLsn } = chunk; + for (const msg of messages) { + if (msg.tag == 'relation') { + await this.handleRelation(batch, getPgOutputRelation(msg), true); + } else if (msg.tag == 'begin') { + inTx = true; + } else if (msg.tag == 'commit') { + Metrics.getInstance().transactions_replicated_total.add(1); + inTx = false; + await batch.commit(msg.lsn!); + await this.ack(msg.lsn!, replicationStream); + } else { + if (count % 100 == 0) { + logger.info(`${this.slot_name} replicating op ${count} ${msg.lsn}`); + } - count += 1; - await this.writeChange(batch, msg); + count += 1; + await this.writeChange(batch, msg); + } } - } - if (!inTx) { - // In a transaction, we ack and commit according to the transaction progress. - // Outside transactions, we use the PrimaryKeepalive messages to advance progress. - // Big caveat: This _must not_ be used to skip individual messages, since this LSN - // may be in the middle of the next transaction. - // It must only be used to associate checkpoints with LSNs. - if (await batch.keepalive(chunkLastLsn)) { - await this.ack(chunkLastLsn, replicationStream); + if (!inTx) { + // In a transaction, we ack and commit according to the transaction progress. + // Outside transactions, we use the PrimaryKeepalive messages to advance progress. + // Big caveat: This _must not_ be used to skip individual messages, since this LSN + // may be in the middle of the next transaction. + // It must only be used to associate checkpoints with LSNs. + if (await batch.keepalive(chunkLastLsn)) { + await this.ack(chunkLastLsn, replicationStream); + } } - } - Metrics.getInstance().chunks_replicated_total.add(1); + Metrics.getInstance().chunks_replicated_total.add(1); + } } - }); + ); } async ack(lsn: string, replicationStream: pgwire.ReplicationStream) { diff --git a/packages/service-core/src/storage/BucketStorage.ts b/packages/service-core/src/storage/BucketStorage.ts index 96f16ad56..735367700 100644 --- a/packages/service-core/src/storage/BucketStorage.ts +++ b/packages/service-core/src/storage/BucketStorage.ts @@ -201,6 +201,16 @@ export interface BucketDataBatchOptions { export interface StartBatchOptions extends ParseSyncRulesOptions { zeroLSN: string; + /** + * Whether or not to store a copy of the current data. + * + * This is needed if we need to apply partial updates, for example + * when we get TOAST values from Postgres. + * + * This is not needed when we get the full document from the source + * database, for example from MongoDB. + */ + storeCurrentData: boolean; } export interface SyncRulesBucketStorageListener extends DisposableListener { diff --git a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts index 20e200500..54bb81443 100644 --- a/packages/service-core/src/storage/mongo/MongoBucketBatch.ts +++ b/packages/service-core/src/storage/mongo/MongoBucketBatch.ts @@ -33,6 +33,16 @@ const MAX_ROW_SIZE = 15 * 1024 * 1024; // In the future, we can investigate allowing multiple replication streams operating independently. const replicationMutex = new util.Mutex(); +export interface MongoBucketBatchOptions { + db: PowerSyncMongo; + syncRules: SqlSyncRules; + groupId: number; + slotName: string; + lastCheckpointLsn: string | null; + noCheckpointBeforeLsn: string; + storeCurrentData: boolean; +} + export class MongoBucketBatch extends DisposableObserver implements BucketStorageBatch { private readonly client: mongo.MongoClient; public readonly db: PowerSyncMongo; @@ -42,6 +52,7 @@ export class MongoBucketBatch extends DisposableObserver { - // 1. Find sizes of current_data documents, to assist in intelligent batching without - // exceeding memory limits. - // - // A previous attempt tried to do batching by the results of the current_data query - // (automatically limited to 48MB(?) per batch by MongoDB). The issue is that it changes - // the order of processing, which then becomes really tricky to manage. - // This now takes 2+ queries, but doesn't have any issues with order of operations. - const sizeLookups: SourceKey[] = batch.batch.map((r) => { - return { g: this.group_id, t: r.record.sourceTable.id, k: r.beforeId }; - }); + let sizes: Map | undefined = undefined; + if (this.storeCurrentData) { + // We skip this step if we don't store current_data, since the sizes will + // always be small in that case. + + // Find sizes of current_data documents, to assist in intelligent batching without + // exceeding memory limits. + // + // A previous attempt tried to do batching by the results of the current_data query + // (automatically limited to 48MB(?) per batch by MongoDB). The issue is that it changes + // the order of processing, which then becomes really tricky to manage. + // This now takes 2+ queries, but doesn't have any issues with order of operations. + const sizeLookups: SourceKey[] = batch.batch.map((r) => { + return { g: this.group_id, t: r.record.sourceTable.id, k: r.beforeId }; + }); - const sizes = new Map(); + sizes = new Map(); - const sizeCursor: mongo.AggregationCursor<{ _id: SourceKey; size: number }> = this.db.current_data.aggregate( - [ - { - $match: { - _id: { $in: sizeLookups } - } - }, - { - $project: { - _id: 1, - size: { $bsonSize: '$$ROOT' } + const sizeCursor: mongo.AggregationCursor<{ _id: SourceKey; size: number }> = this.db.current_data.aggregate( + [ + { + $match: { + _id: { $in: sizeLookups } + } + }, + { + $project: { + _id: 1, + size: { $bsonSize: '$$ROOT' } + } } - } - ], - { session } - ); - for await (let doc of sizeCursor.stream()) { - const key = cacheKey(doc._id.t, doc._id.k); - sizes.set(key, doc.size); + ], + { session } + ); + for await (let doc of sizeCursor.stream()) { + const key = cacheKey(doc._id.t, doc._id.k); + sizes.set(key, doc.size); + } } // If set, we need to start a new transaction with this batch. @@ -181,6 +192,7 @@ export class MongoBucketBatch extends DisposableObserver 16MB. afterData = new bson.Binary(bson.serialize(after!)); diff --git a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts index 72e007714..88da4540d 100644 --- a/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts +++ b/packages/service-core/src/storage/mongo/MongoSyncBucketStorage.ts @@ -53,7 +53,7 @@ export class MongoSyncBucketStorage } }); - private parsedSyncRulesCache: {parsed: SqlSyncRules, options: ParseSyncRulesOptions} | undefined; + private parsedSyncRulesCache: { parsed: SqlSyncRules; options: ParseSyncRulesOptions } | undefined; private writeCheckpointAPI: WriteCheckpointAPI; constructor( @@ -104,13 +104,13 @@ export class MongoSyncBucketStorage } getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules { - const {parsed, options: cachedOptions} = this.parsedSyncRulesCache ?? {}; + const { parsed, options: cachedOptions } = this.parsedSyncRulesCache ?? {}; /** * Check if the cached sync rules, if present, had the same options. * Parse sync rules if the options are different or if there is no cached value. */ - if (!parsed || options.defaultSchema != cachedOptions?.defaultSchema ) { - this.parsedSyncRulesCache = {parsed: this.sync_rules.parsed(options).sync_rules, options}; + if (!parsed || options.defaultSchema != cachedOptions?.defaultSchema) { + this.parsedSyncRulesCache = { parsed: this.sync_rules.parsed(options).sync_rules, options }; } return this.parsedSyncRulesCache!.parsed; @@ -141,14 +141,15 @@ export class MongoSyncBucketStorage ); const checkpoint_lsn = doc?.last_checkpoint_lsn ?? null; - await using batch = new MongoBucketBatch( - this.db, - this.sync_rules.parsed(options).sync_rules, - this.group_id, - this.slot_name, - checkpoint_lsn, - doc?.no_checkpoint_before ?? options.zeroLSN - ); + await using batch = new MongoBucketBatch({ + db: this.db, + syncRules: this.sync_rules.parsed(options).sync_rules, + groupId: this.group_id, + slotName: this.slot_name, + lastCheckpointLsn: checkpoint_lsn, + noCheckpointBeforeLsn: doc?.no_checkpoint_before ?? options.zeroLSN, + storeCurrentData: options.storeCurrentData + }); this.iterateListeners((cb) => cb.batchStarted?.(batch)); await callback(batch); diff --git a/packages/service-core/src/storage/mongo/OperationBatch.ts b/packages/service-core/src/storage/mongo/OperationBatch.ts index 4bcc0e64a..127562d54 100644 --- a/packages/service-core/src/storage/mongo/OperationBatch.ts +++ b/packages/service-core/src/storage/mongo/OperationBatch.ts @@ -43,7 +43,16 @@ export class OperationBatch { return this.batch.length >= MAX_BATCH_COUNT || this.currentSize > MAX_RECORD_BATCH_SIZE; } - *batched(sizes: Map): Generator { + /** + * + * @param sizes Map of source key to estimated size of the current_data document, or undefined if current_data is not persisted. + * + */ + *batched(sizes: Map | undefined): Generator { + if (sizes == null) { + yield this.batch; + return; + } let currentBatch: RecordOperation[] = []; let currentBatchSize = 0; for (let op of this.batch) { From 812b5c4f098f5e1b7493fcb8acb0d4087d2b155a Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 13 Nov 2024 13:24:35 +0200 Subject: [PATCH 293/298] Fix tests. --- packages/service-core/test/src/util.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/service-core/test/src/util.ts b/packages/service-core/test/src/util.ts index 421879d3c..138ee5d03 100644 --- a/packages/service-core/test/src/util.ts +++ b/packages/service-core/test/src/util.ts @@ -51,7 +51,8 @@ export const PARSE_OPTIONS: ParseSyncRulesOptions = { export const BATCH_OPTIONS: StartBatchOptions = { ...PARSE_OPTIONS, - zeroLSN: ZERO_LSN + zeroLSN: ZERO_LSN, + storeCurrentData: true }; export function testRules(content: string): PersistedSyncRulesContent { From 28e2269bb63cdf0ea3a7be9b89bde4ce84854a66 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 13 Nov 2024 16:26:45 +0200 Subject: [PATCH 294/298] Use $changeStreamSplitLargeEvent to handle large updates. (#130) --- .../src/replication/ChangeStream.ts | 38 +++++++++++++++--- .../test/src/change_stream.test.ts | 40 ++++++++++++++----- 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/modules/module-mongodb/src/replication/ChangeStream.ts b/modules/module-mongodb/src/replication/ChangeStream.ts index 61224848e..730f39813 100644 --- a/modules/module-mongodb/src/replication/ChangeStream.ts +++ b/modules/module-mongodb/src/replication/ChangeStream.ts @@ -517,14 +517,13 @@ export class ChangeStream { const startAfter = mongoLsnToTimestamp(lastLsn) ?? undefined; logger.info(`Resume streaming at ${startAfter?.inspect()} / ${lastLsn}`); - // TODO: Use changeStreamSplitLargeEvent - const filters = this.getSourceNamespaceFilters(); const pipeline: mongo.Document[] = [ { $match: filters.$match - } + }, + { $changeStreamSplitLargeEvent: {} } ]; let fullDocument: 'required' | 'updateLookup'; @@ -568,22 +567,49 @@ export class ChangeStream { // no data to replicate. let waitForCheckpointLsn: string | null = await createCheckpoint(this.client, this.defaultDb); + let splitDocument: mongo.ChangeStreamDocument | null = null; + while (true) { if (this.abort_signal.aborted) { break; } - const changeDocument = await stream.tryNext(); + const originalChangeDocument = await stream.tryNext(); - if (changeDocument == null || this.abort_signal.aborted) { + if (originalChangeDocument == null || this.abort_signal.aborted) { continue; } await touch(); - if (startAfter != null && changeDocument.clusterTime?.lte(startAfter)) { + if (startAfter != null && originalChangeDocument.clusterTime?.lte(startAfter)) { continue; } + let changeDocument = originalChangeDocument; + if (originalChangeDocument?.splitEvent != null) { + // Handle split events from $changeStreamSplitLargeEvent. + // This is only relevant for very large update operations. + const splitEvent = originalChangeDocument?.splitEvent; + + if (splitDocument == null) { + splitDocument = originalChangeDocument; + } else { + splitDocument = Object.assign(splitDocument, originalChangeDocument); + } + + if (splitEvent.fragment == splitEvent.of) { + // Got all fragments + changeDocument = splitDocument; + splitDocument = null; + } else { + // Wait for more fragments + continue; + } + } else if (splitDocument != null) { + // We were waiting for fragments, but got a different event + throw new Error(`Incomplete splitEvent: ${JSON.stringify(splitDocument.splitEvent)}`); + } + // console.log('event', changeDocument); if ( diff --git a/modules/module-mongodb/test/src/change_stream.test.ts b/modules/module-mongodb/test/src/change_stream.test.ts index ddab9b8f0..0380fce6b 100644 --- a/modules/module-mongodb/test/src/change_stream.test.ts +++ b/modules/module-mongodb/test/src/change_stream.test.ts @@ -352,32 +352,41 @@ bucket_definitions: expect(data).toMatchObject([putOp('test_data', { id: test_id, description: 'test1' })]); }); - // Not correctly implemented yet - test.skip('large record', async () => { + test('large record', async () => { + // Test a large update. + + // Without $changeStreamSplitLargeEvent, we get this error: + // MongoServerError: PlanExecutor error during aggregation :: caused by :: BSONObj size: 33554925 (0x20001ED) is invalid. + // Size must be between 0 and 16793600(16MB) + await using context = await ChangeStreamTestContext.open(factory); await context.updateSyncRules(`bucket_definitions: global: data: - - SELECT _id as id, description, other FROM "test_data"`); + - SELECT _id as id, name, other FROM "test_data"`); const { db } = context; await context.replicateSnapshot(); - // 16MB - const largeDescription = crypto.randomBytes(8_000_000 - 100).toString('hex'); - const collection = db.collection('test_data'); - const result = await collection.insertOne({ description: largeDescription }); + const result = await collection.insertOne({ name: 't1' }); const test_id = result.insertedId; - await collection.updateOne({ _id: test_id }, { $set: { name: 't2' } }); + // 12MB field. + // The field appears twice in the ChangeStream event, so the total size + // is > 16MB. + + // We don't actually have this description field in the sync rules, + // That causes other issues, not relevant for this specific test. + const largeDescription = crypto.randomBytes(12000000 / 2).toString('hex'); + + await collection.updateOne({ _id: test_id }, { $set: { description: largeDescription } }); context.startStreaming(); const data = await context.getBucketData('global[]'); expect(data.length).toEqual(2); - const row = JSON.parse(data[0].data as string); - delete row.description; - expect(row).toEqual({ id: test_id.toHexString() }); + const row1 = JSON.parse(data[0].data as string); + expect(row1).toEqual({ id: test_id.toHexString(), name: 't1' }); delete data[0].data; expect(data[0]).toMatchObject({ object_id: test_id.toHexString(), @@ -385,6 +394,15 @@ bucket_definitions: op: 'PUT', op_id: '1' }); + const row2 = JSON.parse(data[1].data as string); + expect(row2).toEqual({ id: test_id.toHexString(), name: 't1' }); + delete data[1].data; + expect(data[1]).toMatchObject({ + object_id: test_id.toHexString(), + object_type: 'test_data', + op: 'PUT', + op_id: '2' + }); }); test('collection not in sync rules', async () => { From 7b168a238d9e4a76332a78663e757a481889dd06 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 14 Nov 2024 09:41:31 +0200 Subject: [PATCH 295/298] Fix tests. --- .../src/routes/probes.integration.test.ts | 64 ++++++++++--------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/packages/service-core/test/src/routes/probes.integration.test.ts b/packages/service-core/test/src/routes/probes.integration.test.ts index 5b1b3e276..7a3419e70 100644 --- a/packages/service-core/test/src/routes/probes.integration.test.ts +++ b/packages/service-core/test/src/routes/probes.integration.test.ts @@ -6,8 +6,8 @@ import * as system from '../../../src/system/system-index.js'; import { configureFastifyServer } from '../../../src/index.js'; import { ProbeRoutes } from '../../../src/routes/endpoints/probes.js'; -vi.mock("@powersync/lib-services-framework", async () => { - const actual = await vi.importActual("@powersync/lib-services-framework") as any; +vi.mock('@powersync/lib-services-framework', async () => { + const actual = (await vi.importActual('@powersync/lib-services-framework')) as any; return { ...actual, container: { @@ -15,18 +15,18 @@ vi.mock("@powersync/lib-services-framework", async () => { probes: { state: vi.fn() } - }, - } -}) + } + }; +}); describe('Probe Routes Integration', () => { let app: FastifyInstance; - let mockSystem: system.CorePowerSyncSystem; + let mockSystem: system.ServiceContext; beforeEach(async () => { app = Fastify(); - mockSystem = {} as system.CorePowerSyncSystem; - await configureFastifyServer(app, { system: mockSystem }); + mockSystem = { routerEngine: {} } as system.ServiceContext; + await configureFastifyServer(app, { service_context: mockSystem }); await app.ready(); }); @@ -46,7 +46,7 @@ describe('Probe Routes Integration', () => { const response = await app.inject({ method: 'GET', - url: ProbeRoutes.STARTUP, + url: ProbeRoutes.STARTUP }); expect(response.statusCode).toBe(200); @@ -67,7 +67,7 @@ describe('Probe Routes Integration', () => { const response = await app.inject({ method: 'GET', - url: ProbeRoutes.STARTUP, + url: ProbeRoutes.STARTUP }); expect(response.statusCode).toBe(400); @@ -90,7 +90,7 @@ describe('Probe Routes Integration', () => { const response = await app.inject({ method: 'GET', - url: ProbeRoutes.LIVENESS, + url: ProbeRoutes.LIVENESS }); expect(response.statusCode).toBe(200); @@ -111,7 +111,7 @@ describe('Probe Routes Integration', () => { const response = await app.inject({ method: 'GET', - url: ProbeRoutes.LIVENESS, + url: ProbeRoutes.LIVENESS }); expect(response.statusCode).toBe(400); @@ -134,7 +134,7 @@ describe('Probe Routes Integration', () => { const response = await app.inject({ method: 'GET', - url: ProbeRoutes.READINESS, + url: ProbeRoutes.READINESS }); expect(response.statusCode).toBe(200); @@ -155,7 +155,7 @@ describe('Probe Routes Integration', () => { const response = await app.inject({ method: 'GET', - url: ProbeRoutes.READINESS, + url: ProbeRoutes.READINESS }); expect(response.statusCode).toBe(400); @@ -172,17 +172,19 @@ describe('Probe Routes Integration', () => { vi.mocked(container.probes.state).mockReturnValue(mockState); // Create array of 15 concurrent requests (default concurrency is 10) - const requests = Array(15).fill(null).map(() => - app.inject({ - method: 'GET', - url: ProbeRoutes.STARTUP, - }) - ); + const requests = Array(15) + .fill(null) + .map(() => + app.inject({ + method: 'GET', + url: ProbeRoutes.STARTUP + }) + ); const responses = await Promise.all(requests); // All requests should complete successfully - responses.forEach(response => { + responses.forEach((response) => { expect(response.statusCode).toBe(200); expect(JSON.parse(response.payload)).toEqual({ ...mockState, @@ -196,18 +198,20 @@ describe('Probe Routes Integration', () => { vi.mocked(container.probes.state).mockReturnValue(mockState); // Create array of 35 concurrent requests (default max_queue_depth is 20) - const requests = Array(35).fill(null).map(() => - app.inject({ - method: 'GET', - url: ProbeRoutes.STARTUP, - }) - ); + const requests = Array(35) + .fill(null) + .map(() => + app.inject({ + method: 'GET', + url: ProbeRoutes.STARTUP + }) + ); const responses = await Promise.all(requests); // Some requests should succeed and some should fail with 429 - const successCount = responses.filter(r => r.statusCode === 200).length; - const queueFullCount = responses.filter(r => r.statusCode === 429).length; + const successCount = responses.filter((r) => r.statusCode === 200).length; + const queueFullCount = responses.filter((r) => r.statusCode === 429).length; expect(successCount).toBeGreaterThan(0); expect(queueFullCount).toBeGreaterThan(0); @@ -222,7 +226,7 @@ describe('Probe Routes Integration', () => { const response = await app.inject({ method: 'GET', - url: ProbeRoutes.STARTUP, + url: ProbeRoutes.STARTUP }); expect(response.headers['content-type']).toMatch(/application\/json/); From 62cfb69085c2d67fdb6aad3f86311cd080156a1e Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Thu, 14 Nov 2024 16:28:08 +0200 Subject: [PATCH 296/298] Fix managed write checkpoint filtering. (#134) --- .../storage/mongo/MongoWriteCheckpointAPI.ts | 11 +++- packages/service-core/test/src/sync.test.ts | 63 ++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts b/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts index bbcebb0de..966eb77be 100644 --- a/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts +++ b/packages/service-core/src/storage/mongo/MongoWriteCheckpointAPI.ts @@ -111,9 +111,16 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI { } protected async lastManagedWriteCheckpoint(filters: ManagedWriteCheckpointFilters) { - const { user_id } = filters; + const { user_id, heads } = filters; + // TODO: support multiple heads when we need to support multiple connections + const lsn = heads['1']; + if (lsn == null) { + // Can happen if we haven't replicated anything yet. + return null; + } const lastWriteCheckpoint = await this.db.write_checkpoints.findOne({ - user_id: user_id + user_id: user_id, + 'lsns.1': { $lte: lsn } }); return lastWriteCheckpoint?.client_id ?? null; } diff --git a/packages/service-core/test/src/sync.test.ts b/packages/service-core/test/src/sync.test.ts index 64e463274..75e5f550f 100644 --- a/packages/service-core/test/src/sync.test.ts +++ b/packages/service-core/test/src/sync.test.ts @@ -1,6 +1,6 @@ import { SaveOperationTag } from '@/storage/storage-index.js'; import { RequestTracker } from '@/sync/RequestTracker.js'; -import { streamResponse } from '@/sync/sync.js'; +import { streamResponse, SyncStreamParameters } from '@/sync/sync.js'; import { StreamingSyncLine } from '@/util/protocol-types.js'; import { JSONBig } from '@powersync/service-jsonbig'; import { RequestParameters } from '@powersync/service-sync-rules'; @@ -381,6 +381,67 @@ function defineTests(factory: StorageFactory) { }) }); }); + + test('write checkpoint', async () => { + const f = await factory(); + + const syncRules = await f.updateSyncRules({ + content: BASIC_SYNC_RULES + }); + + const storage = f.getInstance(syncRules); + await storage.autoActivate(); + + await storage.startBatch(BATCH_OPTIONS, async (batch) => { + // <= the managed write checkpoint LSN below + await batch.commit('0/1'); + }); + + const checkpoint = await storage.createManagedWriteCheckpoint({ + user_id: 'test', + heads: { '1': '1/0' } + }); + + const params: SyncStreamParameters = { + storage: f, + params: { + buckets: [], + include_checksum: true, + raw_data: true + }, + parseOptions: PARSE_OPTIONS, + tracker, + syncParams: new RequestParameters({ sub: 'test' }, {}), + token: { sub: 'test', exp: Date.now() / 1000 + 10 } as any + }; + const stream1 = streamResponse(params); + const lines1 = await consumeCheckpointLines(stream1); + + // If write checkpoints are not correctly filtered, this may already + // contain the write checkpoint. + expect(lines1[0]).toMatchObject({ + checkpoint: expect.objectContaining({ + last_op_id: '0', + write_checkpoint: undefined + }) + }); + + await storage.startBatch(BATCH_OPTIONS, async (batch) => { + // must be >= the managed write checkpoint LSN + await batch.commit('1/0'); + }); + + // At this point the LSN has advanced, so the write checkpoint should be + // included in the next checkpoint message. + const stream2 = streamResponse(params); + const lines2 = await consumeCheckpointLines(stream2); + expect(lines2[0]).toMatchObject({ + checkpoint: expect.objectContaining({ + last_op_id: '0', + write_checkpoint: `${checkpoint}` + }) + }); + }); } /** From 1f224845c44a7e3d25685cf34803cbc5c4e298c1 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Tue, 19 Nov 2024 10:16:15 +0200 Subject: [PATCH 297/298] Support json_each in parameter queries (#126) * Proof-of-concept: json_each support in parameter queries. * Extract out json_each. * Fixes. * Tweak usesDangerousRequestParameters check. * Fix handling of nested json. * Fix tableName regression. * Add changeset. --------- Co-authored-by: stevensJourney <51082125+stevensJourney@users.noreply.github.com> --- .changeset/violet-garlics-know.md | 5 + packages/sync-rules/src/SqlParameterQuery.ts | 10 +- .../TableValuedFunctionSqlParameterQuery.ts | 196 ++++++++++++++++++ .../sync-rules/src/TableValuedFunctions.ts | 45 ++++ packages/sync-rules/src/request_functions.ts | 10 +- packages/sync-rules/src/sql_filters.ts | 21 +- packages/sync-rules/src/sql_functions.ts | 3 - packages/sync-rules/src/types.ts | 31 ++- packages/sync-rules/src/utils.ts | 5 +- .../src/table_valued_function_queries.test.ts | 146 +++++++++++++ 10 files changed, 448 insertions(+), 24 deletions(-) create mode 100644 .changeset/violet-garlics-know.md create mode 100644 packages/sync-rules/src/TableValuedFunctionSqlParameterQuery.ts create mode 100644 packages/sync-rules/src/TableValuedFunctions.ts create mode 100644 packages/sync-rules/test/src/table_valued_function_queries.test.ts diff --git a/.changeset/violet-garlics-know.md b/.changeset/violet-garlics-know.md new file mode 100644 index 000000000..cb973e611 --- /dev/null +++ b/.changeset/violet-garlics-know.md @@ -0,0 +1,5 @@ +--- +'@powersync/service-sync-rules': minor +--- + +Support json_each as a table-valued function. diff --git a/packages/sync-rules/src/SqlParameterQuery.ts b/packages/sync-rules/src/SqlParameterQuery.ts index 41c083d58..92c6cf527 100644 --- a/packages/sync-rules/src/SqlParameterQuery.ts +++ b/packages/sync-rules/src/SqlParameterQuery.ts @@ -24,6 +24,7 @@ import { } from './types.js'; import { filterJsonRow, getBucketId, isJsonValue, isSelectStatement } from './utils.js'; import { SyncRulesOptions } from './SqlSyncRules.js'; +import { TableValuedFunctionSqlParameterQuery } from './TableValuedFunctionSqlParameterQuery.js'; /** * Represents a parameter query, such as: @@ -57,11 +58,16 @@ export class SqlParameterQuery { rows.errors.push(...checkUnsupportedFeatures(sql, q)); - if (q.from.length != 1 || q.from[0].type != 'table') { + if (q.from.length != 1) { throw new SqlRuleError('Must SELECT from a single table', sql, q.from?.[0]._location); + } else if (q.from[0].type == 'call') { + const from = q.from[0]; + return TableValuedFunctionSqlParameterQuery.fromSql(descriptor_name, sql, from, q, options); + } else if (q.from[0].type == 'statement') { + throw new SqlRuleError('Subqueries are not supported yet', sql, q.from?.[0]._location); } - const tableRef = q.from?.[0].name; + const tableRef = q.from[0].name; if (tableRef?.name == null) { throw new SqlRuleError('Must SELECT from a single table', sql, q.from?.[0]._location); } diff --git a/packages/sync-rules/src/TableValuedFunctionSqlParameterQuery.ts b/packages/sync-rules/src/TableValuedFunctionSqlParameterQuery.ts new file mode 100644 index 000000000..5537fe2bb --- /dev/null +++ b/packages/sync-rules/src/TableValuedFunctionSqlParameterQuery.ts @@ -0,0 +1,196 @@ +import { FromCall, SelectedColumn, SelectFromStatement } from 'pgsql-ast-parser'; +import { SqlRuleError } from './errors.js'; +import { SqlTools } from './sql_filters.js'; +import { checkUnsupportedFeatures, isClauseError, isParameterValueClause, sqliteBool } from './sql_support.js'; +import { TABLE_VALUED_FUNCTIONS, TableValuedFunction } from './TableValuedFunctions.js'; +import { + ParameterValueClause, + ParameterValueSet, + QueryParseOptions, + RequestParameters, + SqliteJsonValue, + SqliteRow +} from './types.js'; +import { getBucketId, isJsonValue } from './utils.js'; + +/** + * Represents a parameter query using a table-valued function. + * + * Right now this only supports json_each: + * + * SELECT json_each.value as v FROM json_each(request.parameters() -> 'array') + * + * This can currently not be combined with parameter table queries or multiple table-valued functions. + */ +export class TableValuedFunctionSqlParameterQuery { + static fromSql( + descriptor_name: string, + sql: string, + call: FromCall, + q: SelectFromStatement, + options?: QueryParseOptions + ): TableValuedFunctionSqlParameterQuery { + const query = new TableValuedFunctionSqlParameterQuery(); + + query.errors.push(...checkUnsupportedFeatures(sql, q)); + + if (!(call.function.name in TABLE_VALUED_FUNCTIONS)) { + query.errors.push(new SqlRuleError(`Table-valued function ${call.function.name} is not defined.`, sql, call)); + return query; + } + + const callTable = call.alias?.name ?? call.function.name; + const callExpression = call.args[0]; + + const tools = new SqlTools({ + table: callTable, + parameter_tables: ['token_parameters', 'user_parameters', callTable], + supports_parameter_expressions: true, + sql + }); + const where = q.where; + + const filter = tools.compileParameterValueExtractor(where); + const callClause = tools.compileParameterValueExtractor(callExpression); + const columns = q.columns ?? []; + const bucket_parameters = columns.map((column) => tools.getOutputName(column)); + + query.sql = sql; + query.descriptor_name = descriptor_name; + query.bucket_parameters = bucket_parameters; + query.columns = columns; + query.tools = tools; + query.function = TABLE_VALUED_FUNCTIONS[call.function.name]!; + query.callTableName = callTable; + if (!isClauseError(callClause)) { + query.callClause = callClause; + } + if (!isClauseError(filter)) { + query.filter = filter; + } + + for (let column of columns) { + if (column.alias != null) { + tools.checkSpecificNameCase(column.alias); + } + const name = tools.getSpecificOutputName(column); + const extractor = tools.compileParameterValueExtractor(column.expr); + if (isClauseError(extractor)) { + // Error logged already + continue; + } + query.parameter_extractors[name] = extractor; + } + + query.errors.push(...tools.errors); + + if (query.usesDangerousRequestParameters && !options?.accept_potentially_dangerous_queries) { + let err = new SqlRuleError( + "Potentially dangerous query based on parameters set by the client. The client can send any value for these parameters so it's not a good place to do authorization.", + sql + ); + err.type = 'warning'; + query.errors.push(err); + } + return query; + } + + sql?: string; + columns?: SelectedColumn[]; + parameter_extractors: Record = {}; + descriptor_name?: string; + /** _Output_ bucket parameters */ + bucket_parameters?: string[]; + id?: string; + tools?: SqlTools; + + filter?: ParameterValueClause; + callClause?: ParameterValueClause; + function?: TableValuedFunction; + callTableName?: string; + + errors: SqlRuleError[] = []; + + getStaticBucketIds(parameters: RequestParameters): string[] { + if (this.filter == null || this.callClause == null) { + // Error in filter clause + return []; + } + + const valueString = this.callClause.lookupParameterValue(parameters); + const rows = this.function!.call([valueString]); + let total: string[] = []; + for (let row of rows) { + total.push(...this.getIndividualBucketIds(row, parameters)); + } + return total; + } + + private getIndividualBucketIds(row: SqliteRow, parameters: RequestParameters): string[] { + const mergedParams: ParameterValueSet = { + raw_token_payload: parameters.raw_token_payload, + raw_user_parameters: parameters.raw_user_parameters, + user_id: parameters.user_id, + lookup: (table, column) => { + if (table == this.callTableName) { + return row[column]!; + } else { + return parameters.lookup(table, column); + } + } + }; + const filterValue = this.filter!.lookupParameterValue(mergedParams); + if (sqliteBool(filterValue) === 0n) { + return []; + } + + let result: Record = {}; + for (let name of this.bucket_parameters!) { + const value = this.parameter_extractors[name].lookupParameterValue(mergedParams); + if (isJsonValue(value)) { + result[`bucket.${name}`] = value; + } else { + throw new Error(`Invalid parameter value: ${value}`); + } + } + + return [getBucketId(this.descriptor_name!, this.bucket_parameters!, result)]; + } + + get hasAuthenticatedBucketParameters(): boolean { + // select where request.jwt() ->> 'role' == 'authorized' + // we do not count this as a sufficient check + // const authenticatedFilter = this.filter!.usesAuthenticatedRequestParameters; + + // select request.user_id() as user_id + const authenticatedExtractor = + Object.values(this.parameter_extractors).find( + (clause) => isParameterValueClause(clause) && clause.usesAuthenticatedRequestParameters + ) != null; + + // select value from json_each(request.jwt() ->> 'project_ids') + const authenticatedArgument = this.callClause?.usesAuthenticatedRequestParameters ?? false; + + return authenticatedExtractor || authenticatedArgument; + } + + get usesUnauthenticatedRequestParameters(): boolean { + // select where request.parameters() ->> 'include_comments' + const unauthenticatedFilter = this.filter?.usesUnauthenticatedRequestParameters; + + // select request.parameters() ->> 'project_id' + const unauthenticatedExtractor = + Object.values(this.parameter_extractors).find( + (clause) => isParameterValueClause(clause) && clause.usesUnauthenticatedRequestParameters + ) != null; + + // select value from json_each(request.parameters() ->> 'project_ids') + const unauthenticatedArgument = this.callClause?.usesUnauthenticatedRequestParameters ?? false; + + return unauthenticatedFilter || unauthenticatedExtractor || unauthenticatedArgument; + } + + get usesDangerousRequestParameters() { + return this.usesUnauthenticatedRequestParameters && !this.hasAuthenticatedBucketParameters; + } +} diff --git a/packages/sync-rules/src/TableValuedFunctions.ts b/packages/sync-rules/src/TableValuedFunctions.ts new file mode 100644 index 000000000..e3e40165c --- /dev/null +++ b/packages/sync-rules/src/TableValuedFunctions.ts @@ -0,0 +1,45 @@ +import { SqliteJsonValue, SqliteRow, SqliteValue } from './types.js'; +import { jsonValueToSqlite } from './utils.js'; + +export interface TableValuedFunction { + readonly name: string; + call: (args: SqliteValue[]) => SqliteRow[]; + detail: string; + documentation: string; +} + +export const JSON_EACH: TableValuedFunction = { + name: 'json_each', + call(args: SqliteValue[]) { + if (args.length != 1) { + throw new Error(`json_each expects 1 argument, got ${args.length}`); + } + const valueString = args[0]; + if (valueString === null) { + return []; + } else if (typeof valueString !== 'string') { + throw new Error(`Expected json_each to be called with a string, got ${valueString}`); + } + let values: SqliteJsonValue[] = []; + try { + values = JSON.parse(valueString); + } catch (e) { + throw new Error('Expected JSON string'); + } + if (!Array.isArray(values)) { + throw new Error('Expected an array'); + } + + return values.map((v) => { + return { + value: jsonValueToSqlite(v) + }; + }); + }, + detail: 'Each element of a JSON array', + documentation: 'Returns each element of a JSON array as a separate row.' +}; + +export const TABLE_VALUED_FUNCTIONS: Record = { + json_each: JSON_EACH +}; diff --git a/packages/sync-rules/src/request_functions.ts b/packages/sync-rules/src/request_functions.ts index 941daeb98..b99c88911 100644 --- a/packages/sync-rules/src/request_functions.ts +++ b/packages/sync-rules/src/request_functions.ts @@ -1,9 +1,9 @@ import { ExpressionType } from './ExpressionType.js'; -import { RequestParameters, SqliteValue } from './types.js'; +import { ParameterValueSet, SqliteValue } from './types.js'; export interface SqlParameterFunction { readonly debugName: string; - call: (parameters: RequestParameters) => SqliteValue; + call: (parameters: ParameterValueSet) => SqliteValue; getReturnType(): ExpressionType; /** request.user_id(), request.jwt(), token_parameters.* */ usesAuthenticatedRequestParameters: boolean; @@ -15,7 +15,7 @@ export interface SqlParameterFunction { const request_parameters: SqlParameterFunction = { debugName: 'request.parameters', - call(parameters: RequestParameters) { + call(parameters: ParameterValueSet) { return parameters.raw_user_parameters; }, getReturnType() { @@ -30,7 +30,7 @@ const request_parameters: SqlParameterFunction = { const request_jwt: SqlParameterFunction = { debugName: 'request.jwt', - call(parameters: RequestParameters) { + call(parameters: ParameterValueSet) { return parameters.raw_token_payload; }, getReturnType() { @@ -44,7 +44,7 @@ const request_jwt: SqlParameterFunction = { const request_user_id: SqlParameterFunction = { debugName: 'request.user_id', - call(parameters: RequestParameters) { + call(parameters: ParameterValueSet) { return parameters.user_id; }, getReturnType() { diff --git a/packages/sync-rules/src/sql_filters.ts b/packages/sync-rules/src/sql_filters.ts index 5cf9961f3..14b41c824 100644 --- a/packages/sync-rules/src/sql_filters.ts +++ b/packages/sync-rules/src/sql_filters.ts @@ -500,7 +500,8 @@ export class SqlTools { if (expr.type != 'ref') { return false; } - return this.parameter_tables.includes(expr.table?.name ?? ''); + const tableName = expr.table?.name ?? this.default_table; + return this.parameter_tables.includes(tableName ?? ''); } /** @@ -585,13 +586,12 @@ export class SqlTools { } getParameterRefClause(expr: ExprRef): ParameterValueClause { - const table = expr.table!.name; + const table = (expr.table?.name ?? this.default_table)!; const column = expr.name; return { key: `${table}.${column}`, lookupParameterValue: (parameters) => { - const pt: SqliteJsonRow | undefined = (parameters as any)[table]; - return pt?.[column] ?? null; + return parameters.lookup(table, column); }, usesAuthenticatedRequestParameters: table == 'token_parameters', usesUnauthenticatedRequestParameters: table == 'user_parameters' @@ -607,18 +607,17 @@ export class SqlTools { * * Only "value" tables are supported here, not parameter values. */ - getTableName(ref: ExprRef) { + getTableName(ref: ExprRef): string { if (this.refHasSchema(ref)) { throw new SqlRuleError(`Specifying schema in column references is not supported`, this.sql, ref); } - if (ref.table?.name == null && this.default_table != null) { - return this.default_table; - } else if (this.value_tables.includes(ref.table?.name ?? '')) { - return ref.table!.name; + const tableName = ref.table?.name ?? this.default_table; + if (this.value_tables.includes(tableName ?? '')) { + return tableName!; } else if (ref.table?.name == null) { throw new SqlRuleError(`Table name required`, this.sql, ref); } else { - throw new SqlRuleError(`Undefined table ${ref.table?.name}`, this.sql, ref); + throw new SqlRuleError(`Undefined table ${tableName}`, this.sql, ref); } } @@ -750,6 +749,8 @@ function staticValue(expr: Expr): SqliteValue { return expr.value ? SQLITE_TRUE : SQLITE_FALSE; } else if (expr.type == 'integer') { return BigInt(expr.value); + } else if (expr.type == 'null') { + return null; } else { return (expr as any).value; } diff --git a/packages/sync-rules/src/sql_functions.ts b/packages/sync-rules/src/sql_functions.ts index fd6f71b91..62ebc7bae 100644 --- a/packages/sync-rules/src/sql_functions.ts +++ b/packages/sync-rules/src/sql_functions.ts @@ -823,9 +823,6 @@ export function jsonExtract(sourceValue: SqliteValue, path: SqliteValue, operato if (operator == '->') { // -> must always stringify return JSONBig.stringify(value); - } else if (typeof value == 'object' || Array.isArray(value)) { - // Objects and arrays must be stringified - return JSONBig.stringify(value); } else { // Plain scalar value - simple conversion. return jsonValueToSqlite(value as string | number | bigint | boolean | null); diff --git a/packages/sync-rules/src/types.ts b/packages/sync-rules/src/types.ts index 5aa8ea378..b506c09c4 100644 --- a/packages/sync-rules/src/types.ts +++ b/packages/sync-rules/src/types.ts @@ -73,7 +73,23 @@ export interface RequestJwtPayload { [key: string]: any; } -export class RequestParameters { +export interface ParameterValueSet { + lookup(table: string, column: string): SqliteValue; + + /** + * JSON string of raw request parameters. + */ + raw_user_parameters: string; + + /** + * JSON string of raw request parameters. + */ + raw_token_payload: string; + + user_id: string; +} + +export class RequestParameters implements ParameterValueSet { token_parameters: SqliteJsonRow; user_parameters: SqliteJsonRow; @@ -106,6 +122,15 @@ export class RequestParameters { this.raw_user_parameters = JSONBig.stringify(clientParameters); this.user_parameters = toSyncRulesParameters(clientParameters); } + + lookup(table: string, column: string): SqliteJsonValue { + if (table == 'token_parameters') { + return this.token_parameters[column]; + } else if (table == 'user_parameters') { + return this.user_parameters[column]; + } + throw new Error(`Unknown table: ${table}`); + } } /** @@ -200,7 +225,7 @@ export interface InputParameter { * * Only relevant for parameter queries. */ - parametersToLookupValue(parameters: RequestParameters): SqliteValue; + parametersToLookupValue(parameters: ParameterValueSet): SqliteValue; } export interface EvaluateRowOptions { @@ -276,7 +301,7 @@ export interface ParameterValueClause { * * Only relevant for parameter queries. */ - lookupParameterValue(parameters: RequestParameters): SqliteValue; + lookupParameterValue(parameters: ParameterValueSet): SqliteValue; } export interface QuerySchema { diff --git a/packages/sync-rules/src/utils.ts b/packages/sync-rules/src/utils.ts index 3cf15bc6e..06e34f540 100644 --- a/packages/sync-rules/src/utils.ts +++ b/packages/sync-rules/src/utils.ts @@ -51,9 +51,12 @@ export function filterJsonRow(data: SqliteRow): SqliteJsonRow { * * Types specifically not supported in output are `boolean` and `undefined`. */ -export function jsonValueToSqlite(value: null | undefined | string | number | bigint | boolean): SqliteValue { +export function jsonValueToSqlite(value: null | undefined | string | number | bigint | boolean | any): SqliteValue { if (typeof value == 'boolean') { return value ? SQLITE_TRUE : SQLITE_FALSE; + } else if (typeof value == 'object' || Array.isArray(value)) { + // Objects and arrays must be stringified + return JSONBig.stringify(value); } else { return value ?? null; } diff --git a/packages/sync-rules/test/src/table_valued_function_queries.test.ts b/packages/sync-rules/test/src/table_valued_function_queries.test.ts new file mode 100644 index 000000000..aaa33cac4 --- /dev/null +++ b/packages/sync-rules/test/src/table_valued_function_queries.test.ts @@ -0,0 +1,146 @@ +import { describe, expect, test } from 'vitest'; +import { RequestParameters, SqlParameterQuery } from '../../src/index.js'; +import { StaticSqlParameterQuery } from '../../src/StaticSqlParameterQuery.js'; +import { PARSE_OPTIONS } from './util.js'; + +describe('table-valued function queries', () => { + test('json_each(array param)', function () { + const sql = "SELECT json_each.value as v FROM json_each(request.parameters() -> 'array')"; + const query = SqlParameterQuery.fromSql('mybucket', sql, { + ...PARSE_OPTIONS, + accept_potentially_dangerous_queries: true + }) as StaticSqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.bucket_parameters).toEqual(['v']); + + expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, { array: [1, 2, 3] }))).toEqual([ + 'mybucket[1]', + 'mybucket[2]', + 'mybucket[3]' + ]); + }); + + test('json_each(static string)', function () { + const sql = `SELECT json_each.value as v FROM json_each('[1,2,3]')`; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.bucket_parameters).toEqual(['v']); + + expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, {}))).toEqual([ + 'mybucket[1]', + 'mybucket[2]', + 'mybucket[3]' + ]); + }); + + test('json_each(null)', function () { + const sql = `SELECT json_each.value as v FROM json_each(null)`; + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as StaticSqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.bucket_parameters).toEqual(['v']); + + expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, {}))).toEqual([]); + }); + + test('json_each with fn alias', function () { + const sql = "SELECT e.value FROM json_each(request.parameters() -> 'array') e"; + const query = SqlParameterQuery.fromSql('mybucket', sql, { + ...PARSE_OPTIONS, + accept_potentially_dangerous_queries: true + }) as StaticSqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.bucket_parameters).toEqual(['value']); + + expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, { array: [1, 2, 3] }))).toEqual([ + 'mybucket[1]', + 'mybucket[2]', + 'mybucket[3]' + ]); + }); + + test('json_each with direct value', function () { + const sql = "SELECT value FROM json_each(request.parameters() -> 'array')"; + const query = SqlParameterQuery.fromSql('mybucket', sql, { + ...PARSE_OPTIONS, + accept_potentially_dangerous_queries: true + }) as StaticSqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.bucket_parameters).toEqual(['value']); + + expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, { array: [1, 2, 3] }))).toEqual([ + 'mybucket[1]', + 'mybucket[2]', + 'mybucket[3]' + ]); + }); + + test('json_each in filters (1)', function () { + const sql = "SELECT value as v FROM json_each(request.parameters() -> 'array') e WHERE e.value >= 2"; + const query = SqlParameterQuery.fromSql('mybucket', sql, { + ...PARSE_OPTIONS, + accept_potentially_dangerous_queries: true + }) as StaticSqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.bucket_parameters).toEqual(['v']); + + expect(query.getStaticBucketIds(new RequestParameters({ sub: '' }, { array: [1, 2, 3] }))).toEqual([ + 'mybucket[2]', + 'mybucket[3]' + ]); + }); + + test('json_each with nested json', function () { + const sql = + "SELECT value ->> 'id' as project_id FROM json_each(request.jwt() -> 'projects') WHERE (value ->> 'role') = 'admin'"; + const query = SqlParameterQuery.fromSql('mybucket', sql, { + ...PARSE_OPTIONS, + accept_potentially_dangerous_queries: true + }) as StaticSqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.bucket_parameters).toEqual(['project_id']); + + expect( + query.getStaticBucketIds( + new RequestParameters( + { + sub: '', + projects: [ + { id: 1, role: 'admin' }, + { id: 2, role: 'user' } + ] + }, + {} + ) + ) + ).toEqual(['mybucket[1]']); + }); + + describe('dangerous queries', function () { + function testDangerousQuery(sql: string) { + test(sql, function () { + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; + expect(query.errors).toMatchObject([ + { + message: + "Potentially dangerous query based on parameters set by the client. The client can send any value for these parameters so it's not a good place to do authorization." + } + ]); + expect(query.usesDangerousRequestParameters).toEqual(true); + }); + } + function testSafeQuery(sql: string) { + test(sql, function () { + const query = SqlParameterQuery.fromSql('mybucket', sql, PARSE_OPTIONS) as SqlParameterQuery; + expect(query.errors).toEqual([]); + expect(query.usesDangerousRequestParameters).toEqual(false); + }); + } + + testSafeQuery('select value from json_each(request.user_id())'); + testDangerousQuery("select value from json_each(request.parameters() ->> 'project_ids')"); + testSafeQuery("select request.user_id() as user_id, value FROM json_each(request.parameters() ->> 'project_ids')"); + testSafeQuery( + "select request.parameters() ->> 'something' as something, value as project_id FROM json_each(request.jwt() ->> 'project_ids')" + ); + }); +}); From cd698acee4755fd068d41fa921135fd8d5fc9c19 Mon Sep 17 00:00:00 2001 From: stevensJourney <51082125+stevensJourney@users.noreply.github.com> Date: Tue, 26 Nov 2024 16:50:49 +0200 Subject: [PATCH 298/298] Modular Architecture Prep for Merge (#137) * remove postgres errors from mysql+mongodb rate limiters * use common typescript version * update changeset * update zongji package --- .changeset/tender-vans-impress.md | 2 +- modules/module-mongodb/package.json | 5 +- .../src/replication/MongoErrorRateLimiter.ts | 9 +-- modules/module-mysql/package.json | 7 +- .../src/replication/MySQLErrorRateLimiter.ts | 7 -- modules/module-postgres/package.json | 5 +- package.json | 2 + packages/rsocket-router/package.json | 4 +- packages/service-core/package.json | 5 +- .../src/routes/endpoints/sync-rules.ts | 3 +- pnpm-lock.yaml | 77 ++++--------------- service/package.json | 4 +- test-client/package.json | 3 +- 13 files changed, 29 insertions(+), 104 deletions(-) diff --git a/.changeset/tender-vans-impress.md b/.changeset/tender-vans-impress.md index 0da1c4848..106960369 100644 --- a/.changeset/tender-vans-impress.md +++ b/.changeset/tender-vans-impress.md @@ -4,7 +4,7 @@ '@powersync/lib-services-framework': minor '@powersync/service-jpgwire': minor '@powersync/service-types': minor -'@powersync/service-image': minor +'@powersync/service-image': major '@powersync/service-module-postgres': patch --- diff --git a/modules/module-mongodb/package.json b/modules/module-mongodb/package.json index d928ec513..8236e3b29 100644 --- a/modules/module-mongodb/package.json +++ b/modules/module-mongodb/package.json @@ -39,9 +39,6 @@ "uri-js": "^4.4.1" }, "devDependencies": { - "@types/uuid": "^9.0.4", - "typescript": "^5.2.2", - "vitest": "^2.1.1", - "vite-tsconfig-paths": "^4.3.2" + "@types/uuid": "^9.0.4" } } diff --git a/modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts b/modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts index bcf58db63..17b65c66c 100644 --- a/modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts +++ b/modules/module-mongodb/src/replication/MongoErrorRateLimiter.ts @@ -1,5 +1,5 @@ -import { setTimeout } from 'timers/promises'; import { ErrorRateLimiter } from '@powersync/service-core'; +import { setTimeout } from 'timers/promises'; export class MongoErrorRateLimiter implements ErrorRateLimiter { nextAllowed: number = Date.now(); @@ -27,13 +27,6 @@ export class MongoErrorRateLimiter implements ErrorRateLimiter { } else if (message.includes('ECONNREFUSED')) { // Could be fail2ban or similar this.setDelay(120_000); - } else if ( - message.includes('Unable to do postgres query on ended pool') || - message.includes('Postgres unexpectedly closed connection') - ) { - // Connection timed out - ignore / immediately retry - // We don't explicitly set the delay to 0, since there could have been another error that - // we need to respect. } else { this.setDelay(30_000); } diff --git a/modules/module-mysql/package.json b/modules/module-mysql/package.json index 18611a52c..584ff1fa9 100644 --- a/modules/module-mysql/package.json +++ b/modules/module-mysql/package.json @@ -33,7 +33,7 @@ "@powersync/service-sync-rules": "workspace:*", "@powersync/service-types": "workspace:*", "@powersync/service-jsonbig": "workspace:*", - "@powersync/mysql-zongji": "0.0.0-dev-20241031142605", + "@powersync/mysql-zongji": "^0.1.0", "semver": "^7.5.4", "async": "^3.2.4", "mysql2": "^3.11.0", @@ -44,9 +44,6 @@ "devDependencies": { "@types/semver": "^7.5.4", "@types/async": "^3.2.24", - "@types/uuid": "^9.0.4", - "typescript": "^5.5.4", - "vite-tsconfig-paths": "^4.3.2", - "vitest": "^2.1.1" + "@types/uuid": "^9.0.4" } } diff --git a/modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts b/modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts index aff96c3ab..8966cd201 100644 --- a/modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts +++ b/modules/module-mysql/src/replication/MySQLErrorRateLimiter.ts @@ -26,13 +26,6 @@ export class MySQLErrorRateLimiter implements ErrorRateLimiter { } else if (message.includes('ECONNREFUSED')) { // Could be fail2ban or similar this.setDelay(120_000); - } else if ( - message.includes('Unable to do postgres query on ended pool') || - message.includes('Postgres unexpectedly closed connection') - ) { - // Connection timed out - ignore / immediately retry - // We don't explicitly set the delay to 0, since there could have been another error that - // we need to respect. } else { this.setDelay(30_000); } diff --git a/modules/module-postgres/package.json b/modules/module-postgres/package.json index d42db9204..b34b8dd80 100644 --- a/modules/module-postgres/package.json +++ b/modules/module-postgres/package.json @@ -41,9 +41,6 @@ "uri-js": "^4.4.1" }, "devDependencies": { - "@types/uuid": "^9.0.4", - "typescript": "^5.6.2", - "vitest": "^2.1.1", - "vite-tsconfig-paths": "^4.3.2" + "@types/uuid": "^9.0.4" } } diff --git a/package.json b/package.json index 0f8fe506f..1846299d9 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "tsc-watch": "^6.2.0", "ts-node-dev": "^2.0.0", "typescript": "^5.6.2", + "vite-tsconfig-paths": "^4.3.2", + "vitest": "^2.1.1", "ws": "^8.2.3" } } diff --git a/packages/rsocket-router/package.json b/packages/rsocket-router/package.json index 97747a9f5..039abc5d3 100644 --- a/packages/rsocket-router/package.json +++ b/packages/rsocket-router/package.json @@ -28,8 +28,6 @@ "@types/uuid": "^9.0.4", "@types/ws": "~8.2.0", "bson": "^6.6.0", - "rsocket-websocket-client": "1.0.0-alpha.3", - "typescript": "^5.6.2", - "vitest": "^2.1.1" + "rsocket-websocket-client": "1.0.0-alpha.3" } } diff --git a/packages/service-core/package.json b/packages/service-core/package.json index 54ee47f2b..df1e5976a 100644 --- a/packages/service-core/package.json +++ b/packages/service-core/package.json @@ -50,9 +50,6 @@ "@types/lodash": "^4.17.5", "@types/uuid": "^9.0.4", "fastify": "4.23.2", - "fastify-plugin": "^4.5.1", - "typescript": "^5.6.2", - "vite-tsconfig-paths": "^4.3.2", - "vitest": "^2.1.1" + "fastify-plugin": "^4.5.1" } } diff --git a/packages/service-core/src/routes/endpoints/sync-rules.ts b/packages/service-core/src/routes/endpoints/sync-rules.ts index 70e6127d6..c902171cf 100644 --- a/packages/service-core/src/routes/endpoints/sync-rules.ts +++ b/packages/service-core/src/routes/endpoints/sync-rules.ts @@ -3,10 +3,9 @@ import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules'; import type { FastifyPluginAsync } from 'fastify'; import * as t from 'ts-codec'; -import * as system from '../../system/system-index.js'; +import { RouteAPI } from '../../api/RouteAPI.js'; import { authApi } from '../auth.js'; import { routeDefinition } from '../router.js'; -import { RouteAPI } from '../../api/RouteAPI.js'; const DeploySyncRulesRequest = t.object({ content: t.string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd892d264..012efe2c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,12 @@ importers: typescript: specifier: ^5.6.2 version: 5.6.2 + vite-tsconfig-paths: + specifier: ^4.3.2 + version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) + vitest: + specifier: ^2.1.1 + version: 2.1.1(@types/node@22.5.5) ws: specifier: ^8.2.3 version: 8.18.0 @@ -127,15 +133,6 @@ importers: '@types/uuid': specifier: ^9.0.4 version: 9.0.8 - typescript: - specifier: ^5.2.2 - version: 5.6.2 - vite-tsconfig-paths: - specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) - vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5) modules/module-mysql: dependencies: @@ -143,8 +140,8 @@ importers: specifier: workspace:* version: link:../../libs/lib-services '@powersync/mysql-zongji': - specifier: 0.0.0-dev-20241031142605 - version: 0.0.0-dev-20241031142605 + specifier: ^0.1.0 + version: 0.1.0 '@powersync/service-core': specifier: workspace:* version: link:../../packages/service-core @@ -185,15 +182,6 @@ importers: '@types/uuid': specifier: ^9.0.4 version: 9.0.8 - typescript: - specifier: ^5.5.4 - version: 5.6.2 - vite-tsconfig-paths: - specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) - vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5) modules/module-postgres: dependencies: @@ -234,15 +222,6 @@ importers: '@types/uuid': specifier: ^9.0.4 version: 9.0.8 - typescript: - specifier: ^5.6.2 - version: 5.6.2 - vite-tsconfig-paths: - specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) - vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5) packages/jpgwire: dependencies: @@ -298,12 +277,6 @@ importers: rsocket-websocket-client: specifier: 1.0.0-alpha.3 version: 1.0.0-alpha.3 - typescript: - specifier: ^5.6.2 - version: 5.6.2 - vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5) packages/service-core: dependencies: @@ -407,15 +380,6 @@ importers: fastify-plugin: specifier: ^4.5.1 version: 4.5.1 - typescript: - specifier: ^5.6.2 - version: 5.6.2 - vite-tsconfig-paths: - specifier: ^4.3.2 - version: 4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)) - vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5) packages/sync-rules: dependencies: @@ -565,12 +529,6 @@ importers: ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@22.5.5)(typescript@5.6.2) - typescript: - specifier: ^5.6.2 - version: 5.6.2 - vitest: - specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5) test-client: dependencies: @@ -590,9 +548,6 @@ importers: '@types/node': specifier: ^22.5.5 version: 22.5.5 - typescript: - specifier: ^5.6.2 - version: 5.6.2 packages: @@ -1181,8 +1136,8 @@ packages: resolution: {integrity: sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==} engines: {node: '>=12'} - '@powersync/mysql-zongji@0.0.0-dev-20241031142605': - resolution: {integrity: sha512-LATx5xfJjXcbvW3s9yCy2tlhhsgOuaUoUk0/EH67sgbTQQFZTfBVO/2gzdJH5GvGgONdqoPBQ7skWNwBF/dEXw==} + '@powersync/mysql-zongji@0.1.0': + resolution: {integrity: sha512-2GjOxVws+wtbb+xFUJe4Ozzkp/f0Gsna0fje9art76bmz6yfLCW4K3Mf2/M310xMnAIp8eP9hsJ6DYwwZCo1RA==} engines: {node: '>=20.0.0'} '@prisma/instrumentation@5.16.1': @@ -4582,7 +4537,7 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 - '@powersync/mysql-zongji@0.0.0-dev-20241031142605': + '@powersync/mysql-zongji@0.1.0': dependencies: '@vlasky/mysql': 2.18.6 big-integer: 1.6.51 @@ -4675,7 +4630,7 @@ snapshots: '@opentelemetry/semantic-conventions': 1.25.1 '@prisma/instrumentation': 5.16.1 '@sentry/core': 8.17.0 - '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1) + '@sentry/opentelemetry': 8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/semantic-conventions@1.25.1) '@sentry/types': 8.17.0 '@sentry/utils': 8.17.0 optionalDependencies: @@ -4683,7 +4638,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.25.1)': + '@sentry/opentelemetry@8.17.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.6.0))(@opentelemetry/sdk-trace-base@1.25.1(@opentelemetry/api@1.6.0))(@opentelemetry/semantic-conventions@1.25.1)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) @@ -4880,7 +4835,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -7044,7 +6999,7 @@ snapshots: tuf-js@1.1.7: dependencies: '@tufjs/models': 1.0.4 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7 make-fetch-happen: 11.1.1 transitivePeerDependencies: - supports-color @@ -7143,7 +7098,7 @@ snapshots: vite-tsconfig-paths@4.3.2(typescript@5.6.2)(vite@5.3.3(@types/node@22.5.5)): dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.1.1(typescript@5.6.2) optionalDependencies: diff --git a/service/package.json b/service/package.json index e54948ba6..10f636faa 100644 --- a/service/package.json +++ b/service/package.json @@ -48,8 +48,6 @@ "copyfiles": "^2.4.1", "nodemon": "^3.0.1", "npm-check-updates": "^16.14.4", - "ts-node": "^10.9.1", - "typescript": "^5.6.2", - "vitest": "^2.1.1" + "ts-node": "^10.9.1" } } diff --git a/test-client/package.json b/test-client/package.json index ddf8e5e60..4d5f3d9d4 100644 --- a/test-client/package.json +++ b/test-client/package.json @@ -20,7 +20,6 @@ "yaml": "^2.5.0" }, "devDependencies": { - "@types/node": "^22.5.5", - "typescript": "^5.6.2" + "@types/node": "^22.5.5" } }