From dca6f8e24ab46454bd4a2ca06a8deb5e787a3c46 Mon Sep 17 00:00:00 2001 From: Alexander Shevtsov Date: Thu, 3 Oct 2024 13:25:41 +0200 Subject: [PATCH 01/78] removed indexer from repository --- explorer/.env.example => .env.example | 0 .gitignore | 6 +- .../drizzle.config.ts => drizzle.config.ts | 0 drizzle/0000_ordinary_lady_vermin.sql | 134 ++ drizzle/meta/0000_snapshot.json | 624 +++++++ drizzle/meta/_journal.json | 13 + drizzle/relations.ts | 63 + drizzle/schema.ts | 132 ++ explorer/eslint.config.js => eslint.config.js | 0 explorer/.gitignore | 6 - .../src/routes/api/blocks/[hash]/+server.ts | 14 - explorer/src/routes/api/search/+server.ts | 15 - explorer/src/routes/api/spaces/+server.ts | 44 - .../routes/api/transactions/[id]/+server.ts | 24 - indexer/.env.example | 7 - indexer/.gitignore | 4 - indexer/drizzle.config.ts | 11 - indexer/package-lock.json | 1439 ----------------- indexer/package.json | 28 - indexer/src/db.ts | 20 - indexer/src/index.ts | 285 ---- indexer/src/rpc.ts | 56 - indexer/src/schema.ts | 104 -- indexer/src/utils.ts | 33 - indexer/tsconfig.json | 14 - .../package-lock.json => package-lock.json | 8 +- explorer/package.json => package.json | 2 +- .../postcss.config.js => postcss.config.js | 0 explorer/server.js => server.js | 0 {explorer/src => src}/app.css | 0 {explorer/src => src}/app.d.ts | 0 {explorer/src => src}/app.html | 0 .../lib/components/Block.svelte | 49 +- .../lib/components/Countdown.svelte | 0 .../lib/components/SortSelector.svelte | 0 .../src => src}/lib/components/Spinner.svelte | 0 .../lib/components/ThemeToggle.svelte | 0 .../lib/components/Transaction.svelte | 37 +- {explorer/src => src}/lib/db.ts | 0 {explorer/src => src}/lib/index.ts | 0 .../src => src}/lib/request-validation.ts | 0 src/lib/schema.ts | 200 +++ .../lib/schema.ts => src/lib/schema_old.ts | 0 {explorer/src => src}/lib/statusMeta.ts | 0 {explorer/src => src}/lib/utils.ts | 0 src/params/hash.ts | 3 + src/params/height.ts | 3 + {explorer/src => src}/routes/+error.svelte | 0 .../src => src}/routes/+layout.server.ts | 0 {explorer/src => src}/routes/+layout.svelte | 0 {explorer/src => src}/routes/+page.server.ts | 0 {explorer/src => src}/routes/+page.svelte | 1 + src/routes/api/blocks/[hash=hash]/+server.ts | 43 + .../api/blocks/[height=height]/+server.ts | 42 + .../routes/api/blocks/stats/+server.ts | 7 +- src/routes/api/search/+server.ts | 31 + src/routes/api/spaces/+server.ts | 67 + .../routes/api/spaces/[name]/+server.ts | 0 src/routes/api/transactions/[txid]/+server.ts | 76 + .../routes/block/[hash=hash]}/+page.server.ts | 3 +- src/routes/block/[hash=hash]/+page.svelte | 11 + .../block/[height=height]/+page.server.ts | 6 + src/routes/block/[height=height]/+page.svelte | 11 + .../src => src}/routes/past/+page.server.ts | 0 .../src => src}/routes/past/+page.svelte | 0 .../routes/space/[id]/+page.svelte | 0 .../src => src}/routes/space/[id]/+page.ts | 0 .../routes/tx/[txid]}/+page.server.ts | 11 +- src/routes/tx/[txid]/+page.svelte | 6 + .../routes/upcoming/+page.server.ts | 0 .../src => src}/routes/upcoming/+page.svelte | 0 {explorer/static => static}/action/bid.svg | 0 {explorer/static => static}/action/reject.svg | 0 {explorer/static => static}/action/revoke.svg | 0 .../static => static}/action/rollout.svg | 0 .../static => static}/action/transfer.svg | 0 {explorer/static => static}/arrow-right.svg | 0 {explorer/static => static}/favicon.png | Bin {explorer/static => static}/logo.png | Bin explorer/svelte.config.js => svelte.config.js | 0 .../tailwind.config.js => tailwind.config.js | 0 explorer/tsconfig.json => tsconfig.json | 0 explorer/vite.config.ts => vite.config.ts | 0 83 files changed, 1534 insertions(+), 2159 deletions(-) rename explorer/.env.example => .env.example (100%) rename explorer/drizzle.config.ts => drizzle.config.ts (100%) create mode 100644 drizzle/0000_ordinary_lady_vermin.sql create mode 100644 drizzle/meta/0000_snapshot.json create mode 100644 drizzle/meta/_journal.json create mode 100644 drizzle/relations.ts create mode 100644 drizzle/schema.ts rename explorer/eslint.config.js => eslint.config.js (100%) delete mode 100644 explorer/.gitignore delete mode 100644 explorer/src/routes/api/blocks/[hash]/+server.ts delete mode 100644 explorer/src/routes/api/search/+server.ts delete mode 100644 explorer/src/routes/api/spaces/+server.ts delete mode 100644 explorer/src/routes/api/transactions/[id]/+server.ts delete mode 100644 indexer/.env.example delete mode 100644 indexer/.gitignore delete mode 100644 indexer/drizzle.config.ts delete mode 100644 indexer/package-lock.json delete mode 100644 indexer/package.json delete mode 100644 indexer/src/db.ts delete mode 100644 indexer/src/index.ts delete mode 100644 indexer/src/rpc.ts delete mode 100644 indexer/src/schema.ts delete mode 100644 indexer/src/utils.ts delete mode 100644 indexer/tsconfig.json rename explorer/package-lock.json => package-lock.json (99%) rename explorer/package.json => package.json (98%) rename explorer/postcss.config.js => postcss.config.js (100%) rename explorer/server.js => server.js (100%) rename {explorer/src => src}/app.css (100%) rename {explorer/src => src}/app.d.ts (100%) rename {explorer/src => src}/app.html (100%) rename explorer/src/routes/block/[hash]/+page.svelte => src/lib/components/Block.svelte (65%) rename {explorer/src => src}/lib/components/Countdown.svelte (100%) rename {explorer/src => src}/lib/components/SortSelector.svelte (100%) rename {explorer/src => src}/lib/components/Spinner.svelte (100%) rename {explorer/src => src}/lib/components/ThemeToggle.svelte (100%) rename explorer/src/routes/tx/[id]/+page.svelte => src/lib/components/Transaction.svelte (67%) rename {explorer/src => src}/lib/db.ts (100%) rename {explorer/src => src}/lib/index.ts (100%) rename {explorer/src => src}/lib/request-validation.ts (100%) create mode 100644 src/lib/schema.ts rename explorer/src/lib/schema.ts => src/lib/schema_old.ts (100%) rename {explorer/src => src}/lib/statusMeta.ts (100%) rename {explorer/src => src}/lib/utils.ts (100%) create mode 100644 src/params/hash.ts create mode 100644 src/params/height.ts rename {explorer/src => src}/routes/+error.svelte (100%) rename {explorer/src => src}/routes/+layout.server.ts (100%) rename {explorer/src => src}/routes/+layout.svelte (100%) rename {explorer/src => src}/routes/+page.server.ts (100%) rename {explorer/src => src}/routes/+page.svelte (99%) create mode 100644 src/routes/api/blocks/[hash=hash]/+server.ts create mode 100644 src/routes/api/blocks/[height=height]/+server.ts rename {explorer/src => src}/routes/api/blocks/stats/+server.ts (60%) create mode 100644 src/routes/api/search/+server.ts create mode 100644 src/routes/api/spaces/+server.ts rename {explorer/src => src}/routes/api/spaces/[name]/+server.ts (100%) create mode 100644 src/routes/api/transactions/[txid]/+server.ts rename {explorer/src/routes/block/[hash] => src/routes/block/[hash=hash]}/+page.server.ts (82%) create mode 100644 src/routes/block/[hash=hash]/+page.svelte create mode 100644 src/routes/block/[height=height]/+page.server.ts create mode 100644 src/routes/block/[height=height]/+page.svelte rename {explorer/src => src}/routes/past/+page.server.ts (100%) rename {explorer/src => src}/routes/past/+page.svelte (100%) rename {explorer/src => src}/routes/space/[id]/+page.svelte (100%) rename {explorer/src => src}/routes/space/[id]/+page.ts (100%) rename {explorer/src/routes/tx/[id] => src/routes/tx/[txid]}/+page.server.ts (69%) create mode 100644 src/routes/tx/[txid]/+page.svelte rename {explorer/src => src}/routes/upcoming/+page.server.ts (100%) rename {explorer/src => src}/routes/upcoming/+page.svelte (100%) rename {explorer/static => static}/action/bid.svg (100%) rename {explorer/static => static}/action/reject.svg (100%) rename {explorer/static => static}/action/revoke.svg (100%) rename {explorer/static => static}/action/rollout.svg (100%) rename {explorer/static => static}/action/transfer.svg (100%) rename {explorer/static => static}/arrow-right.svg (100%) rename {explorer/static => static}/favicon.png (100%) rename {explorer/static => static}/logo.png (100%) rename explorer/svelte.config.js => svelte.config.js (100%) rename explorer/tailwind.config.js => tailwind.config.js (100%) rename explorer/tsconfig.json => tsconfig.json (100%) rename explorer/vite.config.ts => vite.config.ts (100%) diff --git a/explorer/.env.example b/.env.example similarity index 100% rename from explorer/.env.example rename to .env.example diff --git a/.gitignore b/.gitignore index 3f41869..b0ad634 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ +node_modules +.env +.svelte-kit +pgdata .vscode -pgdata \ No newline at end of file +build \ No newline at end of file diff --git a/explorer/drizzle.config.ts b/drizzle.config.ts similarity index 100% rename from explorer/drizzle.config.ts rename to drizzle.config.ts diff --git a/drizzle/0000_ordinary_lady_vermin.sql b/drizzle/0000_ordinary_lady_vermin.sql new file mode 100644 index 0000000..d7e6060 --- /dev/null +++ b/drizzle/0000_ordinary_lady_vermin.sql @@ -0,0 +1,134 @@ +-- Current sql file was generated after introspecting the database +-- If you want to run this migration please uncomment this code before executing migrations +/* +DO $$ BEGIN + CREATE TYPE "public"."covenant_action" AS ENUM('RESERVE', 'BID', 'TRANSFER'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "goose_db_version" ( + "id" serial PRIMARY KEY NOT NULL, + "version_id" bigint NOT NULL, + "is_applied" boolean NOT NULL, + "tstamp" timestamp DEFAULT now() +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "blocks" ( + "hash" "bytea" PRIMARY KEY NOT NULL, + "size" bigint NOT NULL, + "stripped_size" bigint NOT NULL, + "weight" integer NOT NULL, + "height" integer NOT NULL, + "version" integer NOT NULL, + "hash_merkle_root" "bytea" NOT NULL, + "time" integer NOT NULL, + "median_time" integer NOT NULL, + "nonce" bigint NOT NULL, + "bits" "bytea" NOT NULL, + "difficulty" double precision NOT NULL, + "chainwork" "bytea" NOT NULL, + "orphan" boolean DEFAULT false NOT NULL, + CONSTRAINT "blocks_height_key" UNIQUE("height") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "transactions" ( + "txid" "bytea" PRIMARY KEY NOT NULL, + "tx_hash" "bytea", + "version" integer NOT NULL, + "size" bigint NOT NULL, + "vsize" bigint NOT NULL, + "weight" bigint NOT NULL, + "locktime" integer NOT NULL, + "fee" bigint NOT NULL, + "block_hash" "bytea", + "index" integer +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "tx_outputs" ( + "block_hash" "bytea" NOT NULL, + "txid" "bytea" NOT NULL, + "index" integer NOT NULL, + "value" bigint NOT NULL, + "scriptpubkey" "bytea", + CONSTRAINT "tx_outputs_pkey" PRIMARY KEY("block_hash","txid","index") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "tx_inputs" ( + "block_hash" "bytea" NOT NULL, + "txid" "bytea" NOT NULL, + "index" bigint NOT NULL, + "hash_prevout" "bytea", + "index_prevout" bigint NOT NULL, + "sequence" bigint NOT NULL, + "coinbase" "bytea", + "txinwitness" bytea[], + CONSTRAINT "tx_inputs_pkey" PRIMARY KEY("block_hash","txid","index") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "vmetaouts" ( + "block_hash" "bytea" NOT NULL, + "txid" "bytea" NOT NULL, + "tx_index" bigint NOT NULL, + "outpoint_txid" "bytea" NOT NULL, + "outpoint_index" bigint NOT NULL, + "name" text NOT NULL, + "burn_increment" bigint, + "covenant_action" "covenant_action" NOT NULL, + "claim_height" bigint, + "expire_height" bigint, + CONSTRAINT "vmetaouts_pkey" PRIMARY KEY("block_hash","txid","tx_index") +); +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "transactions" ADD CONSTRAINT "transactions_block_hash_fkey" FOREIGN KEY ("block_hash") REFERENCES "public"."blocks"("hash") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tx_outputs" ADD CONSTRAINT "tx_outputs_block_hash_fkey" FOREIGN KEY ("block_hash") REFERENCES "public"."blocks"("hash") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tx_outputs" ADD CONSTRAINT "tx_outputs_txid_fkey" FOREIGN KEY ("txid") REFERENCES "public"."transactions"("txid") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tx_inputs" ADD CONSTRAINT "tx_inputs_block_hash_fkey" FOREIGN KEY ("block_hash") REFERENCES "public"."blocks"("hash") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "tx_inputs" ADD CONSTRAINT "tx_inputs_txid_fkey" FOREIGN KEY ("txid") REFERENCES "public"."transactions"("txid") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "vmetaouts" ADD CONSTRAINT "vmetaouts_block_hash_fkey" FOREIGN KEY ("block_hash") REFERENCES "public"."blocks"("hash") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "vmetaouts" ADD CONSTRAINT "vmetaouts_txid_fkey" FOREIGN KEY ("txid") REFERENCES "public"."transactions"("txid") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "vmetaouts" ADD CONSTRAINT "vmetaouts_outpoint_txid_fkey" FOREIGN KEY ("outpoint_txid") REFERENCES "public"."transactions"("txid") ON DELETE no action ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "transactions_block_hash_index" ON "transactions" USING btree ("block_hash" int4_ops,"index" int4_ops) WHERE (block_hash IS NOT NULL);--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "tx_inputs_hash_prevout_index" ON "tx_inputs" USING btree ("hash_prevout" int8_ops,"index_prevout" int8_ops) WHERE (hash_prevout IS NOT NULL);--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "tx_inputs_txid_index" ON "tx_inputs" USING btree ("txid" bytea_ops); +*/ \ No newline at end of file diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json new file mode 100644 index 0000000..663ce27 --- /dev/null +++ b/drizzle/meta/0000_snapshot.json @@ -0,0 +1,624 @@ +{ + "id": "00000000-0000-0000-0000-000000000000", + "prevId": "", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.goose_db_version": { + "name": "goose_db_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "version_id": { + "name": "version_id", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "is_applied": { + "name": "is_applied", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "tstamp": { + "name": "tstamp", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.blocks": { + "name": "blocks", + "schema": "", + "columns": { + "hash": { + "name": "hash", + "type": "bytea", + "primaryKey": true, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "stripped_size": { + "name": "stripped_size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "hash_merkle_root": { + "name": "hash_merkle_root", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "time": { + "name": "time", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "median_time": { + "name": "median_time", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "nonce": { + "name": "nonce", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "bits": { + "name": "bits", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "difficulty": { + "name": "difficulty", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "chainwork": { + "name": "chainwork", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "orphan": { + "name": "orphan", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "blocks_height_key": { + "columns": [ + "height" + ], + "nullsNotDistinct": false, + "name": "blocks_height_key" + } + } + }, + "public.transactions": { + "name": "transactions", + "schema": "", + "columns": { + "txid": { + "name": "txid", + "type": "bytea", + "primaryKey": true, + "notNull": true + }, + "tx_hash": { + "name": "tx_hash", + "type": "bytea", + "primaryKey": false, + "notNull": false + }, + "version": { + "name": "version", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "vsize": { + "name": "vsize", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "locktime": { + "name": "locktime", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "fee": { + "name": "fee", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "block_hash": { + "name": "block_hash", + "type": "bytea", + "primaryKey": false, + "notNull": false + }, + "index": { + "name": "index", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "transactions_block_hash_index": { + "name": "transactions_block_hash_index", + "columns": [ + { + "expression": "block_hash", + "asc": true, + "nulls": "last", + "opclass": "int4_ops", + "isExpression": false + }, + { + "expression": "index", + "asc": true, + "nulls": "last", + "opclass": "int4_ops", + "isExpression": false + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "where": "(block_hash IS NOT NULL)", + "with": {} + } + }, + "foreignKeys": { + "transactions_block_hash_fkey": { + "name": "transactions_block_hash_fkey", + "tableFrom": "transactions", + "tableTo": "blocks", + "schemaTo": "public", + "columnsFrom": [ + "block_hash" + ], + "columnsTo": [ + "hash" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "public.tx_outputs": { + "name": "tx_outputs", + "schema": "", + "columns": { + "block_hash": { + "name": "block_hash", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "txid": { + "name": "txid", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "index": { + "name": "index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "scriptpubkey": { + "name": "scriptpubkey", + "type": "bytea", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "tx_outputs_block_hash_fkey": { + "name": "tx_outputs_block_hash_fkey", + "tableFrom": "tx_outputs", + "tableTo": "blocks", + "schemaTo": "public", + "columnsFrom": [ + "block_hash" + ], + "columnsTo": [ + "hash" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tx_outputs_txid_fkey": { + "name": "tx_outputs_txid_fkey", + "tableFrom": "tx_outputs", + "tableTo": "transactions", + "schemaTo": "public", + "columnsFrom": [ + "txid" + ], + "columnsTo": [ + "txid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "tx_outputs_pkey": { + "name": "tx_outputs_pkey", + "columns": [ + "block_hash", + "txid", + "index" + ] + } + }, + "uniqueConstraints": {} + }, + "public.tx_inputs": { + "name": "tx_inputs", + "schema": "", + "columns": { + "block_hash": { + "name": "block_hash", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "txid": { + "name": "txid", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "index": { + "name": "index", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "hash_prevout": { + "name": "hash_prevout", + "type": "bytea", + "primaryKey": false, + "notNull": false + }, + "index_prevout": { + "name": "index_prevout", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "sequence": { + "name": "sequence", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "coinbase": { + "name": "coinbase", + "type": "bytea", + "primaryKey": false, + "notNull": false + }, + "txinwitness": { + "name": "txinwitness", + "type": "bytea[]", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "tx_inputs_hash_prevout_index": { + "name": "tx_inputs_hash_prevout_index", + "columns": [ + { + "expression": "hash_prevout", + "asc": true, + "nulls": "last", + "opclass": "int8_ops", + "isExpression": false + }, + { + "expression": "index_prevout", + "asc": true, + "nulls": "last", + "opclass": "int8_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "where": "(hash_prevout IS NOT NULL)", + "with": {} + }, + "tx_inputs_txid_index": { + "name": "tx_inputs_txid_index", + "columns": [ + { + "expression": "txid", + "asc": true, + "nulls": "last", + "opclass": "bytea_ops", + "isExpression": false + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tx_inputs_block_hash_fkey": { + "name": "tx_inputs_block_hash_fkey", + "tableFrom": "tx_inputs", + "tableTo": "blocks", + "schemaTo": "public", + "columnsFrom": [ + "block_hash" + ], + "columnsTo": [ + "hash" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tx_inputs_txid_fkey": { + "name": "tx_inputs_txid_fkey", + "tableFrom": "tx_inputs", + "tableTo": "transactions", + "schemaTo": "public", + "columnsFrom": [ + "txid" + ], + "columnsTo": [ + "txid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "tx_inputs_pkey": { + "name": "tx_inputs_pkey", + "columns": [ + "block_hash", + "txid", + "index" + ] + } + }, + "uniqueConstraints": {} + }, + "public.vmetaouts": { + "name": "vmetaouts", + "schema": "", + "columns": { + "block_hash": { + "name": "block_hash", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "txid": { + "name": "txid", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "tx_index": { + "name": "tx_index", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "outpoint_txid": { + "name": "outpoint_txid", + "type": "bytea", + "primaryKey": false, + "notNull": true + }, + "outpoint_index": { + "name": "outpoint_index", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "burn_increment": { + "name": "burn_increment", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "covenant_action": { + "name": "covenant_action", + "type": "covenant_action", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "claim_height": { + "name": "claim_height", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "expire_height": { + "name": "expire_height", + "type": "bigint", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "vmetaouts_block_hash_fkey": { + "name": "vmetaouts_block_hash_fkey", + "tableFrom": "vmetaouts", + "tableTo": "blocks", + "schemaTo": "public", + "columnsFrom": [ + "block_hash" + ], + "columnsTo": [ + "hash" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "vmetaouts_txid_fkey": { + "name": "vmetaouts_txid_fkey", + "tableFrom": "vmetaouts", + "tableTo": "transactions", + "schemaTo": "public", + "columnsFrom": [ + "txid" + ], + "columnsTo": [ + "txid" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "vmetaouts_outpoint_txid_fkey": { + "name": "vmetaouts_outpoint_txid_fkey", + "tableFrom": "vmetaouts", + "tableTo": "transactions", + "schemaTo": "public", + "columnsFrom": [ + "outpoint_txid" + ], + "columnsTo": [ + "txid" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "vmetaouts_pkey": { + "name": "vmetaouts_pkey", + "columns": [ + "block_hash", + "txid", + "tx_index" + ] + } + }, + "uniqueConstraints": {} + } + }, + "enums": { + "public.covenant_action": { + "name": "covenant_action", + "values": [ + "RESERVE", + "BID", + "TRANSFER" + ], + "schema": "public" + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": { + "tx_inputs": { + "columns": { + "txinwitness": { + "isArray": true, + "dimensions": 1, + "rawType": "bytea" + } + } + } + } + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json new file mode 100644 index 0000000..7c5a966 --- /dev/null +++ b/drizzle/meta/_journal.json @@ -0,0 +1,13 @@ +{ + "version": "7", + "dialect": "postgresql", + "entries": [ + { + "idx": 0, + "version": "7", + "when": 1727572787440, + "tag": "0000_ordinary_lady_vermin", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/drizzle/relations.ts b/drizzle/relations.ts new file mode 100644 index 0000000..58b677b --- /dev/null +++ b/drizzle/relations.ts @@ -0,0 +1,63 @@ +import { relations } from "drizzle-orm/relations"; +import { blocks, transactions, tx_outputs, tx_inputs, vmetaouts } from "./schema"; + +export const transactionsRelations = relations(transactions, ({one, many}) => ({ + block: one(blocks, { + fields: [transactions.block_hash], + references: [blocks.hash] + }), + tx_outputs: many(tx_outputs), + tx_inputs: many(tx_inputs), + vmetaouts_txid: many(vmetaouts, { + relationName: "vmetaouts_txid_transactions_txid" + }), + vmetaouts_outpoint_txid: many(vmetaouts, { + relationName: "vmetaouts_outpoint_txid_transactions_txid" + }), +})); + +export const blocksRelations = relations(blocks, ({many}) => ({ + transactions: many(transactions), + tx_outputs: many(tx_outputs), + tx_inputs: many(tx_inputs), + vmetaouts: many(vmetaouts), +})); + +export const tx_outputsRelations = relations(tx_outputs, ({one}) => ({ + block: one(blocks, { + fields: [tx_outputs.block_hash], + references: [blocks.hash] + }), + transaction: one(transactions, { + fields: [tx_outputs.txid], + references: [transactions.txid] + }), +})); + +export const tx_inputsRelations = relations(tx_inputs, ({one}) => ({ + block: one(blocks, { + fields: [tx_inputs.block_hash], + references: [blocks.hash] + }), + transaction: one(transactions, { + fields: [tx_inputs.txid], + references: [transactions.txid] + }), +})); + +export const vmetaoutsRelations = relations(vmetaouts, ({one}) => ({ + block: one(blocks, { + fields: [vmetaouts.block_hash], + references: [blocks.hash] + }), + transaction_txid: one(transactions, { + fields: [vmetaouts.txid], + references: [transactions.txid], + relationName: "vmetaouts_txid_transactions_txid" + }), + transaction_outpoint_txid: one(transactions, { + fields: [vmetaouts.outpoint_txid], + references: [transactions.txid], + relationName: "vmetaouts_outpoint_txid_transactions_txid" + }), +})); \ No newline at end of file diff --git a/drizzle/schema.ts b/drizzle/schema.ts new file mode 100644 index 0000000..a5b15f0 --- /dev/null +++ b/drizzle/schema.ts @@ -0,0 +1,132 @@ +import { pgTable, pgEnum, serial, bigint, boolean, timestamp, unique, integer, doublePrecision, uniqueIndex, foreignKey, primaryKey, index, text, customType } from "drizzle-orm/pg-core" + import { sql } from "drizzle-orm" + + +export const covenant_action = pgEnum("covenant_action", ['RESERVE', 'BID', 'TRANSFER']) + +const bytea = customType({ + dataType: 'bytea', + fromDriver(value: unknown): Buffer { + return value as Buffer; + }, + toDriver(value: Buffer): Buffer { + return value; + }, +}); + +export const goose_db_version = pgTable("goose_db_version", { + id: serial("id").primaryKey().notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + version_id: bigint("version_id", { mode: "number" }).notNull(), + is_applied: boolean("is_applied").notNull(), + tstamp: timestamp("tstamp", { mode: 'string' }).defaultNow(), +}); + +export const blocks = pgTable("blocks", { + // TODO: failed to parse database type 'bytea' + hash: bytea("hash").primaryKey().notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + size: bigint("size", { mode: "number" }).notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + stripped_size: bigint("stripped_size", { mode: "number" }).notNull(), + weight: integer("weight").notNull(), + height: integer("height").notNull(), + version: integer("version").notNull(), + // TODO: failed to parse database type 'bytea' + hash_merkle_root: bytea("hash_merkle_root").notNull(), + time: integer("time").notNull(), + median_time: integer("median_time").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + nonce: bigint("nonce", { mode: "number" }).notNull(), + // TODO: failed to parse database type 'bytea' + bits: bytea("bits").notNull(), + difficulty: doublePrecision("difficulty").notNull(), + // TODO: failed to parse database type 'bytea' + chainwork: bytea("chainwork").notNull(), + orphan: boolean("orphan").default(false).notNull(), +}, +(table) => { + return { + blocks_height_key: unique("blocks_height_key").on(table.height), + } +}); + +export const transactions = pgTable("transactions", { + txid: bytea("txid").primaryKey().notNull(), + tx_hash: bytea("tx_hash"), + version: integer("version").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + size: bigint("size", { mode: "number" }).notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + vsize: bigint("vsize", { mode: "number" }).notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + weight: bigint("weight", { mode: "number" }).notNull(), + locktime: integer("locktime").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + fee: bigint("fee", { mode: "number" }).notNull(), + block_hash: bytea("block_hash").references(() => blocks.hash, { onDelete: "cascade" } ), + index: integer("index"), +}, +(table) => { + return { + block_hash_idx: uniqueIndex("transactions_block_hash_index").using("btree", table.block_hash, table.index).where(sql`(block_hash IS NOT NULL)`), + } +}); + +export const tx_outputs = pgTable("tx_outputs", { + block_hash: bytea("block_hash").notNull().references(() => blocks.hash, { onDelete: "cascade" } ), + txid: bytea("txid").notNull().references(() => transactions.txid, { onDelete: "cascade" } ), + index: integer("index").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + value: bigint("value", { mode: "number" }).notNull(), + scriptpubkey: bytea("scriptpubkey"), +}, +(table) => { + return { + tx_outputs_pkey: primaryKey({ columns: [table.block_hash, table.txid, table.index], name: "tx_outputs_pkey"}), + } +}); + +export const tx_inputs = pgTable("tx_inputs", { + block_hash: bytea("block_hash").notNull().references(() => blocks.hash, { onDelete: "cascade" } ), + txid: bytea("txid").notNull().references(() => transactions.txid, { onDelete: "cascade" } ), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + index: bigint("index", { mode: "number" }).notNull(), + hash_prevout: bytea("hash_prevout"), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + index_prevout: bigint("index_prevout", { mode: "number" }).notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + sequence: bigint("sequence", { mode: "number" }).notNull(), + coinbase: bytea("coinbase"), + txinwitness: bytea("txinwitness").array(), +}, +(table) => { + return { + hash_prevout_idx: index("tx_inputs_hash_prevout_index").using("btree", table.hash_prevout, table.index_prevout).where(sql`(hash_prevout IS NOT NULL)`), + txid_idx: index().using("btree", table.txid), + tx_inputs_pkey: primaryKey({ columns: [table.block_hash, table.txid, table.index], name: "tx_inputs_pkey"}), + } +}); + +export const vmetaouts = pgTable("vmetaouts", { + block_hash: bytea("block_hash").notNull().references(() => blocks.hash, { onDelete: "cascade" } ), + txid: bytea("txid").notNull().references(() => transactions.txid, { onDelete: "cascade" } ), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + tx_index: bigint("tx_index", { mode: "number" }).notNull(), + outpoint_txid: bytea("outpoint_txid").notNull().references(() => transactions.txid), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + outpoint_index: bigint("outpoint_index", { mode: "number" }).notNull(), + name: text("name").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + burn_increment: bigint("burn_increment", { mode: "number" }), + covenant_action: covenant_action("covenant_action").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + claim_height: bigint("claim_height", { mode: "number" }), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + expire_height: bigint("expire_height", { mode: "number" }), +}, +(table) => { + return { + vmetaouts_pkey: primaryKey({ columns: [table.block_hash, table.txid, table.tx_index], name: "vmetaouts_pkey"}), + } +}); diff --git a/explorer/eslint.config.js b/eslint.config.js similarity index 100% rename from explorer/eslint.config.js rename to eslint.config.js diff --git a/explorer/.gitignore b/explorer/.gitignore deleted file mode 100644 index b0ad634..0000000 --- a/explorer/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -.env -.svelte-kit -pgdata -.vscode -build \ No newline at end of file diff --git a/explorer/src/routes/api/blocks/[hash]/+server.ts b/explorer/src/routes/api/blocks/[hash]/+server.ts deleted file mode 100644 index 61c1aa9..0000000 --- a/explorer/src/routes/api/blocks/[hash]/+server.ts +++ /dev/null @@ -1,14 +0,0 @@ -import db from '$lib/db'; -import { error, json } from '@sveltejs/kit'; -import { type RequestHandler } from '@sveltejs/kit'; -import { sql } from 'drizzle-orm'; -import { spaces, blocks, spacesHistory } from '$lib/schema'; - -export const GET: RequestHandler = async function ({ request, url, params }) { - const blockDb = await db.query.blocks.findFirst({ - where: sql`${blocks.hash} = ${params.hash}`, - with: { transactions: { with: { spaceHistories: true } }} - }); - - return json(blockDb); -}; \ No newline at end of file diff --git a/explorer/src/routes/api/search/+server.ts b/explorer/src/routes/api/search/+server.ts deleted file mode 100644 index 965b822..0000000 --- a/explorer/src/routes/api/search/+server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import db from '$lib/db'; -import { error, json } from '@sveltejs/kit'; -import { type RequestHandler } from '@sveltejs/kit'; -import { sql, asc, desc } from 'drizzle-orm'; -import { spaces, spacesHistory, blockStats } from '$lib/schema'; - -export const GET: RequestHandler = async function ({ request, url }) { - const search = url.searchParams.get('q'); - if (!search) - return json([]); - - const result = await db.select().from(spaces).where(sql`similarity(${spaces.name}, ${search}) > 0`).orderBy(sql`similarity(${spaces.name}, ${search}) desc`).limit(3); - - return json(result); -} \ No newline at end of file diff --git a/explorer/src/routes/api/spaces/+server.ts b/explorer/src/routes/api/spaces/+server.ts deleted file mode 100644 index 1ae52f0..0000000 --- a/explorer/src/routes/api/spaces/+server.ts +++ /dev/null @@ -1,44 +0,0 @@ -import db from '$lib/db'; -import { error, json } from '@sveltejs/kit'; -import { type RequestHandler } from '@sveltejs/kit'; -import { sql, asc, desc } from 'drizzle-orm'; -import { spaces, spacesHistory, blockStats } from '$lib/schema'; - -export const GET: RequestHandler = async function ({ request, url }) { - const status = url.searchParams.get('status'); - const sortBy = url.searchParams.get('sort'); - const direction = url.searchParams.get('direction'); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let orderBy: any; - - if (sortBy == 'ending') { - orderBy = sql`case when ${spaces.claimHeight} > (select block_height from ${blockStats}) then 1 else 2 end, ${spaces.claimHeight}` - } else if (sortBy == 'price') { - orderBy = [direction === 'desc' ? desc(spaces.bid_amount) : asc(spaces.bid_amount)]; - } else if (sortBy == 'register_date') { - orderBy = [direction === 'desc' ? desc(spaces.spacesHistoryId) : asc(spaces.spacesHistoryId)]; - } - - const spacesDb = await db.query.spaces.findMany({ - where: status ? sql`${spaces.status} = ${status}`: sql`1=1`, - extras: { rank: sql`dense_rank() over(order by ${spaces.bid_amount} desc, ${spaces.nameSha256})`.as('rank') }, - with: { - history: { - columns: { id: true, action: true, bid_amount: true }, - with: { - transaction: { - columns: { id: true, txid: true }, - with: { - block: { columns: { time: true } } - } - } - }, - orderBy: (history) => history.id, - }, - }, - orderBy - }); - - return json(spacesDb ?? []); -} \ No newline at end of file diff --git a/explorer/src/routes/api/transactions/[id]/+server.ts b/explorer/src/routes/api/transactions/[id]/+server.ts deleted file mode 100644 index 7a616ac..0000000 --- a/explorer/src/routes/api/transactions/[id]/+server.ts +++ /dev/null @@ -1,24 +0,0 @@ -import db from '$lib/db'; -import { error, json } from '@sveltejs/kit'; -import { type RequestHandler } from '@sveltejs/kit'; -import { sql } from 'drizzle-orm'; -import { spaces, spacesHistory, transactions } from '$lib/schema'; - -export const GET: RequestHandler = async function ({ request, url, params }) { - const transaction = await db.query.transactions.findFirst({ - where: sql`${transactions.txid} = ${params.id}`, - with: { - block: { - columns: { time: true, height: true, confirmations: true }, - }, - spaceHistories: { - columns: { spaceName: true, action: true, bid_amount: true, meta: true } - } - } - }); - - if (!transaction) - return error(404, 'Transaction not found'); - - return json(transaction); -} \ No newline at end of file diff --git a/indexer/.env.example b/indexer/.env.example deleted file mode 100644 index fdd115a..0000000 --- a/indexer/.env.example +++ /dev/null @@ -1,7 +0,0 @@ -DB_URL=postgres://postgres:password@localhost:5432/spacesprotocol_explorer -NETWORK=testnet4 -SPACES_STARTING_BLOCKHEIGHT=38580 -BITCOIN_RPC_URL=http://localhost:48332 -BITCOIN_RPC_USER=testnet4 -BITCOIN_RPC_PASSWORD=testnet4 -SPACED_RPC_URL=http://localhost:7224 diff --git a/indexer/.gitignore b/indexer/.gitignore deleted file mode 100644 index 806aa67..0000000 --- a/indexer/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules -.env -dist -.vscode \ No newline at end of file diff --git a/indexer/drizzle.config.ts b/indexer/drizzle.config.ts deleted file mode 100644 index 61284b3..0000000 --- a/indexer/drizzle.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from "drizzle-kit"; - -export default defineConfig({ - schema: "./src/schema.ts", - dialect: 'postgresql', - dbCredentials: { - url: process.env.DB_URL, - }, - verbose: true, - strict: true, -}); diff --git a/indexer/package-lock.json b/indexer/package-lock.json deleted file mode 100644 index 48b584d..0000000 --- a/indexer/package-lock.json +++ /dev/null @@ -1,1439 +0,0 @@ -{ - "name": "spacesprotocol-indexer", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "spacesprotocol-indexer", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "bech32": "^2.0.0", - "dotenv": "^16.4.5", - "drizzle-orm": "^0.31.2", - "node-cron": "^3.0.3", - "node-fetch": "^3.3.2", - "pg": "^8.12.0" - }, - "devDependencies": { - "@types/node": "^20.14.10", - "@types/pg": "^8.11.6", - "drizzle-kit": "^0.22.7", - "typescript": "^5.5.3" - } - }, - "node_modules/@esbuild-kit/core-utils": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz", - "integrity": "sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==", - "dev": true, - "dependencies": { - "esbuild": "~0.18.20", - "source-map-support": "^0.5.21" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild-kit/core-utils/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/@esbuild-kit/esm-loader": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.6.5.tgz", - "integrity": "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==", - "dev": true, - "dependencies": { - "@esbuild-kit/core-utils": "^3.3.2", - "get-tsconfig": "^4.7.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", - "devOptional": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true - }, - "node_modules/@types/pg": { - "version": "8.11.6", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.6.tgz", - "integrity": "sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ==", - "devOptional": true, - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^4.0.1" - } - }, - "node_modules/@types/pg/node_modules/pg-types": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", - "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", - "devOptional": true, - "dependencies": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.1.0", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/pg/node_modules/postgres-array": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", - "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", - "devOptional": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-bytea": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", - "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", - "devOptional": true, - "dependencies": { - "obuf": "~1.1.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/pg/node_modules/postgres-date": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", - "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", - "devOptional": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-interval": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", - "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", - "devOptional": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/bech32": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", - "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/drizzle-kit": { - "version": "0.22.8", - "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.22.8.tgz", - "integrity": "sha512-VjI4wsJjk3hSqHSa3TwBf+uvH6M6pRHyxyoVbt935GUzP9tUR/BRZ+MhEJNgryqbzN2Za1KP0eJMTgKEPsalYQ==", - "dev": true, - "dependencies": { - "@esbuild-kit/esm-loader": "^2.5.5", - "esbuild": "^0.19.7", - "esbuild-register": "^3.5.0" - }, - "bin": { - "drizzle-kit": "bin.cjs" - } - }, - "node_modules/drizzle-orm": { - "version": "0.31.4", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.31.4.tgz", - "integrity": "sha512-VGD9SH9aStF2z4QOTnVlVX/WghV/EnuEzTmsH3fSVp2E4fFgc8jl3viQrS/XUJx1ekW4rVVLJMH42SfGQdjX3Q==", - "peerDependencies": { - "@aws-sdk/client-rds-data": ">=3", - "@cloudflare/workers-types": ">=3", - "@electric-sql/pglite": ">=0.1.1", - "@libsql/client": "*", - "@neondatabase/serverless": ">=0.1", - "@op-engineering/op-sqlite": ">=2", - "@opentelemetry/api": "^1.4.1", - "@planetscale/database": ">=1", - "@prisma/client": "*", - "@tidbcloud/serverless": "*", - "@types/better-sqlite3": "*", - "@types/pg": "*", - "@types/react": ">=18", - "@types/sql.js": "*", - "@vercel/postgres": ">=0.8.0", - "@xata.io/client": "*", - "better-sqlite3": ">=7", - "bun-types": "*", - "expo-sqlite": ">=13.2.0", - "knex": "*", - "kysely": "*", - "mysql2": ">=2", - "pg": ">=8", - "postgres": ">=3", - "react": ">=18", - "sql.js": ">=1", - "sqlite3": ">=5" - }, - "peerDependenciesMeta": { - "@aws-sdk/client-rds-data": { - "optional": true - }, - "@cloudflare/workers-types": { - "optional": true - }, - "@electric-sql/pglite": { - "optional": true - }, - "@libsql/client": { - "optional": true - }, - "@neondatabase/serverless": { - "optional": true - }, - "@op-engineering/op-sqlite": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@prisma/client": { - "optional": true - }, - "@tidbcloud/serverless": { - "optional": true - }, - "@types/better-sqlite3": { - "optional": true - }, - "@types/pg": { - "optional": true - }, - "@types/react": { - "optional": true - }, - "@types/sql.js": { - "optional": true - }, - "@vercel/postgres": { - "optional": true - }, - "@xata.io/client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "bun-types": { - "optional": true - }, - "expo-sqlite": { - "optional": true - }, - "knex": { - "optional": true - }, - "kysely": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "pg": { - "optional": true - }, - "postgres": { - "optional": true - }, - "prisma": { - "optional": true - }, - "react": { - "optional": true - }, - "sql.js": { - "optional": true - }, - "sqlite3": { - "optional": true - } - } - }, - "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" - } - }, - "node_modules/esbuild-register": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.5.0.tgz", - "integrity": "sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "peerDependencies": { - "esbuild": ">=0.12 <1" - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/get-tsconfig": { - "version": "4.7.5", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", - "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/node-cron": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", - "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", - "dependencies": { - "uuid": "8.3.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "devOptional": true - }, - "node_modules/pg": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", - "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", - "dependencies": { - "pg-connection-string": "^2.6.4", - "pg-pool": "^3.6.2", - "pg-protocol": "^1.6.1", - "pg-types": "^2.1.0", - "pgpass": "1.x" - }, - "engines": { - "node": ">= 8.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.1.1" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", - "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-numeric": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", - "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", - "devOptional": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/pg-pool": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", - "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", - "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/postgres": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", - "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/porsager" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-range": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", - "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", - "devOptional": true - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/indexer/package.json b/indexer/package.json deleted file mode 100644 index 78261e4..0000000 --- a/indexer/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "spacesprotocol-indexer", - "version": "1.0.0", - "description": "", - "main": "index.js", - "type": "module", - "scripts": { - "start": "tsc && node dist/index.js", - "build": "tsc", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "devDependencies": { - "@types/node": "^20.14.10", - "@types/pg": "^8.11.6", - "drizzle-kit": "^0.22.7", - "typescript": "^5.5.3" - }, - "dependencies": { - "bech32": "^2.0.0", - "dotenv": "^16.4.5", - "drizzle-orm": "^0.31.2", - "node-cron": "^3.0.3", - "node-fetch": "^3.3.2", - "pg": "^8.12.0" - } -} diff --git a/indexer/src/db.ts b/indexer/src/db.ts deleted file mode 100644 index a4c56fa..0000000 --- a/indexer/src/db.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { drizzle } from "drizzle-orm/node-postgres"; -import pg from 'pg'; -const { Client } = pg; - -import * as schema from './schema.js'; -import dotenv from 'dotenv'; -dotenv.config(); - -export async function getDb() { - const client = new Client({ connectionString: process.env.DB_URL }); - await client.connect(); - const dbDrizzle = drizzle(client, { schema }); - - // Create an extended db object with the end method - const db = Object.assign(dbDrizzle, { - end: () => client.end() - }) as typeof dbDrizzle & { end: () => Promise }; - - return db; -} \ No newline at end of file diff --git a/indexer/src/index.ts b/indexer/src/index.ts deleted file mode 100644 index 5b598a8..0000000 --- a/indexer/src/index.ts +++ /dev/null @@ -1,285 +0,0 @@ -import { SimpleRpcClient } from './rpc.js'; -import { spaces, spacesHistory, blocks, transactions, syncs, blockStats } from './schema.js'; -import { getDb } from './db.js'; -import { sql, desc } from 'drizzle-orm'; -import { performance } from 'perf_hooks'; -import cron from 'node-cron'; -import { sha256SpaceName } from './utils.js'; -import dotenv from 'dotenv'; -dotenv.config(); - -type NewSpaceHistory = typeof spacesHistory.$inferInsert; - -interface TransactionOutput { - name?: string; - outpoint?: string; - covenant?: any; -} - -interface MetaOutput { - name?: string; - action?: string; - target?: { - name: string; - }; - covenant?: any; - bid_value?: number; -} - -interface Transaction { - txid: string; - version: number; - vout: TransactionOutput[]; - vmetaout: MetaOutput[]; -} - -interface Block { - hash: string, - confirmations: number, - height: number, - version: number, - versionHex: string, - merkleroot: string, - time: number, - mediantime: number, - nonce: number, - bits: string, - difficulty: number, - chainwork: string, - nTx: number, - previousblockhash: string, - nextblockhash: string, - strippedsize: number, - size: number, - weight: number, -} - -class SimpleIndexer { - public nameHistory: { [key: string]: (TransactionOutput | MetaOutput)[] }; - - constructor() { - this.nameHistory = {}; - } - - /** - * Process block - * @param height the block height - * @param blockHash the block hash - * @param block the Bitcoin block data - * @param spaceTxs any transactions relevant to Spaces in this block - */ - async processBlock(block: Block, spaceTxs: Transaction[], db): Promise { - if (spaceTxs.length === 0) { - // No spaces transactions in this block - return; - } - - try { - await db.transaction(async (dbTx) => { - const blockDB = await dbTx.insert(blocks).values({ - hash: block.hash, - confirmations: block.confirmations, - height: block.height, - version: block.version, - merkleroot: block.merkleroot, - time: block.time, - mediantime: block.mediantime, - nonce: block.nonce, - bits: block.bits, - difficulty: block.difficulty.toString(), - chainwork: block.chainwork, - nTx: block.nTx, - previousblockhash: block.previousblockhash, - nextblockhash: block.nextblockhash, - strippedsize: block.strippedsize, - size: block.size, - weight: block.weight, - }).returning({ insertedBlockId: blocks.id }); - - const transactionsDb = await dbTx.insert(transactions).values(spaceTxs.map(x => ({ - txid: x.txid, - version: x.version, - blockId: blockDB[0].insertedBlockId, - blockHash: block.hash, - data: x, - }))).returning(); - - for (let tx of spaceTxs) { - for (let i = 0; i < tx.vout.length; i++) { - let vout = tx.vout[i]; - if (!vout.name || vout.covenant?.type !== 'transfer') continue; - let spaceDb = (await dbTx.select().from(spaces).where(sql`${spaces.name} = ${vout.name}`))?.[0]; - - const historyRecord = (await dbTx.insert(spacesHistory).values({ - spaceName: spaceDb.name, - spaceId: spaceDb.id, - transactionId: transactionsDb.find(x => x.txid === tx.txid).id, - txid: tx.txid, - action: 'transfer', - bid_amount: null, - meta: {...vout, outpoint: `${tx.txid}:${i}`} - }).returning())?.[0]; - - if (spaceDb.status != 'registered') - await dbTx.update(spaces).set({ status: 'registered', spacesHistoryId: historyRecord.id, updatedAt: sql`now()` }).where(sql`${spaces.id} = ${spaceDb.id}`); - } - - for (let meta of tx.vmetaout) { - const name = meta.name || meta.target?.name; - if (!name) continue; - - let newSpaceStatus = null - let spaceDb = (await dbTx.select().from(spaces).where(sql`${spaces.name} = ${name}`))?.[0]; - - if (!spaceDb) { - spaceDb = (await dbTx.insert(spaces).values({ - name, - nameSha256: sha256SpaceName(name.slice(1)), - claim_height: meta.covenant?.claim_height, - status: 'pre-auction', - bid_amount: meta.covenant?.total_burned - }).returning())?.[0]; - newSpaceStatus = 'pre-auction'; - } - - let spacesHistoryDb = await dbTx.select().from(spacesHistory).where(sql`${spacesHistory.spaceId} = ${spaceDb.id}`).orderBy(spacesHistory.id); - - let action = null; - let bid_amount = null; - - - if (meta.bid_value != null) { - action = 'rollout'; - newSpaceStatus = 'auction'; - } else if (spacesHistoryDb.length > 0 && spacesHistoryDb[spacesHistoryDb.length - 1].action === 'bid' && meta.covenant?.type === 'transfer') { - action = 'register'; - newSpaceStatus = 'registered' - } else if (meta.covenant?.type === 'bid') { - action = 'bid'; - bid_amount = meta.covenant.total_burned; - } else if (meta.action != null) { - if (meta.action === 'revoke') - newSpaceStatus = 'revoked'; - action = meta.action; - } - - const historyRecord = (await dbTx.insert(spacesHistory).values({ - spaceName: spaceDb.name, - spaceId: spaceDb.id, - transactionId: transactionsDb.find(x => x.txid === tx.txid).id, - txid: tx.txid, - action, - bid_amount, - meta - }).returning())?.[0]; - - const forUpdate: any = {}; - if (newSpaceStatus) { - forUpdate.status = newSpaceStatus; - forUpdate.spacesHistoryId = historyRecord.id; - } - - if (meta.covenant?.claim_height != null && spaceDb.claim_height != meta.covenant?.claim_height) - forUpdate.claimHeight = meta.covenant.claim_height; - - if (bid_amount != null && bid_amount != spaceDb.bid_amount) - forUpdate.bid_amount = bid_amount; - - if (Object.keys(forUpdate).length) - await dbTx.update(spaces).set({ - ...forUpdate, - updatedAt: sql`now()` - }).where(sql`${spaces.id} = ${spaceDb.id}`); - } - } - }); - } catch (e) { - console.error(e); - throw e; - } - } -} - -export default SimpleIndexer; - -async function sync() { - const LOCK_KEY = 123456; - let startBlockHeight = null; - let endBlockHeight = null; - let startTime = null; - let db = await getDb(); - - try { - const db_lock = await db.execute(sql`SELECT pg_try_advisory_lock(${LOCK_KEY})`); - - if (!db_lock.rows[0].pg_try_advisory_lock) { - console.log(`Another instance is running. Exiting.`); - return; - } - - console.log(`Starting sync ...`); - - startTime = performance.now(); - - const last_sync = (await db.select().from(syncs).orderBy(desc(syncs.endBlockHeight)).limit(1))?.[0]; - let height = last_sync?.endBlockHeight ? (last_sync.endBlockHeight + 1) : Number(process.env.SPACES_STARTING_BLOCKHEIGHT); - startBlockHeight = height; - - const bitcoinClient = new SimpleRpcClient(process.env.BITCOIN_RPC_URL, process.env.BITCOIN_RPC_USER, process.env.BITCOIN_RPC_PASSWORD); - const spacedClient = new SimpleRpcClient(process.env.SPACED_RPC_URL); - const simpleIndexer = new SimpleIndexer(); - const blockCount = await bitcoinClient.request('getblockcount'); - - if (blockCount < height) { - console.log('No new blocks to process.'); - return; - } - - - while (height <= blockCount) { - const blockHash = await bitcoinClient.request('getblockhash', [height]); - - // You can fetch the whole block from bitcoin core - const block = await bitcoinClient.request('getblock', [blockHash]); - - // Spaced indexes transactions relevant to the spaces protocol by the block hash - // if a block does not have any spaces transactions, it will not be stored in the index - const txData = (await spacedClient.request('getblockdata', [blockHash]))?.tx_data ?? []; - - await simpleIndexer.processBlock(block, txData, db); - endBlockHeight = height; - height++; - } - - const stats = await db.select().from(blockStats); - if (!stats.length) - await db.insert(blockStats).values({ blockHeight: blockCount }); - else - await db.update(blockStats).set({ blockHeight: blockCount, updatedAt: sql`now()` }); - - } catch (error) { - console.error(error); - } finally { - if (startBlockHeight != null && endBlockHeight != null) { - await db.insert(syncs).values({ - startBlockHeight, - endBlockHeight, - durationSeconds: Math.round((performance.now() - startTime) / 1000), - }); - - console.log(`Synced blocks from ${startBlockHeight} to ${endBlockHeight}. Duration: ${Math.round((performance.now() - startTime) / 1000)} seconds.`); - } - - await db.execute(sql`SELECT pg_advisory_unlock(${LOCK_KEY})`); - db.end(); - } -} - - -console.log('scheduling syncs'); -cron.schedule('* * * * *', async () => { - console.log('Cron: starting sync...'); - await sync().catch(console.error); -}); - -// sync().catch(console.error); \ No newline at end of file diff --git a/indexer/src/rpc.ts b/indexer/src/rpc.ts deleted file mode 100644 index fdb5ddf..0000000 --- a/indexer/src/rpc.ts +++ /dev/null @@ -1,56 +0,0 @@ -import fetch from 'node-fetch'; - -export class SimpleRpcClient { - private rpcUrl: string; - private authHeader: string; - private id: number; - - constructor(rpcUrl, rpcUser = '', rpcPassword = '') { - this.rpcUrl = rpcUrl; - if (rpcUser && rpcPassword) { - this.authHeader = 'Basic ' + Buffer.from(`${rpcUser}:${rpcPassword}`).toString('base64'); - } - this.id = 1; - } - - async request(method, params = []) { - const rpcRequest = { - jsonrpc: '2.0', - id: this.id++, - method: method, - params: params - }; - - let headers = { - 'Content-Type': 'application/json', - }; - if (this.authHeader) { - headers['Authorization'] = this.authHeader; - } - - try { - const response = await fetch(this.rpcUrl, { - method: 'POST', - headers, - body: JSON.stringify(rpcRequest) - }); - - const responseText = await response.text(); - let data; - try { - data = JSON.parse(responseText); - } catch (parseError) { - console.error('Error parsing JSON:', parseError); - throw new Error(`Failed to parse response: ${responseText}`); - } - - if (data.error) { - throw new Error(`RPC error: ${JSON.stringify(data.error)}`); - } - - return data.result; - } catch (error) { - throw error; - } - } -} diff --git a/indexer/src/schema.ts b/indexer/src/schema.ts deleted file mode 100644 index bee1280..0000000 --- a/indexer/src/schema.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { serial, text, pgTable, integer, bigint, numeric, timestamp, jsonb } from 'drizzle-orm/pg-core'; -import { relations } from 'drizzle-orm'; - - -export const blocks = pgTable('blocks', { - id: serial('id').primaryKey(), - hash: text('hash').notNull().unique(), - confirmations: integer('confirmations').notNull(), - height: bigint('height', { mode: 'number' }).notNull(), - version: bigint('version', { mode: 'number' }).notNull(), - merkleroot: text('merkleroot').notNull(), - time: bigint('time', { mode: 'number' }).notNull(), - mediantime: bigint('mediantime', { mode: 'number' }).notNull(), - nonce: bigint('nonce', { mode: 'number' }).notNull(), - bits: text('bits').notNull(), - difficulty: numeric('difficulty').notNull(), - chainwork: text('chainwork').notNull(), - nTx: integer('n_tx').notNull(), - previousblockhash: text('previousblockhash').notNull(), - nextblockhash: text('nextblockhash'), - strippedsize: integer('strippedsize').notNull(), - size: bigint('size', { mode: 'number' }).notNull(), - weight: bigint('weight', { mode: 'number' }).notNull(), - createdAt: timestamp('created_at').notNull().defaultNow(), - updatedAt: timestamp('updated_at') -}); - -export const blocksRelations = relations(blocks, ({ many }) => ({ - transactions: many(transactions), -})); - -export const transactions = pgTable('transactions', { - id: serial('id').primaryKey(), - txid: text('txid').notNull().unique(), - version: integer('version').notNull(), - blockId: integer('block_id').notNull().references(() => blocks.id), - blockHash: text('block_hash').notNull().references(() => blocks.hash), - data: jsonb('data').notNull(), - createdAt: timestamp('created_at').notNull().defaultNow(), - updatedAt: timestamp('updated_at') -}); - -export const transactionsRelations = relations(transactions, ({ one, many }) => ({ - block: one(blocks, { - fields: [transactions.blockId], - references: [blocks.id], - }), - spaceHistories: many(spacesHistory), -})); - -export const spaces = pgTable('spaces', { - id: serial('id').primaryKey(), - name: text('name').notNull().unique(), - nameSha256: text('name_sha256').notNull(), - status: text('status').notNull(), - bid_amount: integer('bid_amount'), - claimHeight: integer('claim_height'), - spacesHistoryId: integer('spaces_history_id').references(() => spacesHistory.id, { onDelete: 'cascade' }), - createdAt: timestamp('created_at').notNull().defaultNow(), - updatedAt: timestamp('updated_at') -}); - -export const spacesRelations = relations(spaces, ({ many }) => ({ - history: many(spacesHistory), -})); - -export const spacesHistory = pgTable('spaces_history', { - id: serial('id').primaryKey(), - spaceName: text('space_name').notNull().references(() => spaces.name, { onDelete: 'cascade' }), - spaceId: integer('space_id').notNull().references(() => spaces.id, { onDelete: 'cascade' }), - transactionId: integer('transaction_id').notNull().references(() => transactions.id), - txid: text('txid').notNull().references(() => transactions.txid), - action: text('action'), - bid_amount: integer('bid_amount'), - meta: jsonb('meta'), - createdAt: timestamp('created_at').notNull().defaultNow(), - updatedAt: timestamp('updated_at') -}); - -export const spacesHistoryRelations = relations(spacesHistory, ({ one }) => ({ - space: one(spaces, { - fields: [spacesHistory.spaceId], - references: [spaces.id], - }), - transaction: one(transactions, { - fields: [spacesHistory.transactionId], - references: [transactions.id], - }), -})); - -export const syncs = pgTable('syncs', { - id: serial('id').primaryKey(), - startBlockHeight: integer('start_block_height').notNull(), - endBlockHeight: integer('end_block_height').notNull(), - durationSeconds: integer('duration_seconds').notNull(), - createdAt: timestamp('created_at').notNull().defaultNow() -}); - -export const blockStats = pgTable('block_stats', { - id: serial('id').primaryKey(), - blockHeight: integer('block_height'), - createdAt: timestamp('created_at').notNull().defaultNow(), - updatedAt: timestamp('updated_at') -}); \ No newline at end of file diff --git a/indexer/src/utils.ts b/indexer/src/utils.ts deleted file mode 100644 index d1a5266..0000000 --- a/indexer/src/utils.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { bech32m } from 'bech32'; -import crypto from 'crypto'; - -export function decodeScriptPubKeyToTaprootAddress(scriptPubKey, network = 'mainnet') { - if (!scriptPubKey.startsWith('5120') || scriptPubKey.length !== 68) { - throw new Error('Invalid P2TR ScriptPubKey'); - } - - // Extract the 32-byte public key (after '5120') - const pubkeyHex = scriptPubKey.slice(4); - const pubkeyBytes = Buffer.from(pubkeyHex, 'hex'); - - // Determine the HRP (Human-Readable Part) based on the network - const hrp = network === 'mainnet' ? 'bc' : 'tb'; - - // Convert bytes to 5-bit groups (Bech32m) - const pubkeyBits = bech32m.toWords(pubkeyBytes); - - // Create the address with a version 1 witness program - const address = bech32m.encode(hrp, [1].concat(pubkeyBits)); - - return address; -} - -export function sha256SpaceName(spaceName) { - const byteArray = Buffer.from(spaceName, 'utf8'); - const lengthPrefix = Buffer.from([byteArray.length]); - const lengthPrefixedByteArray = Buffer.concat([lengthPrefix, byteArray]); - const finalByteArray = Buffer.concat([lengthPrefixedByteArray, Buffer.from([0])]); - const sha256Hash = crypto.createHash('sha256').update(finalByteArray).digest('hex'); - - return sha256Hash; -} \ No newline at end of file diff --git a/indexer/tsconfig.json b/indexer/tsconfig.json deleted file mode 100644 index 97d282c..0000000 --- a/indexer/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "esnext", - "module": "nodenext", - "moduleResolution": "nodenext", - "outDir": "./dist", - "rootDir": "./src", - "strict": false, - "skipLibCheck": true, - "esModuleInterop": true, - }, - "include": ["src"], - "exclude": ["node_modules"] -} diff --git a/explorer/package-lock.json b/package-lock.json similarity index 99% rename from explorer/package-lock.json rename to package-lock.json index f4eea6f..427a311 100644 --- a/explorer/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "@types/pg": "^8.11.6", "autoprefixer": "^10.4.19", "daisyui": "^4.12.10", - "drizzle-kit": "^0.22.7", + "drizzle-kit": "^0.22.8", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.36.0", @@ -2641,9 +2641,9 @@ } }, "node_modules/drizzle-kit": { - "version": "0.22.7", - "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.22.7.tgz", - "integrity": "sha512-9THPCb2l1GPt7wxhws9LvTR0YG565ZlVgTuqGMwjs590Kch1pXu4GyjEArVijSF5m0OBj3qgdeKmuJXhKXgWFw==", + "version": "0.22.8", + "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.22.8.tgz", + "integrity": "sha512-VjI4wsJjk3hSqHSa3TwBf+uvH6M6pRHyxyoVbt935GUzP9tUR/BRZ+MhEJNgryqbzN2Za1KP0eJMTgKEPsalYQ==", "dev": true, "dependencies": { "@esbuild-kit/esm-loader": "^2.5.5", diff --git a/explorer/package.json b/package.json similarity index 98% rename from explorer/package.json rename to package.json index 00dbe59..1be5b66 100644 --- a/explorer/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@types/pg": "^8.11.6", "autoprefixer": "^10.4.19", "daisyui": "^4.12.10", - "drizzle-kit": "^0.22.7", + "drizzle-kit": "^0.22.8", "eslint": "^9.0.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-svelte": "^2.36.0", diff --git a/explorer/postcss.config.js b/postcss.config.js similarity index 100% rename from explorer/postcss.config.js rename to postcss.config.js diff --git a/explorer/server.js b/server.js similarity index 100% rename from explorer/server.js rename to server.js diff --git a/explorer/src/app.css b/src/app.css similarity index 100% rename from explorer/src/app.css rename to src/app.css diff --git a/explorer/src/app.d.ts b/src/app.d.ts similarity index 100% rename from explorer/src/app.d.ts rename to src/app.d.ts diff --git a/explorer/src/app.html b/src/app.html similarity index 100% rename from explorer/src/app.html rename to src/app.html diff --git a/explorer/src/routes/block/[hash]/+page.svelte b/src/lib/components/Block.svelte similarity index 65% rename from explorer/src/routes/block/[hash]/+page.svelte rename to src/lib/components/Block.svelte index ba47b08..91b8b37 100644 --- a/explorer/src/routes/block/[hash]/+page.svelte +++ b/src/lib/components/Block.svelte @@ -1,9 +1,11 @@ @@ -14,7 +16,11 @@
- {data.confirmations} + {data.height} + Height +
+
+ {data.max_height-data.height} Confirmations
@@ -34,24 +40,24 @@ Nonce
- {data.merkleroot} + {data.hash_merkle_root} Merkle Root
- {#each data.transactions as tx} + {#each data.transactions as tx, tx_index}
-

Tx # {tx.txid}

+

Tx#{tx_index} {tx.txid}

Inputs

- {#each tx.data.vin ?? [] as vin} + {#each tx.tx_inputs ?? [] as tx_input}
- {#if vin.previous_output?.split(':')[0].replaceAll('0', '').length == 0} - Coinbase + {#if tx_input && tx_input.coinbase.length > 0} + Coinbase input {:else} - {vin.previous_output} + {tx_input.hash_prevout} {/if}
{/each} @@ -60,21 +66,18 @@

Outputs

- {#each tx.spaceHistories as event} - -
- {event.action[0].toUpperCase() + event.action.slice(1)} - {event.spaceName} - - {event.action == 'reject' ? ` ${event.meta.reason}` - : event.action == 'bid' ? ` ${numberFormatter.format(event.bid_amount)} sats` : "" - } -
- - {/each} + {#each tx.tx_outputs as tx_output} +
+ + + {tx_output.value} satoshi + +
+ {/each} +
-
- {/each} + {/each} +
- \ No newline at end of file + diff --git a/explorer/src/lib/components/Countdown.svelte b/src/lib/components/Countdown.svelte similarity index 100% rename from explorer/src/lib/components/Countdown.svelte rename to src/lib/components/Countdown.svelte diff --git a/explorer/src/lib/components/SortSelector.svelte b/src/lib/components/SortSelector.svelte similarity index 100% rename from explorer/src/lib/components/SortSelector.svelte rename to src/lib/components/SortSelector.svelte diff --git a/explorer/src/lib/components/Spinner.svelte b/src/lib/components/Spinner.svelte similarity index 100% rename from explorer/src/lib/components/Spinner.svelte rename to src/lib/components/Spinner.svelte diff --git a/explorer/src/lib/components/ThemeToggle.svelte b/src/lib/components/ThemeToggle.svelte similarity index 100% rename from explorer/src/lib/components/ThemeToggle.svelte rename to src/lib/components/ThemeToggle.svelte diff --git a/explorer/src/routes/tx/[id]/+page.svelte b/src/lib/components/Transaction.svelte similarity index 67% rename from explorer/src/routes/tx/[id]/+page.svelte rename to src/lib/components/Transaction.svelte index 1ae445b..087e7df 100644 --- a/explorer/src/routes/tx/[id]/+page.svelte +++ b/src/lib/components/Transaction.svelte @@ -7,13 +7,15 @@ export let data; + + - \ No newline at end of file + diff --git a/explorer/src/lib/db.ts b/src/lib/db.ts similarity index 100% rename from explorer/src/lib/db.ts rename to src/lib/db.ts diff --git a/explorer/src/lib/index.ts b/src/lib/index.ts similarity index 100% rename from explorer/src/lib/index.ts rename to src/lib/index.ts diff --git a/explorer/src/lib/request-validation.ts b/src/lib/request-validation.ts similarity index 100% rename from explorer/src/lib/request-validation.ts rename to src/lib/request-validation.ts diff --git a/src/lib/schema.ts b/src/lib/schema.ts new file mode 100644 index 0000000..3cf667d --- /dev/null +++ b/src/lib/schema.ts @@ -0,0 +1,200 @@ +import { pgTable, pgEnum, serial, bigint, boolean, timestamp, unique, integer, doublePrecision, uniqueIndex, foreignKey, primaryKey, index, text, customType} from "drizzle-orm/pg-core" + +import db from "$lib/db"; +import { sql } from "drizzle-orm"; +import { relations } from "drizzle-orm/relations"; + + +export const covenant_action = pgEnum("covenant_action", ['RESERVE', 'BID', 'TRANSFER']) + +const bytea = customType({ + dataType() { return "bytea" }, + fromDriver(value: unknown): string { + //Why it doesn't work without this? sometimes hexstring, sometimes buffer + if (typeof value === 'string' && value.startsWith('\\x')) { + return value.slice(2) + } + return value.toString('hex') + // return value as Buffer; + }, + toDriver(value: Buffer): Buffer { + console.log("in to driver", value) + return Buffer.from(value, 'hex') + + // return value as Buffer; + }, +}); + +export const goose_db_version = pgTable("goose_db_version", { + id: serial("id").primaryKey().notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + version_id: bigint("version_id", { mode: "number" }).notNull(), + is_applied: boolean("is_applied").notNull(), + tstamp: timestamp("tstamp", { mode: 'string' }).defaultNow(), +}); + +export const blocks = pgTable("blocks", { + hash: bytea("hash").primaryKey().notNull(), + size: bigint("size", { mode: "number" }).notNull(), + stripped_size: bigint("stripped_size", { mode: "number" }).notNull(), + weight: integer("weight").notNull(), + height: integer("height").notNull(), + version: integer("version").notNull(), + hash_merkle_root: bytea("hash_merkle_root").notNull(), + time: integer("time").notNull(), + median_time: integer("median_time").notNull(), + nonce: bigint("nonce", { mode: "number" }).notNull(), + bits: bytea("bits").notNull(), + difficulty: doublePrecision("difficulty").notNull(), + chainwork: bytea("chainwork").notNull(), + orphan: boolean("orphan").default(false).notNull(), +}, +(table) => { + return { + blocks_height_key: unique("blocks_height_key").on(table.height), + } +}); + +export const transactions = pgTable("transactions", { + txid: bytea("txid").primaryKey().notNull(), + tx_hash: bytea("tx_hash"), + version: integer("version").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + size: bigint("size", { mode: "number" }).notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + vsize: bigint("vsize", { mode: "number" }).notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + weight: bigint("weight", { mode: "number" }).notNull(), + locktime: integer("locktime").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + fee: bigint("fee", { mode: "number" }).notNull(), + block_hash: bytea("block_hash").references(() => blocks.hash, { onDelete: "cascade" } ), + index: integer("index"), +}, +(table) => { + return { + block_hash_idx: uniqueIndex("transactions_block_hash_index").using("btree", table.block_hash, table.index).where(sql`(block_hash IS NOT NULL)`), + } +}); + +export const tx_outputs = pgTable("tx_outputs", { + block_hash: bytea("block_hash").notNull().references(() => blocks.hash, { onDelete: "cascade" } ), + txid: bytea("txid").notNull().references(() => transactions.txid, { onDelete: "cascade" } ), + index: integer("index").notNull(), + // You can use { mode: "bigint" } if numbers are exceeding js number limitations + value: bigint("value", { mode: "number" }).notNull(), + scriptpubkey: bytea("scriptpubkey"), +}, +(table) => { + return { + tx_outputs_pkey: primaryKey({ columns: [table.block_hash, table.txid, table.index], name: "tx_outputs_pkey"}), + } +}); + +export const tx_inputs = pgTable("tx_inputs", { + block_hash: bytea("block_hash").notNull().references(() => blocks.hash, { onDelete: "cascade" } ), + txid: bytea("txid").notNull().references(() => transactions.txid, { onDelete: "cascade" } ), + index: bigint("index", { mode: "number" }).notNull(), + hash_prevout: bytea("hash_prevout"), + index_prevout: bigint("index_prevout", { mode: "number" }).notNull(), + sequence: bigint("sequence", { mode: "number" }).notNull(), + coinbase: bytea("coinbase"), + txinwitness: bytea("txinwitness").array(), +}, +(table) => { + return { + hash_prevout_idx: index("tx_inputs_hash_prevout_index").using("btree", table.hash_prevout, table.index_prevout).where(sql`(hash_prevout IS NOT NULL)`), + txid_idx: index().using("btree", table.txid), + tx_inputs_pkey: primaryKey({ columns: [table.block_hash, table.txid, table.index], name: "tx_inputs_pkey"}), + } +}); + +export const vmetaouts = pgTable("vmetaouts", { + block_hash: bytea("block_hash").notNull().references(() => blocks.hash, { onDelete: "cascade" } ), + txid: bytea("txid").notNull().references(() => transactions.txid, { onDelete: "cascade" } ), + tx_index: bigint("tx_index", { mode: "number" }).notNull(), + outpoint_txid: bytea("outpoint_txid").notNull().references(() => transactions.txid), + outpoint_index: bigint("outpoint_index", { mode: "number" }).notNull(), + name: text("name").notNull(), + burn_increment: bigint("burn_increment", { mode: "number" }), + covenant_action: covenant_action("covenant_action").notNull(), + claim_height: bigint("claim_height", { mode: "number" }), + expire_height: bigint("expire_height", { mode: "number" }), +}, +(table) => { + return { + vmetaouts_pkey: primaryKey({ columns: [table.block_hash, table.txid, table.tx_index], name: "vmetaouts_pkey"}), + } +}); + +export const transactionsRelations = relations(transactions, ({one, many}) => ({ + block: one(blocks, { + fields: [transactions.block_hash], + references: [blocks.hash] + }), + tx_outputs: many(tx_outputs), + tx_inputs: many(tx_inputs), + vmetaouts_txid: many(vmetaouts, { + relationName: "vmetaouts_txid_transactions_txid" + }), + vmetaouts_outpoint_txid: many(vmetaouts, { + relationName: "vmetaouts_outpoint_txid_transactions_txid" + }), +})); + +export const blocksRelations = relations(blocks, ({many}) => ({ + transactions: many(transactions), + tx_outputs: many(tx_outputs), + tx_inputs: many(tx_inputs), + vmetaouts: many(vmetaouts), +})); + +export const tx_outputsRelations = relations(tx_outputs, ({one}) => ({ + block: one(blocks, { + fields: [tx_outputs.block_hash], + references: [blocks.hash] + }), + transaction: one(transactions, { + fields: [tx_outputs.txid], + references: [transactions.txid] + }), +})); + +export const tx_inputsRelations = relations(tx_inputs, ({one}) => ({ + block: one(blocks, { + fields: [tx_inputs.block_hash], + references: [blocks.hash] + }), + transaction: one(transactions, { + fields: [tx_inputs.txid], + references: [transactions.txid] + }), +})); + +export const vmetaoutsRelations = relations(vmetaouts, ({one}) => ({ + block: one(blocks, { + fields: [vmetaouts.block_hash], + references: [blocks.hash] + }), + transaction_txid: one(transactions, { + fields: [vmetaouts.txid], + references: [transactions.txid], + relationName: "vmetaouts_txid_transactions_txid" + }), + transaction_outpoint_txid: one(transactions, { + fields: [vmetaouts.outpoint_txid], + references: [transactions.txid], + relationName: "vmetaouts_outpoint_txid_transactions_txid" + }), +})); + +export async function getMaxBlockHeight() { + const result = await db.execute(sql` + SELECT COALESCE(MAX(height), -1)::integer AS max_height + FROM blocks + `); + + return result.rows[0].max_height; + + +} diff --git a/explorer/src/lib/schema.ts b/src/lib/schema_old.ts similarity index 100% rename from explorer/src/lib/schema.ts rename to src/lib/schema_old.ts diff --git a/explorer/src/lib/statusMeta.ts b/src/lib/statusMeta.ts similarity index 100% rename from explorer/src/lib/statusMeta.ts rename to src/lib/statusMeta.ts diff --git a/explorer/src/lib/utils.ts b/src/lib/utils.ts similarity index 100% rename from explorer/src/lib/utils.ts rename to src/lib/utils.ts diff --git a/src/params/hash.ts b/src/params/hash.ts new file mode 100644 index 0000000..296623e --- /dev/null +++ b/src/params/hash.ts @@ -0,0 +1,3 @@ +export function match(params : string) { + return /^[a-fA-F0-9]{64}$/.test(params); +} diff --git a/src/params/height.ts b/src/params/height.ts new file mode 100644 index 0000000..269faaa --- /dev/null +++ b/src/params/height.ts @@ -0,0 +1,3 @@ +export function match(params : string) { + return /^\d+$/.test(params); +} diff --git a/explorer/src/routes/+error.svelte b/src/routes/+error.svelte similarity index 100% rename from explorer/src/routes/+error.svelte rename to src/routes/+error.svelte diff --git a/explorer/src/routes/+layout.server.ts b/src/routes/+layout.server.ts similarity index 100% rename from explorer/src/routes/+layout.server.ts rename to src/routes/+layout.server.ts diff --git a/explorer/src/routes/+layout.svelte b/src/routes/+layout.svelte similarity index 100% rename from explorer/src/routes/+layout.svelte rename to src/routes/+layout.svelte diff --git a/explorer/src/routes/+page.server.ts b/src/routes/+page.server.ts similarity index 100% rename from explorer/src/routes/+page.server.ts rename to src/routes/+page.server.ts diff --git a/explorer/src/routes/+page.svelte b/src/routes/+page.svelte similarity index 99% rename from explorer/src/routes/+page.svelte rename to src/routes/+page.svelte index 5b74641..67bb00f 100644 --- a/explorer/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -41,6 +41,7 @@ {#if $navigating}
Loading...
{:else} + {#each data.spaces as space}
0`).orderBy(sql`similarity(${spaces.name}, ${search}) desc`).limit(3); + } + + // const blockResult + + // const result = await db.select().from(spaces).where(sql`similarity(${spaces.name}, ${search}) > 0`).orderBy(sql`similarity(${spaces.name}, ${search}) desc`).limit(3); + + return json(result); +} diff --git a/src/routes/api/spaces/+server.ts b/src/routes/api/spaces/+server.ts new file mode 100644 index 0000000..295a055 --- /dev/null +++ b/src/routes/api/spaces/+server.ts @@ -0,0 +1,67 @@ +import db from '$lib/db'; +import { error, json } from '@sveltejs/kit'; +import { type RequestHandler } from '@sveltejs/kit'; +import { sql, asc, desc } from 'drizzle-orm'; +// import { spaces, spacesHistory, blockStats } from '$lib/schema'; +import { vmetaouts, blocks, spacesHistory } from '$lib/schema'; + +export const GET: RequestHandler = async function ({ request, url }) { + const status = url.searchParams.get('status'); + const sortBy = url.searchParams.get('sort'); + const direction = url.searchParams.get('direction'); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let orderBy: any; + + // if (sortBy == 'ending') { + // orderBy = sql`case when ${spaces.claimHeight} > (select block_height from ${blockStats}) then 1 else 2 end, ${spaces.claimHeight}` + // } else if (sortBy == 'price') { + // orderBy = [direction === 'desc' ? desc(spaces.bid_amount) : asc(spaces.bid_amount)]; + // } else if (sortBy == 'register_date') { + // orderBy = [direction === 'desc' ? desc(spaces.spacesHistoryId) : asc(spaces.spacesHistoryId)]; + // } + +const latestVmetaouts = await db.query.vmetaouts.findMany({ + limit: 5, + // with: { + // blocks: true + // }, +}); + // blockHash: vmetaouts.block_hash, + // txid: vmetaouts.txid, + // txIndex: vmetaouts.tx_index, + // name: vmetaouts.name, + // covenantAction: vmetaouts.covenant_action, + // claimHeight: vmetaouts.claim_height, + // expireHeight: vmetaouts.expire_height, + // blockHeight: blocks.height + // }) + // .from(vmetaouts) + // + // // .innerJoin(blocks, blocks.hash.eq(vmetaouts.block_hash)) + // // .orderBy(blocks.height.desc()) + // .limit(10); + + // const spacesDb = await db.query.vmetaouts.findMany({ + // where: status ? sql`${vmetaouts.status} = ${status}`: sql`1=1`, + // extras: { rank: sql`dense_rank() over(order by ${spaces.bid_amount} desc, ${spaces.nameSha256})`.as('rank') }, + // with: { + // history: { + // columns: { id: true, action: true, bid_amount: true }, + // with: { + // transaction: { + // columns: { id: true, txid: true }, + // with: { + // block: { columns: { time: true } } + // } + // } + // }, + // orderBy: (history) => history.id, + // }, + // }, + // orderBy + // }); +// console.log(latestVmetaouts) + return json(latestVmetaouts ?? []); + // return json(spacesDb ?? []); +} diff --git a/explorer/src/routes/api/spaces/[name]/+server.ts b/src/routes/api/spaces/[name]/+server.ts similarity index 100% rename from explorer/src/routes/api/spaces/[name]/+server.ts rename to src/routes/api/spaces/[name]/+server.ts diff --git a/src/routes/api/transactions/[txid]/+server.ts b/src/routes/api/transactions/[txid]/+server.ts new file mode 100644 index 0000000..28849f9 --- /dev/null +++ b/src/routes/api/transactions/[txid]/+server.ts @@ -0,0 +1,76 @@ +import db from '$lib/db'; +import { error, json } from '@sveltejs/kit'; +import { type RequestHandler } from '@sveltejs/kit'; +import { sql } from 'drizzle-orm'; +import { transactions } from '$lib/schema'; + +export const GET: RequestHandler = async function ({ params }) { + // const blockDb = await db.query.blocks.findFirst({ + // + // where: sql`${blocks.height} = ${params.height}`, + // with: { + // transactions: { + // with: { + // tx_inputs: { + // columns: { + // block_hash: false, + // txid: false + // } + // }, + // tx_outputs: { + // columns: { + // block_hash: false, + // txid: false + // }, + // }, + // + // }, + // }, + // } + // }) + const txid = Buffer.from(params.txid, 'hex') + + const transaction = await db.query.transactions.findFirst({ + where: sql`${transactions.txid} = ${txid}`, + with: { + block: { + columns: { + time: true, + height: true, + }, + }, + tx_inputs: { + columns: { + block_hash: false + } + }, + tx_outputs: { + columns: { + block_hash: false + }, + }, + }, + extras: { + max_height: sql`( SELECT COALESCE(MAX(height), -1) FROM blocks)::integer`.as('max_height'), + } + }); + + if (!transaction) + return error(404, 'Transaction not found'); + + //TODO add to the schema field spender + transaction.confirmations = transaction.max_height - transaction.block.height + for (const [index, tx_output] of transaction.tx_outputs.entries()) { + const spender_txid = await db.query.tx_inputs.findFirst({ + where: sql`hash_prevout = ${txid} and index_prevout = ${index}`, + columns: { + txid: true + }, + }) + if (spender_txid) { + tx_output.spender = spender_txid.txid + } + } + + return json(transaction); +} diff --git a/explorer/src/routes/block/[hash]/+page.server.ts b/src/routes/block/[hash=hash]/+page.server.ts similarity index 82% rename from explorer/src/routes/block/[hash]/+page.server.ts rename to src/routes/block/[hash=hash]/+page.server.ts index 3788d54..454a0f6 100644 --- a/explorer/src/routes/block/[hash]/+page.server.ts +++ b/src/routes/block/[hash=hash]/+page.server.ts @@ -1,6 +1,7 @@ import { fail, type Actions, type ServerLoad } from '@sveltejs/kit'; export const load: ServerLoad = async ({ fetch, locals, params }) => { + console.log("in block hash page server ts") const block = await fetch(`/api/blocks/${params.hash}`).then(x => x.json()); return block; -}; \ No newline at end of file +}; diff --git a/src/routes/block/[hash=hash]/+page.svelte b/src/routes/block/[hash=hash]/+page.svelte new file mode 100644 index 0000000..af769a4 --- /dev/null +++ b/src/routes/block/[hash=hash]/+page.svelte @@ -0,0 +1,11 @@ + + + diff --git a/src/routes/block/[height=height]/+page.server.ts b/src/routes/block/[height=height]/+page.server.ts new file mode 100644 index 0000000..1b4d4ef --- /dev/null +++ b/src/routes/block/[height=height]/+page.server.ts @@ -0,0 +1,6 @@ +import { type ServerLoad } from '@sveltejs/kit'; + +export const load: ServerLoad = async ({ fetch, locals, params }) => { + const block = await fetch(`/api/blocks/${params.height}`).then(x => x.json()); + return block; +}; diff --git a/src/routes/block/[height=height]/+page.svelte b/src/routes/block/[height=height]/+page.svelte new file mode 100644 index 0000000..af769a4 --- /dev/null +++ b/src/routes/block/[height=height]/+page.svelte @@ -0,0 +1,11 @@ + + + diff --git a/explorer/src/routes/past/+page.server.ts b/src/routes/past/+page.server.ts similarity index 100% rename from explorer/src/routes/past/+page.server.ts rename to src/routes/past/+page.server.ts diff --git a/explorer/src/routes/past/+page.svelte b/src/routes/past/+page.svelte similarity index 100% rename from explorer/src/routes/past/+page.svelte rename to src/routes/past/+page.svelte diff --git a/explorer/src/routes/space/[id]/+page.svelte b/src/routes/space/[id]/+page.svelte similarity index 100% rename from explorer/src/routes/space/[id]/+page.svelte rename to src/routes/space/[id]/+page.svelte diff --git a/explorer/src/routes/space/[id]/+page.ts b/src/routes/space/[id]/+page.ts similarity index 100% rename from explorer/src/routes/space/[id]/+page.ts rename to src/routes/space/[id]/+page.ts diff --git a/explorer/src/routes/tx/[id]/+page.server.ts b/src/routes/tx/[txid]/+page.server.ts similarity index 69% rename from explorer/src/routes/tx/[id]/+page.server.ts rename to src/routes/tx/[txid]/+page.server.ts index e2038cc..36c50d1 100644 --- a/explorer/src/routes/tx/[id]/+page.server.ts +++ b/src/routes/tx/[txid]/+page.server.ts @@ -3,14 +3,15 @@ import { object, string } from 'zod'; import { PUBLIC_BTC_NETWORK } from "$env/static/public"; export const load: ServerLoad = async ({ fetch, locals, params }) => { - const transaction = await fetch(`/api/transactions/${params.id}`); + const transaction = await fetch(`/api/transactions/${params.txid}`); if (transaction.status != 200) error(transaction.status, { message: 'Transaction not found'}); const data = await transaction.json(); - const testnet = PUBLIC_BTC_NETWORK == "testnet4" ? "testnet4/" : ""; - if (!data.spaceHistories.length) - redirect(302, `https://mempool.space/${testnet}tx/${params.id}`); + + // const testnet = PUBLIC_BTC_NETWORK == "testnet4" ? "testnet4/" : ""; + // if (!data.spaceHistories.length) + // redirect(302, `https://mempool.space/${testnet}tx/${params.txid}`); return data; -}; \ No newline at end of file +}; diff --git a/src/routes/tx/[txid]/+page.svelte b/src/routes/tx/[txid]/+page.svelte new file mode 100644 index 0000000..79057c6 --- /dev/null +++ b/src/routes/tx/[txid]/+page.svelte @@ -0,0 +1,6 @@ + + + diff --git a/explorer/src/routes/upcoming/+page.server.ts b/src/routes/upcoming/+page.server.ts similarity index 100% rename from explorer/src/routes/upcoming/+page.server.ts rename to src/routes/upcoming/+page.server.ts diff --git a/explorer/src/routes/upcoming/+page.svelte b/src/routes/upcoming/+page.svelte similarity index 100% rename from explorer/src/routes/upcoming/+page.svelte rename to src/routes/upcoming/+page.svelte diff --git a/explorer/static/action/bid.svg b/static/action/bid.svg similarity index 100% rename from explorer/static/action/bid.svg rename to static/action/bid.svg diff --git a/explorer/static/action/reject.svg b/static/action/reject.svg similarity index 100% rename from explorer/static/action/reject.svg rename to static/action/reject.svg diff --git a/explorer/static/action/revoke.svg b/static/action/revoke.svg similarity index 100% rename from explorer/static/action/revoke.svg rename to static/action/revoke.svg diff --git a/explorer/static/action/rollout.svg b/static/action/rollout.svg similarity index 100% rename from explorer/static/action/rollout.svg rename to static/action/rollout.svg diff --git a/explorer/static/action/transfer.svg b/static/action/transfer.svg similarity index 100% rename from explorer/static/action/transfer.svg rename to static/action/transfer.svg diff --git a/explorer/static/arrow-right.svg b/static/arrow-right.svg similarity index 100% rename from explorer/static/arrow-right.svg rename to static/arrow-right.svg diff --git a/explorer/static/favicon.png b/static/favicon.png similarity index 100% rename from explorer/static/favicon.png rename to static/favicon.png diff --git a/explorer/static/logo.png b/static/logo.png similarity index 100% rename from explorer/static/logo.png rename to static/logo.png diff --git a/explorer/svelte.config.js b/svelte.config.js similarity index 100% rename from explorer/svelte.config.js rename to svelte.config.js diff --git a/explorer/tailwind.config.js b/tailwind.config.js similarity index 100% rename from explorer/tailwind.config.js rename to tailwind.config.js diff --git a/explorer/tsconfig.json b/tsconfig.json similarity index 100% rename from explorer/tsconfig.json rename to tsconfig.json diff --git a/explorer/vite.config.ts b/vite.config.ts similarity index 100% rename from explorer/vite.config.ts rename to vite.config.ts From 80216893728648a2108bbb832bca54573f59c753 Mon Sep 17 00:00:00 2001 From: Alexander Shevtsov Date: Wed, 16 Oct 2024 00:37:33 +0200 Subject: [PATCH 02/78] rework of frontend with new indexer --- README.md | 6 +- package-lock.json | 14 ++ package.json | 1 + src/lib/components/Block.svelte | 61 ++--- src/lib/components/BlockTxs.svelte | 17 ++ src/lib/components/Footer.svelte | 24 ++ src/lib/components/Pagination.svelte | 108 +++++++++ src/lib/components/Transaction.css | 116 +++++++++ src/lib/components/Transaction.svelte | 85 +++---- src/lib/components/TransactionDetails.css | 55 +++++ src/lib/components/TransactionDetails.svelte | 66 ++++++ src/lib/components/TransactionLink.svelte | 13 ++ src/lib/db.ts | 1 + src/lib/links.ts | 17 ++ src/lib/types/api.ts | 73 ++++++ src/lib/utils.ts | 59 +++-- src/lib/utils/formatters.ts | 8 + src/routes/+layout.svelte | 154 +++++++----- src/routes/+page.server.ts | 3 +- src/routes/+page.svelte | 3 +- .../api/block/[hash=hash]/header/+server.ts | 47 ++++ .../api/block/[hash=hash]/txs/+server.ts | 98 ++++++++ .../block/[height=height]/header/+server.ts | 45 ++++ .../api/block/[height=height]/txs/+server.ts | 94 ++++++++ src/routes/api/block/txs.ts | 87 +++++++ src/routes/api/blocks/[hash=hash]/+server.ts | 156 ++++++++++--- .../api/blocks/[height=height]/+server.ts | 163 ++++++++++--- src/routes/api/blocks/stats/+server.ts | 1 - src/routes/api/search/+server.ts | 48 +++- src/routes/api/spaces/[name]/+server.ts | 79 ++++--- src/routes/api/transactions/[txid]/+server.ts | 135 ++++++----- src/routes/api/tx/ins/+server.ts | 37 +++ src/routes/api/tx/outs/+server.ts | 36 +++ src/routes/block/[hash=hash]/+page.server.ts | 7 - src/routes/block/[hash=hash]/+page.svelte | 72 +++++- .../block/[height=height]/+page.server.ts | 6 - src/routes/block/[height=height]/+page.svelte | 71 +++++- src/routes/space/[id]/+page.svelte | 221 ------------------ src/routes/space/[name]/+page.svelte | 182 +++++++++++++++ src/routes/space/{[id] => [name]}/+page.ts | 9 +- src/routes/space/[name]/styles/SpacePage.css | 28 +++ src/routes/tx/[txid]/+page.server.ts | 6 +- static/favicon.ico | Bin 0 -> 53043 bytes static/footer/github.svg | 1 + static/footer/spacesprotocol.png | Bin 0 -> 53043 bytes static/footer/telegram.svg | 4 + svelte.config.js | 11 +- 47 files changed, 1919 insertions(+), 609 deletions(-) create mode 100644 src/lib/components/BlockTxs.svelte create mode 100644 src/lib/components/Footer.svelte create mode 100644 src/lib/components/Pagination.svelte create mode 100644 src/lib/components/Transaction.css create mode 100644 src/lib/components/TransactionDetails.css create mode 100644 src/lib/components/TransactionDetails.svelte create mode 100644 src/lib/components/TransactionLink.svelte create mode 100644 src/lib/links.ts create mode 100644 src/lib/types/api.ts create mode 100644 src/lib/utils/formatters.ts create mode 100644 src/routes/api/block/[hash=hash]/header/+server.ts create mode 100644 src/routes/api/block/[hash=hash]/txs/+server.ts create mode 100644 src/routes/api/block/[height=height]/header/+server.ts create mode 100644 src/routes/api/block/[height=height]/txs/+server.ts create mode 100644 src/routes/api/block/txs.ts create mode 100644 src/routes/api/tx/ins/+server.ts create mode 100644 src/routes/api/tx/outs/+server.ts delete mode 100644 src/routes/block/[hash=hash]/+page.server.ts delete mode 100644 src/routes/block/[height=height]/+page.server.ts delete mode 100644 src/routes/space/[id]/+page.svelte create mode 100644 src/routes/space/[name]/+page.svelte rename src/routes/space/{[id] => [name]}/+page.ts (66%) create mode 100644 src/routes/space/[name]/styles/SpacePage.css create mode 100644 static/favicon.ico create mode 100644 static/footer/github.svg create mode 100644 static/footer/spacesprotocol.png create mode 100644 static/footer/telegram.svg diff --git a/README.md b/README.md index 809b870..db0c1ca 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ -# SpacesProtocol Explorer & Indexer +# SpacesProtocol Explorer -This is a monorepo containing two projects located in the `explorer` and `indexer` directories. +This is a repo for spaces protocol explorer's frontend. For the indexer please refer to this [repo](https://github.com/spacesprotocol/explorer-indexer). ## Indexer + + Indexer is a script written in TypeScript that runs every minute and fetches new blocks from `bitcoind` daemon, queries `spaced` for information about the fetched blocks and writes the relevant information to the database. ### Prerequisites diff --git a/package-lock.json b/package-lock.json index 427a311..ffea2f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@sveltejs/adapter-node": "^5.2.0", "bech32": "^2.0.0", + "bs58": "^6.0.0", "buffer": "^6.0.3", "dayjs": "^1.11.11", "dotenv": "^16.4.5", @@ -2055,6 +2056,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base-x": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-5.0.0.tgz", + "integrity": "sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==" + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -2185,6 +2191,14 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs58": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-6.0.0.tgz", + "integrity": "sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==", + "dependencies": { + "base-x": "^5.0.0" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", diff --git a/package.json b/package.json index 1be5b66..a3c8903 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "dependencies": { "@sveltejs/adapter-node": "^5.2.0", "bech32": "^2.0.0", + "bs58": "^6.0.0", "buffer": "^6.0.3", "dayjs": "^1.11.11", "dotenv": "^16.4.5", diff --git a/src/lib/components/Block.svelte b/src/lib/components/Block.svelte index 91b8b37..8e6017f 100644 --- a/src/lib/components/Block.svelte +++ b/src/lib/components/Block.svelte @@ -6,78 +6,51 @@ const numberFormatter = new Intl.NumberFormat(); - export let data; + export let blockHeader; diff --git a/src/lib/components/BlockTxs.svelte b/src/lib/components/BlockTxs.svelte new file mode 100644 index 0000000..08d500a --- /dev/null +++ b/src/lib/components/BlockTxs.svelte @@ -0,0 +1,17 @@ + + +
+ {#each blockTxs as tx, tx_index} + + {/each} +
+ diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte new file mode 100644 index 0000000..b7a1e17 --- /dev/null +++ b/src/lib/components/Footer.svelte @@ -0,0 +1,24 @@ + + +
+
+ + +
+
+ + + diff --git a/src/lib/components/Pagination.svelte b/src/lib/components/Pagination.svelte new file mode 100644 index 0000000..b68d7fd --- /dev/null +++ b/src/lib/components/Pagination.svelte @@ -0,0 +1,108 @@ + + + + + + + diff --git a/src/lib/components/Transaction.css b/src/lib/components/Transaction.css new file mode 100644 index 0000000..cd26864 --- /dev/null +++ b/src/lib/components/Transaction.css @@ -0,0 +1,116 @@ +.transaction-container { + display: flex; + flex-direction: column; + flex-grow: 1; + padding: 1.25rem 2.5rem 0; + gap: 1.25rem; +} + +@media (min-width: 768px) { + .transaction-container { + padding: 1.25rem 4rem; + } +} + +.transaction-header { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; + margin-bottom: 1.75rem; +} + +.transaction-title { + font-weight: bold; + font-size: 1.875rem; + line-height: 2.25rem; +} + +.transaction-id { + position: relative; + top: 0.25rem; + word-break: break-all; + color: #6b7280; +} + +.transaction-details { + display: flex; + flex-wrap: wrap; + gap: 2.5rem; +} + +.block-link { + text-decoration: none; +} + +.detail-item { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: 0.25rem; +} + +.detail-value { + font-size: 1.25rem; + line-height: 1.75rem; + color: #ec8e32; + font-weight: 600; +} + +.detail-label { + color: #6b7280; +} + +.transaction-io { + display: flex; + flex-wrap: wrap; + gap: 1.25rem; + margin-top: 2.5rem; +} + +.io-section { + flex-basis: 100%; + flex-grow: 1; +} + +@media (min-width: 1024px) { + .io-section { + flex-basis: 45%; + } +} + +.io-title { + font-size: 1.25rem; + line-height: 1.75rem; + margin-bottom: 0.25rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid #6b7280; +} + +.io-list { + display: flex; + flex-direction: column; + gap: 1rem; + padding: 1.25rem 0.25rem; +} + +.io-item { + display: flex; + align-items: center; +} + +.io-icon { + width: 1rem; + height: 1rem; + margin-right: 1rem; + fill: #ec8e32; +} + +.coinbase-label { + color: #6b7280; +} + +.output-value { + flex-grow: 1; + text-align: right; +} diff --git a/src/lib/components/Transaction.svelte b/src/lib/components/Transaction.svelte index 087e7df..7c2a93c 100644 --- a/src/lib/components/Transaction.svelte +++ b/src/lib/components/Transaction.svelte @@ -1,76 +1,43 @@ - + import './Transaction.css'; + -
-
-

Transaction

- #{data.txid} +
+
+

Transaction

+ #{data.txid}
-
- -
- {data.block.height} - Block +
+ +
+ {data.block.height} + Block
-
- {dayjs.unix(data.block.time).format('lll')} - Time +
+ {dayjs.unix(data.block.time).format('lll')} + Time
-
- {data.version} - Version +
+ {data.version} + Version
-
- {data.locktime} - Lock Time +
+ {data.locktime} + Lock Time
-
- {data.confirmations} - Confirmations -
-
-
-
-

Inputs

-
- {#each data.tx_inputs ?? [] as tx_input} -
- - {#if tx_input && tx_input.coinbase.length > 0} - Coinbase - {:else} - {tx_input.hash_prevout} - {/if} -
- {/each} -
- -
-
-

Outputs

-
- {#each data.tx_outputs as tx_output} -
- - {#if tx_output.spender } - - {tx_output.spender} {tx_output.value} satoshi - - {:else} - Unspent {tx_output.value} satoshi - {/if} -
- {/each} -
+
+ {data.confirmations} + Confirmations
+
diff --git a/src/lib/components/TransactionDetails.css b/src/lib/components/TransactionDetails.css new file mode 100644 index 0000000..fe01624 --- /dev/null +++ b/src/lib/components/TransactionDetails.css @@ -0,0 +1,55 @@ +.transaction-details { + @apply flex flex-wrap gap-5 p-5 md:p-8 bg-[#0b0d10] light:bg-gray-100 rounded-xl; +} + +.transaction-id { + @apply w-full text-gray-500; +} + +.transaction-id-link { + @apply text-gray-300 light:text-gray-600 break-all; +} + +.section { + @apply flex flex-col basis-full lg:basis-[45%] grow; +} + +.section-title { + @apply text-xl mb-1 pb-2 border-b border-b-gray-500; +} + +.section-content { + @apply flex flex-col gap-4 p-5; +} + +.input-content { + @apply pl-1; +} + +.arrow-icon { + @apply w-[16px] h-[16px] mr-4; +} + +.item { + @apply flex items-center; +} + +.output-item { + @apply flex items-center justify-between; +} + +.output-address { + @apply text-gray-500 truncate max-w-[70%]; +} + +.output-value { + @apply text-right; +} + +.spender-info { + @apply text-sm text-gray-500; +} + +.more-items { + @apply text-gray-500; +} diff --git a/src/lib/components/TransactionDetails.svelte b/src/lib/components/TransactionDetails.svelte new file mode 100644 index 0000000..fd4bf95 --- /dev/null +++ b/src/lib/components/TransactionDetails.svelte @@ -0,0 +1,66 @@ + + +
+

+ + + +

+
+
+

Inputs

+
+ {#each transaction.inputs.slice(0, maxInputsOutputs) as input} +
+ {#if input.coinbase} + Coinbase input + {:else} + + {/if} +
+ {/each} + {#if transaction.inputs.length > maxInputsOutputs} +
... and {transaction.inputs.length - maxInputsOutputs} more
+ {/if} +
+
+ + + +
+

Outputs

+
+ {#each transaction.outputs.slice(0, maxInputsOutputs) as output} +
+ + {#if output.address} + {output.address} + {:else} + Scriptpubkey: {output.scriptpubkey.slice(0, 20)}... + {/if} + + {numberFormatter.format(output.value)} satoshi +
+ {#if output.spender} +
+ Spent in: +
+ {/if} + {/each} + {#if transaction.outputs.length > maxInputsOutputs} +
... and {transaction.outputs.length - maxInputsOutputs} more
+ {/if} +
+
+
+
diff --git a/src/lib/components/TransactionLink.svelte b/src/lib/components/TransactionLink.svelte new file mode 100644 index 0000000..33e47dd --- /dev/null +++ b/src/lib/components/TransactionLink.svelte @@ -0,0 +1,13 @@ + + + + {displayTxid} + diff --git a/src/lib/db.ts b/src/lib/db.ts index 5fef352..e34f266 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -6,5 +6,6 @@ import * as schema from '$lib/schema'; const pool = new Pool({ connectionString: DB_URL }); const db = drizzle(pool, { schema }); +// const db = drizzle(pool, { schema , logger:true}); export default db; diff --git a/src/lib/links.ts b/src/lib/links.ts new file mode 100644 index 0000000..860afd5 --- /dev/null +++ b/src/lib/links.ts @@ -0,0 +1,17 @@ +export const footerLinks = [ + { + href: "https://docs.spacesprotocol.org/", + text: "Docs", + image: "/footer/spacesprotocol.png" + }, + { + href: "https://github.com/spacesprotocol/", + text: "GitHub", + image: "/footer/github.svg" + }, + { + href: "https://t.me/spacesprotocol", + text: "Telegram", + image: "/footer/telegram.svg" + }, +]; diff --git a/src/lib/types/api.ts b/src/lib/types/api.ts new file mode 100644 index 0000000..a45adc7 --- /dev/null +++ b/src/lib/types/api.ts @@ -0,0 +1,73 @@ +export type CovenantAction = 'RESERVE' | 'BID' | 'TRANSFER'; + +export type ApiSearchResponse = { + block?: Block; + transaction?: Transaction; + names?: string[]; +}; + +export type Vmetaout = { + block_hash: Bytes; + txid: Bytes; + tx_index: number; + outpoint_txid: Bytes; + outpoint_index: number; + name: string; + burn_increment: number | null; + covenant_action: CovenantAction; + claim_height: number | null; + expire_height: number | null; +}; + +// Translated Bytes type (from Go []byte) +export type Bytes = Uint8Array; + +export type Block = { + hash: Bytes; + size: number; + stripped_size: number; + weight: number; + height: number; + version: number; + hash_merkle_root: Bytes; + time: number; + median_time: number; + nonce: number; + bits: Bytes; + difficulty: number; + chainwork: Bytes; + orphan: boolean; +}; + +export type Transaction = { + txid: Bytes; + tx_hash: Bytes | null; + version: number; + size: number; + vsize: number; + weight: number; + locktime: number; + fee: number; + block_hash: Bytes | null; + index: number | null; +}; + +export type TxInput = { + block_hash: Bytes; + txid: Bytes; + index: number; + hash_prevout: Bytes | null; + index_prevout: number; + sequence: number; + coinbase: Bytes | null; + txinwitness: Bytes[]; +}; + +export type TxOutput = { + block_hash: Bytes; + txid: Bytes; + index: number; + value: number; + scriptpubkey: Bytes | null; +}; + diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 68359ab..11555fb 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,7 @@ -import { bech32m } from 'bech32'; +import { bech32m } from 'bech32'; import { Buffer } from 'buffer'; +import bs58 from 'bs58'; +import { PUBLIC_BTC_NETWORK } from "$env/static/public"; export function calculateTimeRemaining(targetHeight: number, currentHeight: number): string { const BLOCK_TIME_MINUTES = 10; @@ -34,23 +36,50 @@ export function formatDuration(seconds: number): string { return result; } -export function decodeScriptPubKeyToTaprootAddress(scriptPubKey, network = 'mainnet') { - if (!scriptPubKey.startsWith('5120') || scriptPubKey.length !== 68) { - throw new Error('Invalid P2TR ScriptPubKey'); +export function decodeScriptPubKeyToTaprootAddress(scriptPubKey: Buffer, network = 'mainnet') { + if (scriptPubKey.length !== 34 || scriptPubKey[0] !== 0x51 || scriptPubKey[1] !== 0x20) { + return null; } + const pubkeyBytes = scriptPubKey.slice(2); + const hrp = network === 'mainnet' ? 'bc' : 'tb'; + const pubkeyBits = bech32m.toWords(pubkeyBytes); + return bech32m.encode(hrp, [1].concat(pubkeyBits)); +} - // Extract the 32-byte public key (after '5120') - const pubkeyHex = scriptPubKey.slice(4); - const pubkeyBytes = Buffer.from(pubkeyHex, 'hex'); +export function parseP2PKHScriptPubKey(scriptPubKey: Buffer) { + if (scriptPubKey.length !== 25 || scriptPubKey[0] !== 0x76 || scriptPubKey[1] !== 0xa9 || scriptPubKey[2] !== 0x14 || scriptPubKey[23] !== 0x88 || scriptPubKey[24] !== 0xac) { + return null; + } + const pubKeyHash = scriptPubKey.slice(3, 23); + const prefix = PUBLIC_BTC_NETWORK === 'mainnet' ? 0x00 : 0x6f; + const payload = Buffer.concat([Buffer.from([prefix]), pubKeyHash]); + return bs58.encode(payload); +} - // Determine the HRP (Human-Readable Part) based on the network - const hrp = network === 'mainnet' ? 'bc' : 'tb'; +export function parseP2WPKH(scriptPubKey: Buffer) { + if (scriptPubKey.length !== 22 || scriptPubKey[0] !== 0x00 || scriptPubKey[1] !== 0x14) { + return null; + } + const pubKeyHash = scriptPubKey.slice(2); + const words = bech32m.toWords(pubKeyHash); + const prefix = PUBLIC_BTC_NETWORK === 'mainnet' ? 'bc' : 'tb'; + return bech32m.encode(prefix, [0].concat(words)); +} - // Convert bytes to 5-bit groups (Bech32m) - const pubkeyBits = bech32m.toWords(pubkeyBytes); +export function parseP2WSH(scriptPubKey: Buffer) { + if (scriptPubKey.length !== 34 || scriptPubKey[0] !== 0x00 || scriptPubKey[1] !== 0x20) { + return null; + } + const scriptHash = scriptPubKey.slice(2); + const words = bech32m.toWords(scriptHash); + const prefix = PUBLIC_BTC_NETWORK === 'mainnet' ? 'bc' : 'tb'; + return bech32m.encode(prefix, [0].concat(words)); +} - // Create the address with a version 1 witness program - const address = bech32m.encode(hrp, [1].concat(pubkeyBits)); +// Remove or comment out this function as it's not needed +// function publicKeyHashToAddress(publicKeyHash) { +// const prefix = 'bc'; // 'bc' for mainnet; use 'tb' for testnet +// const words = bech32m.toWords(publicKeyHash); +// return bech32m.encode(prefix, 0x00, words); // 0x00 is the version for P2WPKH +// } - return address; -} \ No newline at end of file diff --git a/src/lib/utils/formatters.ts b/src/lib/utils/formatters.ts new file mode 100644 index 0000000..2250c7c --- /dev/null +++ b/src/lib/utils/formatters.ts @@ -0,0 +1,8 @@ +export function formatNumberWithSpaces(num: number): string { + return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " "); +} + +export const numberFormatter = { + format: formatNumberWithSpaces +}; + diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 4cc558c..eb566cc 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -7,6 +7,7 @@ import { browser } from '$app/environment'; import ThemeToggle from "$lib/components/ThemeToggle.svelte"; import { PUBLIC_BTC_NETWORK } from "$env/static/public"; + import Footer from "$lib/components/Footer.svelte"; let search = ""; let timeout: any; let searching = false; @@ -34,6 +35,7 @@ showSearchResults = false; highlightedResultIdx = -1; searchResults = await fetch("/api/search?q=" + search).then((x) => x.json()); + console.log("in layout", searchResults) searching = false; if (!navigatingToSpacePage) showSearchResults = true; @@ -50,18 +52,30 @@ else if (event.key == 'Enter') { showSearchResults = false; if (highlightedResultIdx >= 0) { - search = searchResults[highlightedResultIdx].name; - highlightedResultIdx = -1; - goto(`/space/${search.slice(search[0] == '@' ? 1 : 0)}`); + navigateToResult(searchResults[highlightedResultIdx]); } else if (search.length > 0 && search != '@') { navigatingToSpacePage = true; showSearchResults = false; - search = search[0] == '@' ? search : `@${search}`; - goto(`/space/${search[0] == '@' ? search.slice(1) : search}`); + goto(`/spaces/${search}`).then(() => { + search = ""; + }); } } } + function navigateToResult(result: any) { + const { type, value } = result; + + if (type === "transaction") { + goto(`/tx/${value.txid}`).then(() => { search = ""; }); + } else if (type === "block") { + goto(`/block/${value.hash}`).then(() => { search = ""; }); + } else if (type === "space") { + goto(`/spaces/${value.name}`).then(() => { search = "";}); + } + } + + onMount(() => { const handler = (e: any) => { if (!searchBar.contains(e.target) && showSearchResults) showSearchResults = false; @@ -74,7 +88,7 @@ {#if PUBLIC_BTC_NETWORK=="testnet4"} -
Spaces is currently on Testnet4
+
Spaces is currently on Testnet4
{/if}
@@ -83,62 +97,84 @@

Spaces Protocol

-
- -
+ +