diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17056ee961..9b324c5ddb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,12 @@ jobs: name: Get all the necessary dependencies runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - run: ls -lah + - name: Inject slug/short variables + uses: rlespinasse/github-slug-action@v4 - uses: erlef/setup-beam@v1 with: otp-version: "24.3" @@ -24,51 +29,75 @@ jobs: - uses: webfactory/ssh-agent@v0.5.4 with: ssh-private-key: ${{ secrets.KEY_TO_ACCESS_SATELLITE_JS_PROTO }} + + - name: Try to reuse cached deps + id: cache-deps + uses: actions/cache@v3 + with: + path: deps + key: ${{ runner.os }}-mixdeps-${{ hashFiles('**/mix.lock') }} + - run: make deps pretest_compile - - uses: actions/cache@v3 + + - name: Cache compiled code + id: cache-build + uses: actions/cache@v3 with: - path: | - deps - _build - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} - restore-keys: | - ${{ runner.os }}-mix + path: _build + key: ${{ runner.os }}-mixbuild-${{ env.GITHUB_SHA_SHORT }} tests: name: elixir tests and dialyzer runs-on: ubuntu-latest needs: deps steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Inject slug/short variables + uses: rlespinasse/github-slug-action@v4 - uses: erlef/setup-beam@v1 with: otp-version: "24.3" elixir-version: "1.13" - name: Prepare auxillary services run: make start_dev_env - - uses: actions/cache@v3 - with: - path: | - deps - _build - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} - restore-keys: | - ${{ runner.os }}-mix + + - name: Try to reuse cached deps + id: cache-deps + uses: actions/cache@v3 + with: + path: deps + key: ${{ runner.os }}-mixdeps-${{ hashFiles('**/mix.lock') }} + + - name: Cache compiled code + id: cache-build + uses: actions/cache@v3 + with: + path: _build + key: ${{ runner.os }}-mixbuild-${{ env.GITHUB_SHA_SHORT }} + - uses: webfactory/ssh-agent@v0.5.4 with: ssh-private-key: ${{ secrets.KEY_TO_ACCESS_SATELLITE_JS_PROTO }} + - run: make pretest_compile + - run: mix coveralls.lcov env: MIX_ENV: test + - name: Report code coverage uses: zgosalvez/github-actions-report-lcov@v1 with: coverage-files: cover/lcov.info artifact-name: code-coverage-report github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Dialyzer run: make dialyzer + - run: make check-format e2e_tests: @@ -78,7 +107,10 @@ jobs: env: VAXINE_IMAGE: europe-docker.pkg.dev/vaxine/vaxine-io/vaxine:latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Inject slug/short variables uses: rlespinasse/github-slug-action@v4 - name: Log in to the Container registry @@ -87,19 +119,30 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/cache@v3 - with: - path: | - deps - _build - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} - restore-keys: | - ${{ runner.os }}-mix + - uses: erlef/setup-beam@v1 + with: + otp-version: "24.3" + elixir-version: "1.13" + + - name: Try to reuse cached deps + id: cache-deps + uses: actions/cache@v3 + with: + path: deps + key: ${{ runner.os }}-mixdeps-${{ hashFiles('**/mix.lock') }} + + - name: Cache compiled code + id: cache-build + uses: actions/cache@v3 + with: + path: _build + key: ${{ runner.os }}-mixbuild-${{ env.GITHUB_SHA_SHORT }} + - run: make docker-build-ci env: ELECTRIC_IMAGE_NAME: ghcr.io/${{ github.repository }}/electric - ELECTRIC_IMAGE_TAG: ${{ env.GITHUB_HEAD_REF_SLUG || 'main' }} - + ELECTRIC_IMAGE_TAG: ${{ env.GITHUB_SHA_SHORT || 'main' }} + - run: make pretest_compile - run: make lux working-directory: integration_tests @@ -112,6 +155,9 @@ jobs: - run: make test id: tests working-directory: integration_tests + env: + ELECTRIC_IMAGE_NAME: ghcr.io/${{ github.repository }}/electric + ELECTRIC_IMAGE_TAG: ${{ env.GITHUB_SHA_SHORT || 'main' }} - name: Upload lux logs uses: actions/upload-artifact@v3 diff --git a/Dockerfile b/Dockerfile index ed8f405d3b..ed769bb71f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,9 @@ COPY Makefile /app/ RUN make build_tools COPY mix.* /app/ +COPY deps /app/deps/ +RUN ls -lah +RUN ls -lah deps RUN make deps COPY config /app/config/ COPY lib /app/lib/ diff --git a/Makefile b/Makefile index c7c02198ee..c51c55aa66 100644 --- a/Makefile +++ b/Makefile @@ -52,11 +52,7 @@ docker-build: docker build -t electric:local-build . docker-build-ci: - docker pull ${ELECTRIC_IMAGE_NAME}:${ELECTRIC_IMAGE_TAG} || true - docker pull ${ELECTRIC_IMAGE_NAME}:latest || true - docker build --cache-from ${ELECTRIC_IMAGE_NAME}:${ELECTRIC_IMAGE_TAG} \ - --cache-from ${ELECTRIC_IMAGE_NAME}:latest \ - -t ${ELECTRIC_IMAGE_NAME}:${ELECTRIC_IMAGE_TAG} \ + docker build -t ${ELECTRIC_IMAGE_NAME}:${ELECTRIC_IMAGE_TAG} \ -t electric:local-build . docker push ${ELECTRIC_IMAGE_NAME}:${ELECTRIC_IMAGE_TAG} diff --git a/integration_tests/Makefile b/integration_tests/Makefile index b99583a979..6a2661a4c7 100644 --- a/integration_tests/Makefile +++ b/integration_tests/Makefile @@ -2,16 +2,25 @@ include common.mk LUX_DIRS= \ single_dc \ - multi_dc + multi_dc \ + migrations -test: lux build pull +deps: lux + make -C satellite_client build + +test: deps build pull ${LUX} ${LUX_DIRS} build: $(foreach dir, $(LUX_DIRS),make -C ${dir} build;) pull: - docker-compose -f services_templates.yaml pull sysbench postgresql vaxine + docker-compose -f services_templates.yaml pull \ + vaxine \ + sysbench \ + elixir_client \ + postgresql \ + satellite_client clean: $(foreach dir, $(LUX_DIRS),make -C ${dir} clean;) diff --git a/integration_tests/common.mk b/integration_tests/common.mk index 5c46acf48e..a14be0c280 100644 --- a/integration_tests/common.mk +++ b/integration_tests/common.mk @@ -20,6 +20,12 @@ else export SYSBENCH_IMAGE?=${DOCKER_REGISTRY}/sysbench:latest endif +ifeq (${ELECTRIC_IMAGE_NAME}${ELECTRIC_IMAGE_TAG},) + export ELECTRIC_IMAGE=electric:local-build +else + export ELECTRIC_IMAGE=${ELECTRIC_IMAGE_NAME}:${ELECTRIC_IMAGE_TAG} +endif + lux: ${LUX} ${LUX}: @@ -40,18 +46,20 @@ SYSBENCH_COMMIT:=df89d34c410a2277e19f77e47e535d0890b2029b start_dev_env: docker-compose -f ${DOCKER_COMPOSE_FILE} up -d pg_1 pg_2 pg_3 -ifndef LUX_EXTRA_LOGS -export VAXINE_VOLUME=. +ifdef LUX_EXTRA_LOGS +export VAXINE_VOLUME=${LUX_EXTRA_LOGS} +export SATELLITE_DB_PATH=${LUX_EXTRA_LOGS} else -export VAXINE_VOLUME:=${LUX_EXTRA_LOGS} +export SATELLITE_DB_PATH=. +export VAXINE_VOLUME=. endif start_vaxine_%: mkdir -p ${VAXINE_VOLUME}/vaxine_$* - docker-compose -f ${DOCKER_COMPOSE_FILE} up --no-color --no-log-prefix vaxine_$* + docker-compose --verbose -f ${DOCKER_COMPOSE_FILE} up --no-color --no-log-prefix vaxine_$* start_electric_%: - docker-compose -f ${DOCKER_COMPOSE_FILE} up --no-color --no-log-prefix electric_$* + docker-compose --verbose -f ${DOCKER_COMPOSE_FILE} up --no-color --no-log-prefix electric_$* stop_dev_env: docker-compose -f ${DOCKER_COMPOSE_FILE} down @@ -66,7 +74,13 @@ start_elixir_test: docker-compose -f ${DOCKER_COMPOSE_FILE} run \ --rm --entrypoint=/bin/bash \ --workdir=${PROJECT_ROOT} \ - test_client + elixir_client_1 + +start_satellite_client_%: + docker-compose -f ${DOCKER_COMPOSE_FILE} run \ + --rm --entrypoint=/bin/bash \ + --workdir=${PROJECT_ROOT}/integration_tests/satellite_client \ + satellite_client_$* VAXINE_BRANCH?=main vaxine: @@ -93,11 +107,17 @@ docker-psql-%: docker-attach-%: docker-compose -f ${DOCKER_COMPOSE_FILE} exec $* bash -echo: - echo ${UID}:${GID} +DOCKER_WORKDIR?=${PROJECT_ROOT} docker-start-clean-%: - docker-compose -f ${DOCKER_COMPOSE_FILE} run --rm --entrypoint=/bin/sh $* + docker-compose -f ${DOCKER_COMPOSE_FILE} run --rm --entrypoint=/bin/sh \ + --workdir=${DOCKER_WORKDIR} \ + $* + +docker-make: + docker-compose -f ${DOCKER_COMPOSE_FILE} run --rm \ + --workdir=${DOCKER_WORKDIR} ${MK_DOCKER} \ + make ${MK_TARGET} single_test: ${LUX} ${TEST} diff --git a/integration_tests/migrations/Makefile b/integration_tests/migrations/Makefile index 0795e724c1..f0496b577f 100644 --- a/integration_tests/migrations/Makefile +++ b/integration_tests/migrations/Makefile @@ -2,6 +2,11 @@ # @version 0.1 include ../common.mk +export MIGRATION_DIRS=${PROJECT_ROOT}/integration_tests/migrations/migrations + +ELECTRIC_SQL_TAG=v0.0.1-initial +ELECTRIC_SQL_CLI_URL=https://github.com/electric-sql/cli/releases/download/${ELECTRIC_SQL_TAG}/electricsql_cli_${ELECTRIC_SQL_TAG}_linux + DOCKER_COMPOSE_FILE=compose.yaml test: diff --git a/integration_tests/migrations/bidirectional.lux b/integration_tests/migrations/bidirectional.lux index 79a3c225e4..bdfaf815ca 100644 --- a/integration_tests/migrations/bidirectional.lux +++ b/integration_tests/migrations/bidirectional.lux @@ -28,25 +28,33 @@ !curl http://localhost:5050/api/migrations/postgres_2 ?{"applied_at":"[0-9-T:\.Z]{0,50}","hash":"initial","origin":"postgres_2","vsn":"1"} +[shell electric_migrate] + !curl -v -X PUT http://localhost:5050/api/migrations/postgres_1 \ + -H 'Content-Type: application/json' -d '{"vsn":"1669232573_init"}' + ?HTTP/1.1 200 OK + !curl -v -X PUT http://localhost:5050/api/migrations/postgres_2 \ + -H 'Content-Type: application/json' -d '{"vsn":"1669232573_init"}' + ?HTTP/1.1 200 OK + [shell pg_1] [invoke log "Insert data into postgres_1"] - !INSERT INTO entries (id, content) VALUES ('${pg_id}', 'hello from pg_1'); + !INSERT INTO public.items (id, content) VALUES ('${pg_id}', 'hello from pg_1'); ?$psql [shell pg_2] [invoke log "Validate that postgresql_2 have received an update"] - [invoke wait-for "SELECT * FROM entries;" "hello from pg_1" 10 ${psql}] + [invoke wait-for "SELECT * FROM public.items;" "hello from pg_1" 10 ${psql}] [shell electric_migrate] [invoke log "Migrate postgres_1 instance 1 -> 2"] !curl -v -X PUT http://localhost:5050/api/migrations/postgres_1 \ - -H 'Content-Type: application/json' -d '{"vsn":"2"}' + -H 'Content-Type: application/json' -d '{"vsn":"1669232634_add_column"}' ?HTTP/1.1 200 OK [shell pg_1] [invoke log "Insert data that postgres_2 is not able to store"] - !UPDATE entries \ + !UPDATE public.items \ SET content = 'hej pg_1 new data', \ added_column = 'new data added' \ WHERE id = '${pg_id}'; @@ -54,20 +62,20 @@ [shell pg_2] [invoke log "Validate that postgresql_2 have received an update"] - [invoke wait-for "SELECT * FROM entries;" "hej pg_1 new data" 10 ${psql}] + [invoke wait-for "SELECT * FROM public.items;" "hej pg_1 new data" 10 ${psql}] [invoke log "Store data in old schema on postgresql_2"] - !UPDATE entries \ + !UPDATE public.items \ SET content = 'hej pg_2 old data' \ WHERE id = '${pg_id}'; ?$psql -[shell electric] +[shell electric_1] ?"content" => "hej pg_2 old data" [shell pg_1] [invoke log "Read data update"] - [invoke wait-for "SELECT * FROM entries;" "hej pg_2 old data" 10 ${psql}] + [invoke wait-for "SELECT * FROM public.items;" "hej pg_2 old data" 10 ${psql}] [cleanup] [invoke teardown] diff --git a/integration_tests/migrations/compose.yaml b/integration_tests/migrations/compose.yaml index 2a6daf3159..933eab7fa1 100644 --- a/integration_tests/migrations/compose.yaml +++ b/integration_tests/migrations/compose.yaml @@ -20,7 +20,7 @@ services: service: electric volumes: - ./electric.exs:/app/releases/0.1.0/runtime.exs:ro - - ./migration_schemas:/migration_schemas:ro + - ./migrations/:/migrations:ro ports: - "5050:5050" - "5133:5133" @@ -35,7 +35,7 @@ services: service: electric volumes: - ./electric_b.exs:/app/releases/0.1.0/runtime.exs:ro - - ./migration_schemas:/migration_schemas:ro + - ./migrations:/migrations:ro ports: - "5051:5050" - "5134:5133" @@ -52,6 +52,8 @@ services: extends: file: ../services_templates.yaml service: postgresql + volumes: + - ./init-user-db.sh:/docker-entrypoint-initdb.d/init-user-db.sh:ro ports: - "54321:5432" @@ -59,6 +61,8 @@ services: extends: file: ../services_templates.yaml service: postgresql + volumes: + - ./init-user-db.sh:/docker-entrypoint-initdb.d/init-user-db.sh:ro ports: - "54322:5432" @@ -66,5 +70,25 @@ services: extends: file: ../services_templates.yaml service: postgresql + volumes: + - ./init-user-db.sh:/docker-entrypoint-initdb.d/init-user-db.sh:ro ports: - "54323:5432" + + satellite_client_1: + extends: + file: ../services_templates.yaml + service: satellite_client + environment: + MIGRATION_DIRS: ${MIGRATION_DIRS} + volumes: + - ${MIGRATION_DIRS}:${MIGRATION_DIRS}:ro + + satellite_client_2: + extends: + file: ../services_templates.yaml + service: satellite_client + environment: + MIGRATION_DIRS: ${MIGRATION_DIRS} + volumes: + - ${MIGRATION_DIRS}:${MIGRATION_DIRS}:ro diff --git a/integration_tests/migrations/electric.exs b/integration_tests/migrations/electric.exs index e30c038260..63e02f31e9 100644 --- a/integration_tests/migrations/electric.exs +++ b/integration_tests/migrations/electric.exs @@ -70,5 +70,12 @@ config :electric, Electric.Replication.SQConnectors, config :logger, backends: [:console], level: :debug +config :electric, + instance_id: "instance-a.region-1.test.electric-db", + regional_id: "region-1.test.electric-db" + +config :electric, Electric.Satellite.Auth, provider: {Electric.Satellite.Auth.Insecure, []} + config :electric, Electric.Migrations, - dir: "/migration_schemas/" + dir: "/migrations", + migration_file_name_suffix: "/postgres.sql" diff --git a/integration_tests/migrations/electric_b.exs b/integration_tests/migrations/electric_b.exs index a4a43c218e..23427205fb 100644 --- a/integration_tests/migrations/electric_b.exs +++ b/integration_tests/migrations/electric_b.exs @@ -41,5 +41,12 @@ config :electric, Electric.Replication.SQConnectors, config :logger, backends: [:console], level: :debug +config :electric, + instance_id: "instance-a.region-1.test.electric-db", + regional_id: "region-1.test.electric-db" + +config :electric, Electric.Satellite.Auth, provider: {Electric.Satellite.Auth.Insecure, []} + config :electric, Electric.Migrations, - dir: "/migration_schemas/" + dir: "/migrations", + migration_file_name_suffix: "/postgres.sql" diff --git a/integration_tests/migrations/init-user-db.sh b/integration_tests/migrations/init-user-db.sh new file mode 100755 index 0000000000..5b26b73888 --- /dev/null +++ b/integration_tests/migrations/init-user-db.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" "dbname=$POSTGRES_DB replication=database" <<-EOSQL + CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + + CREATE SCHEMA electric; + CREATE TABLE electric.migrations ( + id SERIAL PRIMARY KEY, + version VARCHAR(64) NOT NULL, + hash VARCHAR(64) NOT NULL, + applied_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + UNIQUE(version) + ); + + INSERT INTO electric.migrations (version, hash) VALUES ('1', 'initial'); + + CREATE PUBLICATION all_tables FOR ALL TABLES; + CREATE_REPLICATION_SLOT all_changes LOGICAL pgoutput NOEXPORT_SNAPSHOT; +EOSQL diff --git a/integration_tests/migrations/migration_schemas/1/postgres.sql b/integration_tests/migrations/migration_schemas/1/postgres.sql deleted file mode 100644 index cafb12d3c7..0000000000 --- a/integration_tests/migrations/migration_schemas/1/postgres.sql +++ /dev/null @@ -1,17 +0,0 @@ -CREATE TABLE entries ( - id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), - content VARCHAR(64) NOT NULL, - content_b VARCHAR(64) -); - -CREATE SCHEMA electric; -CREATE TABLE electric.migrations ( - id SERIAL PRIMARY KEY, - version VARCHAR(64) NOT NULL, - hash VARCHAR(64) NOT NULL, - applied_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - UNIQUE(version) - ); -INSERT INTO electric.migrations (version, hash) VALUES ('1', 'initial'); - -ALTER TABLE entries REPLICA IDENTITY FULL; diff --git a/integration_tests/migrations/migration_schemas/2/postgres.sql b/integration_tests/migrations/migration_schemas/2/postgres.sql deleted file mode 100644 index 9056a4510b..0000000000 --- a/integration_tests/migrations/migration_schemas/2/postgres.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE entries ADD added_column varchar(64); diff --git a/integration_tests/migrations/migrations/1669232573_init/migration.sql b/integration_tests/migrations/migrations/1669232573_init/migration.sql new file mode 100644 index 0000000000..17d9e26ef5 --- /dev/null +++ b/integration_tests/migrations/migrations/1669232573_init/migration.sql @@ -0,0 +1,11 @@ +/* +ElectricDB Migration +{"metadata": {"title": "init", "name": "1669232573_init"}} +*/ + +CREATE TABLE IF NOT EXISTS public.items ( + id TEXT PRIMARY KEY, + content TEXT NOT NULL, + content_b TEXT +); + diff --git a/integration_tests/migrations/migrations/1669232573_init/postgres.sql b/integration_tests/migrations/migrations/1669232573_init/postgres.sql new file mode 100644 index 0000000000..b64924df98 --- /dev/null +++ b/integration_tests/migrations/migrations/1669232573_init/postgres.sql @@ -0,0 +1,11 @@ +/* +Manually generated +*/ +CREATE SCHEMA IF NOT EXISTS public; +CREATE TABLE public.items ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + content VARCHAR(64) DEFAULT '' NOT NULL, + content_b VARCHAR(64) DEFAULT '' +); + +ALTER TABLE public.items REPLICA IDENTITY FULL; diff --git a/integration_tests/migrations/migrations/1669232634_add_column/migration.sql b/integration_tests/migrations/migrations/1669232634_add_column/migration.sql new file mode 100644 index 0000000000..1df8e32576 --- /dev/null +++ b/integration_tests/migrations/migrations/1669232634_add_column/migration.sql @@ -0,0 +1,6 @@ +/* +ElectricDB Migration +{"metadata": {"title": "add_column", "name": "1669232634_add_column"}} +*/ + +ALTER TABLE main.items ADD added_column TEXT; diff --git a/integration_tests/migrations/migrations/1669232634_add_column/postgres.sql b/integration_tests/migrations/migrations/1669232634_add_column/postgres.sql new file mode 100644 index 0000000000..3c90fcdb27 --- /dev/null +++ b/integration_tests/migrations/migrations/1669232634_add_column/postgres.sql @@ -0,0 +1,5 @@ +/* +Manually generated +*/ + +ALTER TABLE public.items ADD added_column varchar(64) DEFAULT ''; diff --git a/integration_tests/migrations/migrations/index.js b/integration_tests/migrations/migrations/index.js new file mode 100644 index 0000000000..c5035274f2 --- /dev/null +++ b/integration_tests/migrations/migrations/index.js @@ -0,0 +1,48 @@ +{ + "migrations": [ + { + "body": [ + "CREATE TABLE IF NOT EXISTS items (\n id TEXT PRIMARY KEY,\n content TEXT NOT NULL,\n content_b TEXT DEFAULT ''\n);", + "-- The ops log table\nCREATE TABLE IF NOT EXISTS _electric_oplog (\n rowid INTEGER PRIMARY KEY AUTOINCREMENT,\n namespace String NOT NULL,\n tablename String NOT NULL,\n optype String NOT NULL,\n primaryKey String NOT NULL,\n newRow String,\n oldRow String,\n timestamp TEXT\n);", + "-- Somewhere to keep our metadata\nCREATE TABLE IF NOT EXISTS _electric_meta (\n key TEXT PRIMARY KEY,\n value BLOB\n);", + "-- Somewhere to track migrations\nCREATE TABLE IF NOT EXISTS _electric_migrations (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL UNIQUE,\n sha256 TEXT NOT NULL,\n applied_at TEXT NOT NULL\n);", + "-- Initialisation of the metadata table\nINSERT INTO _electric_meta (key, value) VALUES ('compensations', 0), ('lastAckdRowId','0'), ('lastSentRowId', '0'), ('lsn', 'MA=='), ('clientId', '');", + "-- These are toggles for turning the triggers on and off\nDROP TABLE IF EXISTS _electric_trigger_settings;", + "CREATE TABLE _electric_trigger_settings(tablename STRING PRIMARY KEY, flag INTEGER);", + "INSERT INTO _electric_trigger_settings(tablename,flag) VALUES ('main.items', 1);", + "-- Ensures primary key is immutable\nDROP TRIGGER IF EXISTS update_ensure_main_items_primarykey;", + "CREATE TRIGGER update_ensure_main_items_primarykey\n BEFORE UPDATE ON main.items\nBEGIN\n SELECT\n CASE\n WHEN old.id != new.id THEN\n RAISE (ABORT,'cannot change the value of column id as it belongs to the primary key')\n END;\nEND;", + "-- Triggers that add INSERT, UPDATE, DELETE operation to the _opslog table\n\nDROP TRIGGER IF EXISTS insert_main_items_into_oplog;", + "CREATE TRIGGER insert_main_items_into_oplog\n AFTER INSERT ON main.items\n WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == 'main.items')\nBEGIN\n INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)\n VALUES ('main', 'items', 'INSERT', json_object('id', new.id), json_object('id', new.id, 'content', new.content, 'content_b', new.content_b), NULL, NULL);\nEND;", + "DROP TRIGGER IF EXISTS update_main_items_into_oplog;", + "CREATE TRIGGER update_main_items_into_oplog\n AFTER UPDATE ON main.items\n WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == 'main.items')\nBEGIN\n INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)\n VALUES ('main', 'items', 'UPDATE', json_object('id', new.id), json_object('id', new.id, 'content', new.content, 'content_b', new.content_b), json_object('id', old.id, 'content', old.content, 'content_b', old.content_b), NULL);\nEND;", + "DROP TRIGGER IF EXISTS delete_main_items_into_oplog;", + "CREATE TRIGGER delete_main_items_into_oplog\n AFTER DELETE ON main.items\n WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == 'main.items')\nBEGIN\n INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)\n VALUES ('main', 'items', 'DELETE', json_object('id', old.id), NULL, json_object('id', old.id, 'content', old.content, 'content_b', old.content_b), NULL);\nEND;" + ], + "encoding": "escaped", + "name": "1669232573_init", + "sha256": "71c9fb0baab30a8268c5208bff0feaf1170ff65b1554f278e5bd1871a4fedbe3", + "title": "init" + }, + { + "body": [ + "ALTER TABLE items ADD added_column TEXT DEFAULT '';", + "-- These are toggles for turning the triggers on and off\nDROP TABLE IF EXISTS _electric_trigger_settings;", + "CREATE TABLE _electric_trigger_settings(tablename STRING PRIMARY KEY, flag INTEGER);", + "INSERT INTO _electric_trigger_settings(tablename,flag) VALUES ('main.items', 1);", + "-- Ensures primary key is immutable\nDROP TRIGGER IF EXISTS update_ensure_main_items_primarykey;", + "CREATE TRIGGER update_ensure_main_items_primarykey\n BEFORE UPDATE ON main.items\nBEGIN\n SELECT\n CASE\n WHEN old.id != new.id THEN\n RAISE (ABORT,'cannot change the value of column id as it belongs to the primary key')\n END;\nEND;", + "-- Triggers that add INSERT, UPDATE, DELETE operation to the _opslog table\n\nDROP TRIGGER IF EXISTS insert_main_items_into_oplog;", + "CREATE TRIGGER insert_main_items_into_oplog\n AFTER INSERT ON main.items\n WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == 'main.items')\nBEGIN\n INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)\n VALUES ('main', 'items', 'INSERT', json_object('id', new.id), json_object('id', new.id, 'content', new.content, 'content_b', new.content_b, 'added_column', new.added_column), NULL, NULL);\nEND;", + "DROP TRIGGER IF EXISTS update_main_items_into_oplog;", + "CREATE TRIGGER update_main_items_into_oplog\n AFTER UPDATE ON main.items\n WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == 'main.items')\nBEGIN\n INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)\n VALUES ('main', 'items', 'UPDATE', json_object('id', new.id), json_object('id', new.id, 'content', new.content, 'content_b', new.content_b, 'added_column', new.added_column), json_object('id', old.id, 'content', old.content, 'content_b', old.content_b, 'added_column', old.added_column), NULL);\nEND;", + "DROP TRIGGER IF EXISTS delete_main_items_into_oplog;", + "CREATE TRIGGER delete_main_items_into_oplog\n AFTER DELETE ON main.items\n WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == 'main.items')\nBEGIN\n INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)\n VALUES ('main', 'items', 'DELETE', json_object('id', old.id), NULL, json_object('id', old.id, 'content', old.content, 'content_b', old.content_b, 'added_column', old.added_column), NULL);\nEND;" + ], + "encoding": "escaped", + "name": "1669232634_add_column", + "sha256": "bbc8419bca8eaeb3dc34251145022dd074129c50b488c0079a1b94cbf3346264", + "title": "add_column" + } + ] +} diff --git a/integration_tests/migrations/satellite_test.lux b/integration_tests/migrations/satellite_test.lux new file mode 100644 index 0000000000..42d6f99444 --- /dev/null +++ b/integration_tests/migrations/satellite_test.lux @@ -0,0 +1,139 @@ +[doc Sanity check test that electric migrates PG instance] + +[global fail_pattern=[Ee][Rr][Rr][Oo][Rr]] +[global psql=electric] +[global node=>] +[global pg_id=f989b58b-980d-4d3c-b178-adb6ae8222f1] + +[include shared.luxinc] + +[macro connect_to_electric host port] + !client = await import('./dist/client.js') + ?$node + !migrations = await client.read_migrations(process.env.MIGRATION_DIRS + "/index.js") + ?$node + !db = await client.open_db( process.env.SATELLITE_DB_PATH + "/$LUX_SHELLNAME", \ + "$host", \ + $port, \ + migrations) + ?$node +[endmacro] + +[macro setup_client satellite_number electric] + [invoke start_satellite $satellite_number] + !yarn node + ?$node + [invoke connect_to_electric $electric 5133] +[endmacro] + +[macro migrate electric port pg vsn] + [invoke log "Migrate ${pg} on ${electric} to version: ${vsn}"] + [shell electric_migrate] + !curl -v -X PUT http://localhost:${port}/api/migrations/${pg} \ + -H 'Content-Type: application/json' -d '{"vsn":"${vsn}"}' + ?HTTP/1.1 200 OK + + [shell ${electric}] + ?.* origin=${pg} .* \[notice\] ready to migrate to version: ${vsn} + ?.* origin=${pg} .* \[notice\] successfull migration to version: ${vsn} + ?.* Successfully initialized origin ${pg} +[endmacro] + +[macro node_await_get match] + [invoke wait-for "client.get_items(db)" "${match}" 10 $node] +[endmacro] + +[macro node_await_insert keys] + !client.insert_item(db, ${keys}) + ?$node +[endmacro] + + +#=============================================================================== +# Setup test +#=============================================================================== +# +[invoke setup] + +[shell electric_1] + -$fail_pattern +[shell electric_2] + -$fail_pattern + +#=============================================================================== +# Beginning of the actual test +#=============================================================================== + +[invoke log "Migrate Electric to the latest version"] + +[invoke migrate electric_1 5050 postgres_1 1669232573_init] +[invoke migrate electric_1 5050 postgres_2 1669232573_init] +[invoke migrate electric_1 5050 postgres_1 1669232634_add_column] +[invoke migrate electric_1 5050 postgres_2 1669232634_add_column] +[invoke migrate electric_2 5051 postgres_3 1669232573_init] +[invoke migrate electric_2 5051 postgres_3 1669232634_add_column] + +[invoke log "Electric migrated"] + +[shell satellite_1] + -$fail_pattern + [invoke setup_client 1 "electric_1"] + +[shell satellite_2] + -$fail_pattern + [invoke setup_client 2 "electric_2"] + +[invoke log "Both satellites connected"] + +#=============================================================================== +# Verify that PG -> Satellite works +#============================================================================== + +[global pg_id1=f989b58b-980d-4d3c-b178-adb6ae8222f1] +[global pg_id2=a7a7d9be-a51c-4f86-a4ff-dfdc0c016fe2] +[global pg_id3=4bc27df5-0a7e-4695-b386-e39ddab231a4] + +[shell pg_1] + [invoke log "Insert data into postgres_1"] + !INSERT INTO public.items (id, content) VALUES ('${pg_id1}', 'hello from pg_1'); + ?$psql + +[shell satellite_1] + [invoke node_await_get ${pg_id1}] + +[shell satellite_2] + [invoke node_await_get ${pg_id1}] + +[shell pg_3] + [invoke log "Insert data into postgres_3"] + !INSERT INTO public.items (id, content) VALUES ('${pg_id3}', 'hello from pg_3'); + ?$psql + +[shell satellite_1] + [invoke node_await_get ${pg_id3}] + +[shell satellite_2] + [invoke node_await_get ${pg_id3}] + +#=============================================================================== +# Verify that Satellite -> PG works +#=============================================================================== + +[shell satellite_1] + [invoke node_await_insert "['hello from satellite_1']"] + [invoke node_await_get ${pg_id1}] + + +[shell satellite_2] + [invoke node_await_insert "['hello from satellite_2']"] + +[loop pg pg_1 pg_2 pg_3] + [shell ${pg}] + [invoke log "Verify that ${pg} have received data from both Satellites"] + [invoke wait-for "SELECT * FROM public.items;" "hello from satellite_1" 10 ${psql}] + [invoke wait-for "SELECT * FROM public.items;" "hello from satellite_2" 10 ${psql}] +[endloop] + + +[cleanup] + [invoke teardown] diff --git a/integration_tests/migrations/shared.luxinc b/integration_tests/migrations/shared.luxinc index 17565fb133..a3e342ef20 100644 --- a/integration_tests/migrations/shared.luxinc +++ b/integration_tests/migrations/shared.luxinc @@ -1,5 +1,7 @@ [include ../common.luxinc] +[global dprompt=\w+@\w+:(\S+)\#] + [macro setup_pg_and_vaxine] [shell start_env] [progress start development environment] @@ -12,6 +14,9 @@ [shell pg_2] [invoke start_psql pg_2] +[shell pg_3] + [invoke start_psql pg_3] + [shell vaxine] !make start_vaxine_1 ?(application: vx_server)|(vx_server started) @@ -23,17 +28,26 @@ [endmacro] [macro setup_rest] -[shell electric] +[shell electric_1] [timeout 10] !make start_electric_1 ?START_REPLICATION SLOT ?START_REPLICATION SLOT +[shell electric_2] + [timeout 10] + !make start_electric_2 + ?START_REPLICATION SLOT [shell start_env] [progress setup finished] +[endmacro] +[macro start_satellite name] +[shell satellite_${name}] + !make start_satellite_client_${name} [endmacro] + [macro teardown] [progress stop development environment] !make stop_dev_env diff --git a/integration_tests/migrations/simple_test.lux b/integration_tests/migrations/simple_test.lux index c3f65221de..c5b9cd85ce 100644 --- a/integration_tests/migrations/simple_test.lux +++ b/integration_tests/migrations/simple_test.lux @@ -22,34 +22,34 @@ [shell electric_migrate] !curl -v -X PUT http://localhost:5050/api/migrations/postgres_1 \ - -H 'Content-Type: application/json' -d '{"vsn":"2"}' + -H 'Content-Type: application/json' -d '{"vsn":"1669232573_init"}' ?HTTP/1.1 200 OK -[shell electric] - ?.* origin=postgres_1 .* \[notice\] ready to migrate to version: 2 - ?.* origin=postgres_1 .* \[notice\] successfull migration to version: 2 +[shell electric_1] + ?.* origin=postgres_1 .* \[notice\] ready to migrate to version: 1669232573_init + ?.* origin=postgres_1 .* \[notice\] successfull migration to version: 1669232573_init ?.* Successfully initialized origin postgres_1 [shell electric_migrate] !curl -v -X PUT http://localhost:5050/api/migrations/postgres_2 \ - -H 'Content-Type: application/json' -d '{"vsn":"2"}' + -H 'Content-Type: application/json' -d '{"vsn":"1669232573_init"}' ?HTTP/1.1 200 OK -[shell electric] - ?.* origin=postgres_2 .* \[notice\] ready to migrate to version: 2 - ?.* origin=postgres_2 .* \[notice\] successfull migration to version: 2 +[shell electric_1] + ?.* origin=postgres_2 .* \[notice\] ready to migrate to version: 1669232573_init + ?.* origin=postgres_2 .* \[notice\] successfull migration to version: 1669232573_init ?.* Successfully initialized origin postgres_2 [shell electric_curl] !curl http://localhost:5050/api/migrations/postgres_1 - ?{"applied_at":"[0-9-T:\.Z]{0,50}","hash":"[0-9a-zA-Z]{32}","origin":"postgres_1","vsn":"2"} + ?{"applied_at":"[0-9-T:\.Z]{0,50}","hash":"[0-9a-zA-Z]{32}","origin":"postgres_1","vsn":"1669232573_init"} !curl http://localhost:5050/api/migrations/postgres_2 - ?{"applied_at":"[0-9-T:\.Z]{0,50}","hash":"[0-9a-zA-Z]{32}","origin":"postgres_2","vsn":"2"} + ?{"applied_at":"[0-9-T:\.Z]{0,50}","hash":"[0-9a-zA-Z]{32}","origin":"postgres_2","vsn":"1669232573_init"} [shell electric_migrate] !curl -v -X PUT http://localhost:5050/api/migrations/postgres_1 \ - -H 'Content-Type: application/json' -d '{"vsn":"2"}' + -H 'Content-Type: application/json' -d '{"vsn":"1669232573_init"}' ?HTTP/1.1 403 Forbidden ?:already_migrated diff --git a/integration_tests/multi_dc/Makefile b/integration_tests/multi_dc/Makefile index 750ae748b5..7a8073dad9 100644 --- a/integration_tests/multi_dc/Makefile +++ b/integration_tests/multi_dc/Makefile @@ -2,7 +2,7 @@ # @version 0.1 include ../common.mk -DOCKER_COMPOSE_FILE=multi-dc.yaml +DOCKER_COMPOSE_FILE=compose.yaml test: build ${LUX} *.lux diff --git a/integration_tests/multi_dc/multi-dc.yaml b/integration_tests/multi_dc/compose.yaml similarity index 100% rename from integration_tests/multi_dc/multi-dc.yaml rename to integration_tests/multi_dc/compose.yaml diff --git a/integration_tests/satellite_client/.gitignore b/integration_tests/satellite_client/.gitignore new file mode 100644 index 0000000000..f7cad3629c --- /dev/null +++ b/integration_tests/satellite_client/.gitignore @@ -0,0 +1,5 @@ +.bash_history +.npm/ +.yarnrc +dist/ +node_modules/ diff --git a/integration_tests/satellite_client/Makefile b/integration_tests/satellite_client/Makefile new file mode 100644 index 0000000000..8638d01654 --- /dev/null +++ b/integration_tests/satellite_client/Makefile @@ -0,0 +1,22 @@ +include ../common.mk + +NODEJS_DOCKER=satellite_client +DOCKER_COMPOSE_FILE=../services_templates.yaml +DOCKER_WORKDIR=${PROJECT_ROOT}/integration_tests/satellite_client + +node_modules: + yarn install --frozen-lockfile --no-default-rc + +# By default we would like to build in docker, as we intend +# to run tests with Satellite in it +build: + make docker-make MK_TARGET=local-build MK_DOCKER=${NODEJS_DOCKER} + +local-build: node_modules + yarn build + +run_node: + yarn node + +clean: + rm -rf node_modules diff --git a/integration_tests/satellite_client/package.json b/integration_tests/satellite_client/package.json new file mode 100644 index 0000000000..1d8f6b534b --- /dev/null +++ b/integration_tests/satellite_client/package.json @@ -0,0 +1,31 @@ +{ + "name": "satellite_client", + "version": "1.0.0", + "main": "dist/client.js", + "license": "MIT", + "type": "module", + "scripts": { + "dev": "tsmodule dev", + "build": "rm -rf ./dist && tsmodule build", + "run_node": "node dist/client.js", + "test": "ava", + "pretest": "yarn build", + "prepublishOnly": "yarn test", + "lint": "eslint src --fix" + }, + "dependencies": { + "electric-sql": "https://github.com/electric-sql/typescript-client.git", + "tslint": "^6.1.3", + "tslint-config-prettier": "^1.18.0", + "uuid": "^9.0.0" + }, + "devDependencies": { + "@tsmodule/tsmodule": "^40.0.17", + "@types/better-sqlite3": "^7.6.2", + "@types/live-server": "^1.2.1", + "@types/node": "^16.9.1", + "better-sqlite3": "^7.6.2", + "exponential-backoff": "^3.1.0", + "typescript": "^4.4.3" + } +} diff --git a/integration_tests/satellite_client/src/client.ts b/integration_tests/satellite_client/src/client.ts new file mode 100644 index 0000000000..308df7d796 --- /dev/null +++ b/integration_tests/satellite_client/src/client.ts @@ -0,0 +1,72 @@ +import Database from 'better-sqlite3' + +import { electrify, ElectrifiedDatabase} from 'electric-sql/node' +import * as fs from 'fs'; +import {v4 as uuidv4} from 'uuid'; + +export const read_migrations = (migration_file: string) => { + const data = fs.readFileSync(migration_file) + const json_data = JSON.parse(data); + return json_data.migrations +} + +export const open_db = (name: string, + address: string, + port: number, + migrations: any + ) => { + //= () => Promise { + const original = new Database(name) + const config = { + app: 'satellite_client', + migrations: migrations, + replication: { + address: address, + port: port + }, + token: "token" + } + return electrify(original, config) +} + +export const set_subscribers = (db: ElectrifiedDatabase) => { + db.electric.notifier.subscribeToAuthStateChanges((x) => { + console.log("auth state changes: ") + console.log(x) + }) + db.electric.notifier.subscribeToPotentialDataChanges((x) => { + console.log("potential data change: ") + console.log(x) + }) + db.electric.notifier.subscribeToDataChanges((x) => { + console.log("data changes: ") + console.log(JSON.stringify(x)) + }) +} + +export const get_items = (db: ElectrifiedDatabase) => { + const stmt = db.prepare('SELECT * FROM main.items;') + return stmt.all([]) +} + +export const insert_item = (db: ElectrifiedDatabase, keys: [ string ] ) => { + const st = db.prepare('INSERT INTO main.items (id, content) VALUES ( @uuid, @key )'); + for ( var key of keys ) { + let myuuid = uuidv4(); + st.run({key: key, uuid: myuuid}); + } +} + +export const delete_item = (db: ElectrifiedDatabase, keys: [ string ]) => { + const st = db.prepare("DELETE FROM main.items WHERE content = ?") + for ( var key of keys ) { + st.run(key); + } +} + +export const run = (db: ElectrifiedDatabase) => { + const stmt = db.prepare('select 1') + return db.transaction((bind) => { + stmt.run(bind); + }); +} diff --git a/integration_tests/satellite_client/tsconfig.json b/integration_tests/satellite_client/tsconfig.json new file mode 100644 index 0000000000..c3356e3c78 --- /dev/null +++ b/integration_tests/satellite_client/tsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "allowJs": true, + "allowSyntheticDefaultImports": true, + "alwaysStrict": true, + "checkJs": false, + "declaration": true, + "emitDeclarationOnly": true, + "forceConsistentCasingInFileNames": true, + "incremental": false, + "isolatedModules": true, + "jsx": "preserve", + "lib": ["ESNext", "DOM"], + "module": "ESNext", + "moduleResolution": "node", + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "outDir": "dist", + "resolveJsonModule": true, + "rootDir": "", + "skipLibCheck": true, + "strict": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "target": "ESNext" + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/integration_tests/satellite_client/yarn.lock b/integration_tests/satellite_client/yarn.lock new file mode 100644 index 0000000000..2d163b0d74 --- /dev/null +++ b/integration_tests/satellite_client/yarn.lock @@ -0,0 +1,841 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.0.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/helper-validator-identifier@^7.18.6": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@esbuild/android-arm@0.15.12": + version "0.15.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.12.tgz#e548b10a5e55b9e10537a049ebf0bc72c453b769" + integrity sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA== + +"@esbuild/linux-loong64@0.15.12": + version "0.15.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz#475b33a2631a3d8ca8aa95ee127f9a61d95bf9c1" + integrity sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + +"@tsmodule/tsmodule@^40.0.17": + version "40.19.0" + resolved "https://registry.yarnpkg.com/@tsmodule/tsmodule/-/tsmodule-40.19.0.tgz#ef04279441a23c84364fee37b4a19054462e0838" + integrity sha512-xmESPMTrRsFldpv6NK+tT0zMXAvD2vQO9vxoC6gXKg09DDU/fDeSqWRdPvtULsDrvPQHgTn75SWYN715AQQX4w== + dependencies: + esbuild "0.15.12" + typescript "^4.8.4" + +"@types/better-sqlite3@^7.6.2": + version "7.6.2" + resolved "https://registry.yarnpkg.com/@types/better-sqlite3/-/better-sqlite3-7.6.2.tgz#6ffa6379df60f30beb79d997102ff43412cde55c" + integrity sha512-RgmaapusqTq6IMAr4McMyAsC6RshYTCjXCnzwVV59WctUxC8bNPyUfT9t5F81lKcU41lLurhjqjoMHfauzfqGg== + dependencies: + "@types/node" "*" + +"@types/live-server@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/live-server/-/live-server-1.2.1.tgz#4d6588e1b888b415ed5fe8480224dcd225bc9eba" + integrity sha512-Yind497JdcZT8L9FF7u73nq44KmamiDitsZJEwrAi/pgBhFHThNvtR+2Z/YGNSMjyUoDBFdvhVSQmod06yd1Ng== + +"@types/node@*", "@types/node@>=13.7.0": + version "18.11.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" + integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== + +"@types/node@^16.9.1": + version "16.18.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.3.tgz#d7f7ba828ad9e540270f01ce00d391c54e6e0abc" + integrity sha512-jh6m0QUhIRcZpNv7Z/rpN+ZWXOicUUQbSoWks7Htkbb9IjFQj4kzcX/xFCkjstCj5flMsN8FiSvt+q+Tcs4Llg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-64@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" + integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +better-sqlite3@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/better-sqlite3/-/better-sqlite3-7.6.2.tgz#47cd8cad5b9573cace535f950ac321166bc31384" + integrity sha512-S5zIU1Hink2AH4xPsN0W43T1/AJ5jrPh7Oy07ocuW/AKYYY02GWzz9NH0nbSMn/gw6fDZ5jZ1QsHt1BXAwJ6Lg== + dependencies: + bindings "^1.5.0" + prebuild-install "^7.1.0" + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ== + +chalk@^2.0.0, chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +commander@^2.12.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + +detect-libc@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" + integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +"electric-sql@https://github.com/electric-sql/typescript-client.git": + version "0.3.0" + resolved "https://github.com/electric-sql/typescript-client.git#8eb20c640142533babc7972ca20d8d6e0bb3ee5d" + dependencies: + base-64 "^1.0.0" + events "^3.3.0" + exponential-backoff "^3.1.0" + fastestsmallesttextencoderdecoder "^1.0.22" + frame-stream "^3.0.1" + lodash.throttle "^4.1.1" + long "^5.2.0" + protobufjs "^7.1.1" + sqlite-parser "^1.0.1" + uuid "^9.0.0" + walkjs "^3.2.4" + ws "^8.8.1" + +end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +esbuild-android-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz#5e8151d5f0a748c71a7fbea8cee844ccf008e6fc" + integrity sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q== + +esbuild-android-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz#5ee72a6baa444bc96ffcb472a3ba4aba2cc80666" + integrity sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA== + +esbuild-darwin-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz#70047007e093fa1b3ba7ef86f9b3fa63db51fe25" + integrity sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q== + +esbuild-darwin-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz#41c951f23d9a70539bcca552bae6e5196696ae04" + integrity sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw== + +esbuild-freebsd-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz#a761b5afd12bbedb7d56c612e9cfa4d2711f33f0" + integrity sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw== + +esbuild-freebsd-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz#6b0839d4d58deabc6cbd96276eb8cbf94f7f335e" + integrity sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g== + +esbuild-linux-32@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz#bd50bfe22514d434d97d5150977496e2631345b4" + integrity sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA== + +esbuild-linux-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz#074bb2b194bf658245f8490f29c01ffcdfa8c931" + integrity sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA== + +esbuild-linux-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz#3bf789c4396dc032875a122988efd6f3733f28f5" + integrity sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ== + +esbuild-linux-arm@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz#b91b5a8d470053f6c2c9c8a5e67ec10a71fe4a67" + integrity sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A== + +esbuild-linux-mips64le@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz#2fb54099ada3c950a7536dfcba46172c61e580e2" + integrity sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A== + +esbuild-linux-ppc64le@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz#9e3b8c09825fb27886249dfb3142a750df29a1b7" + integrity sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg== + +esbuild-linux-riscv64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz#923d0f5b6e12ee0d1fe116b08e4ae4478fe40693" + integrity sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA== + +esbuild-linux-s390x@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz#3b1620220482b96266a0c6d9d471d451a1eab86f" + integrity sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww== + +esbuild-netbsd-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz#276730f80da646859b1af5a740e7802d8cd73e42" + integrity sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w== + +esbuild-openbsd-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz#bd0eea1dd2ca0722ed489d88c26714034429f8ae" + integrity sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw== + +esbuild-sunos-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz#5e56bf9eef3b2d92360d6d29dcde7722acbecc9e" + integrity sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg== + +esbuild-windows-32@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz#a4f1a301c1a2fa7701fcd4b91ef9d2620cf293d0" + integrity sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw== + +esbuild-windows-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz#bc2b467541744d653be4fe64eaa9b0dbbf8e07f6" + integrity sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA== + +esbuild-windows-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz#9a7266404334a86be800957eaee9aef94c3df328" + integrity sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA== + +esbuild@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.12.tgz#6c8e22d6d3b7430d165c33848298d3fc9a1f251c" + integrity sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng== + optionalDependencies: + "@esbuild/android-arm" "0.15.12" + "@esbuild/linux-loong64" "0.15.12" + esbuild-android-64 "0.15.12" + esbuild-android-arm64 "0.15.12" + esbuild-darwin-64 "0.15.12" + esbuild-darwin-arm64 "0.15.12" + esbuild-freebsd-64 "0.15.12" + esbuild-freebsd-arm64 "0.15.12" + esbuild-linux-32 "0.15.12" + esbuild-linux-64 "0.15.12" + esbuild-linux-arm "0.15.12" + esbuild-linux-arm64 "0.15.12" + esbuild-linux-mips64le "0.15.12" + esbuild-linux-ppc64le "0.15.12" + esbuild-linux-riscv64 "0.15.12" + esbuild-linux-s390x "0.15.12" + esbuild-netbsd-64 "0.15.12" + esbuild-openbsd-64 "0.15.12" + esbuild-sunos-64 "0.15.12" + esbuild-windows-32 "0.15.12" + esbuild-windows-64 "0.15.12" + esbuild-windows-arm64 "0.15.12" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + +exponential-backoff@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.0.tgz#9409c7e579131f8bd4b32d7d8094a911040f2e68" + integrity sha512-oBuz5SYz5zzyuHINoe9ooePwSu0xApKWgeNzok4hZ5YKXFh9zrQBEM15CXqoZkJJPuI2ArvqjPQd8UKJA753XA== + +fastestsmallesttextencoderdecoder@^1.0.22: + version "1.0.22" + resolved "https://registry.yarnpkg.com/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz#59b47e7b965f45258629cc6c127bf783281c5e93" + integrity sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +frame-stream@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/frame-stream/-/frame-stream-3.0.1.tgz#e58f6ec5dca6fd9f043ca1c077c16bb17ab89183" + integrity sha512-Fu8Cdbt2hHfb7wp2HBG5AOfMO5qaglHoJuoiEoQKHS+mZtO/IsMiac3wEQtBVDmOLVmCmDeoutXbrfPlpwMiqg== + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + +glob@^7.1.1: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3, inherits@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== + +long@^5.0.0, long@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" + integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + +mkdirp@^0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + +node-abi@^3.3.0: + version "3.28.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.28.0.tgz#b0df8b317e1c4f2f323756c5fc8ffccc5bca4718" + integrity sha512-fRlDb4I0eLcQeUvGq7IY3xHrSb0c9ummdvDSYWfT9+LKP+3jCKw/tKoqaM7r1BAoiAC6GtwyjaGnOz6B3OtF+A== + dependencies: + semver "^7.3.5" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +prebuild-install@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + +protobufjs@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.1.2.tgz#a0cf6aeaf82f5625bffcf5a38b7cd2a7de05890c" + integrity sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^3.1.1, readable-stream@^3.4.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +resolve@^1.3.2: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^5.3.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@^7.3.5: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +sqlite-parser@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sqlite-parser/-/sqlite-parser-1.0.1.tgz#110183f2682f04ac6c7d8ad09c44446ef976d5ec" + integrity sha512-/es+YmgQG+VFbwAQD0Nd0Mdzgky8rW3M85zcy2+Vtk3Sj5ydaMl/lopWPehsjsByGw/swVuXSBeMJFh47doRUw== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tar-fs@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-stream@^2.1.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tslib@^1.13.0, tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslint-config-prettier@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" + integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== + +tslint@^6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" + integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.3" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.13.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== + dependencies: + safe-buffer "^5.0.1" + +typescript@^4.3.5, typescript@^4.4.3, typescript@^4.8.4: + version "4.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" + integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +walkjs@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/walkjs/-/walkjs-3.2.4.tgz#58c197226c55676e4f8115684da83a53abf8b530" + integrity sha512-pBwB51XwCQr6YdfL2cD7xY/oAir9mNF0OrReFYxs3BC1Ft4tDdXGJOUVODthqYkomh3siIbbm/i6bg0pbGUM6g== + dependencies: + typescript "^4.3.5" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.8.1: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/integration_tests/services_templates.yaml b/integration_tests/services_templates.yaml index ead09ee511..fddacd4d50 100644 --- a/integration_tests/services_templates.yaml +++ b/integration_tests/services_templates.yaml @@ -20,13 +20,13 @@ services: privileged: true electric: - image: electric:local-build + image: "${ELECTRIC_IMAGE}" privileged: true sysbench: image: "${SYSBENCH_IMAGE}" - elixir_test: + elixir_client: image: "hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}" user: "${UID}:${GID}" environment: @@ -63,3 +63,22 @@ services: test: ["CMD-SHELL", "pg_isready -U electric"] extra_hosts: - "host.docker.internal:host-gateway" + + satellite_client: + image: "node:18" + user: "${UID}:${GID}" + environment: + HOME: ${PROJECT_ROOT}/integration_tests/satellite_client + SATELLITE_DB_PATH: "${SATELLITE_DB_PATH}" + NODE_DISABLE_COLORS: 1 + TERM: dumb + UID: ${UID} + GID: ${GID} + volumes: + # Deliberately mount folder below root, so that yarn/npm would not try + # to access .git directory, as they do not handle git ls-remote properly + - ${PROJECT_ROOT}/integration_tests:${PROJECT_ROOT}/integration_tests:rw + - /etc/group:/etc/group:ro + - /etc/passwd:/etc/passwd:ro + - /etc/shadow:/etc/shadow:ro + privileged: true diff --git a/integration_tests/single_dc/compose.yaml b/integration_tests/single_dc/compose.yaml index fda4e24926..b9997231fc 100644 --- a/integration_tests/single_dc/compose.yaml +++ b/integration_tests/single_dc/compose.yaml @@ -46,10 +46,10 @@ services: file: ../services_templates.yaml service: sysbench - test_client: + elixir_client_1: extends: file: ../services_templates.yaml - service: elixir_test + service: elixir_client pg_1: extends: diff --git a/lib/electric/migration_utils.ex b/lib/electric/migration_utils.ex index a3ff494a60..0b59daf2a4 100644 --- a/lib/electric/migration_utils.ex +++ b/lib/electric/migration_utils.ex @@ -1,4 +1,6 @@ defmodule Electric.Migration.Utils do + require Logger + @type vsn() :: String.t() @spec read_migration_file(vsn()) :: {:ok, binary} | {:error, term} @@ -10,6 +12,7 @@ defmodule Electric.Migration.Utils do File.read(file) false -> + Logger.warn("migration not found: #{file}") {:error, :vsn_not_found} end end diff --git a/lib/electric/postgres/postgres_manager.ex b/lib/electric/postgres/postgres_manager.ex index 4ed6849bb1..e800568cab 100644 --- a/lib/electric/postgres/postgres_manager.ex +++ b/lib/electric/postgres/postgres_manager.ex @@ -175,9 +175,16 @@ defmodule Electric.Replication.PostgresConnectorMng do {:rollback, {:error, :downgrade_not_supported}} {:ok, _, []} -> - {:ok, _, _} = :epgsql.squery(conn, migration_file) - {:ok, _} = :epgsql.equery(conn, @update_migration, [vsn, md5_hash]) - :ok + res = :epgsql.squery(conn, migration_file) + + case check_response(res) do + :ok -> + {:ok, _} = :epgsql.equery(conn, @update_migration, [vsn, md5_hash]) + :ok + + error -> + error + end end end ) @@ -194,10 +201,24 @@ defmodule Electric.Replication.PostgresConnectorMng do else error -> Logger.error("failed to migrate to version: #{vsn}, reason: #{inspect(error)}") - error + {:error, error} + end + end + + defp check_response({:ok, _, _, _}), do: :ok + defp check_response({:ok, _}), do: :ok + defp check_response({:ok, _, _}), do: :ok + defp check_response({:error, _} = error), do: error + + defp check_response([h | t]) do + case check_response(h) do + :ok -> check_response(t) + {:error, _} = error -> error end end + defp check_response([]), do: :ok + defp start_subscription(%State{conn_config: conn_config, repl_config: rep_conf} = state) do case Client.with_conn( conn_config, diff --git a/lib/electric/replication/vaxine/log_consumer.ex b/lib/electric/replication/vaxine/log_consumer.ex index 204129c45c..217a296274 100644 --- a/lib/electric/replication/vaxine/log_consumer.ex +++ b/lib/electric/replication/vaxine/log_consumer.ex @@ -100,6 +100,7 @@ defmodule Electric.Replication.Vaxine.LogConsumer do {:noreply, [], state1} rescue error -> + Logger.error(Exception.format(:error, error, __STACKTRACE__)) {:stop, error, state} end end diff --git a/lib/electric/satellite/satellite_protocol.ex b/lib/electric/satellite/satellite_protocol.ex index 4019f42969..6bcbfaaf30 100644 --- a/lib/electric/satellite/satellite_protocol.ex +++ b/lib/electric/satellite/satellite_protocol.ex @@ -256,7 +256,7 @@ defmodule Electric.Satellite.Protocol do def process_message(%SatInStopReplicationResp{} = _msg, state) do Logger.debug("Received stop replication response") - in_rep = %InRep{state.in_rep | status: nil} + in_rep = %InRep{state.in_rep | status: :paused} {nil, %State{state | in_rep: in_rep}} end