From 51714436e5de8bbd284930051337689ebb4620d1 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 09:05:55 +0000 Subject: [PATCH 01/15] Docker deno cache api.ts --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 55c3052..3fc77e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ ENTRYPOINT [ "deno", "run", "--allow-env", "--allow-read", "--allow-net", "--all FROM common as api ENV API_URL="http://0.0.0.0:80" +RUN deno cache api.ts EXPOSE 80 ENTRYPOINT [ "deno", "run", "--allow-env", "--allow-read", "--allow-net", "--allow-ffi", "api.ts" ] From 802f48855efc115248e4f4022c5220b35d976f83 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 07:08:34 +0000 Subject: [PATCH 02/15] Rename app.ts->dev.ts, deno task serve->dev. --- README.md | 18 +++++++++--------- deno.jsonc | 4 ++-- app.ts => dev.ts | 2 ++ 3 files changed, 13 insertions(+), 11 deletions(-) rename app.ts => dev.ts (98%) mode change 100644 => 100755 diff --git a/README.md b/README.md index edeab78..093983a 100644 --- a/README.md +++ b/README.md @@ -37,21 +37,21 @@ will be populated with required tables. ## Running the application for development -A convenience script (`app.ts`) for running all the components (except for web -at the moment) is provided, along with a deno task (`deno task serve`) that -runs this script with required permissions. After applying the database -migrations and configuring `.env`, start the components with `deno task serve`. -The database will automatically be connected with the default configuration. -The script will also launch an instance of an embedded AMQP broker +A convenience script (`dev.ts`) for running all the components (except for web +at the moment) is provided, along with a deno task (`deno task dev`) tha runs +this script with required permissions. After applying the database migrations +and configuring `.env`, start the components with `deno task dev`. The database +will automatically be connected with the default configuration. The script will +also launch an instance of an embedded AMQP broker (https://deno.land/x/lop/mod.ts) and a webhook receiver for testing (`testWebhookReceiver.ts`) on http://localhost:8888. -The web component can be started with `deno task serve-web`. +The web component can be started with `deno task dev-web`. The components will be started using the configuration defined in `.env` file by default. The configuration may also be overrided by providing the environment variables in the command line (such as -`BLOCK_FINALITY=finalized deno task serve`.) +`BLOCK_FINALITY=finalized deno task dev`.) ## Running components independently & for production @@ -69,7 +69,7 @@ database and configure the components accordingly. You also need an AMQP 0-9-1 compliant message broker (such as RabbitMQ.) For testing, you may also use [lop](https://deno.land/x/lop/mod.ts), which is run -when running the application with `deno task serve`: +when running the application with `deno task dev`: `deno run -A https://deno.land/x/lop/mod.ts`. To configure each of the components with different configurations, you may need diff --git a/deno.jsonc b/deno.jsonc index 004ec00..2adadb5 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,8 +1,8 @@ { "tasks": { "prisma": "deno run --allow-read --allow-sys --allow-run --allow-write scripts/prisma-wrapper.ts", - "serve": "deno run --allow-read --allow-env --allow-run --allow-sys app.ts", - "serve-web": "deno run -A --watch=web/static/,web/routes/ web/dev.ts" + "dev": "deno run --allow-read --allow-env --allow-run --allow-sys dev.ts", + "dev-web": "deno run -A --watch=web/static/,web/routes/ web/dev.ts" }, "lock": false, "compilerOptions": { diff --git a/app.ts b/dev.ts old mode 100644 new mode 100755 similarity index 98% rename from app.ts rename to dev.ts index aba88e4..f52bd45 --- a/app.ts +++ b/dev.ts @@ -1,3 +1,5 @@ +#!/usr/bin/env -S deno run --allow-read --allow-env --allow-run --allow-sys + import { parse } from "std/flags/mod.ts"; import * as path from "std/path/mod.ts"; import { ConsoleHandler } from "std/log/handlers.ts"; From be4b316eaf8f26fe0d54b62369ecd9d2e24edc49 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 07:11:48 +0000 Subject: [PATCH 03/15] Rename runHelpers.ts->runUtils.ts --- api.ts | 2 +- dev.ts | 2 +- emitter.ts | 2 +- observer.ts | 2 +- runHelpers.ts => runUtils.ts | 0 testWebhookReceiver.ts | 2 +- web/main.ts | 2 +- 7 files changed, 6 insertions(+), 6 deletions(-) rename runHelpers.ts => runUtils.ts (100%) diff --git a/api.ts b/api.ts index 669545c..00a91aa 100644 --- a/api.ts +++ b/api.ts @@ -32,7 +32,7 @@ import { getInternalLoggers, getLoggingLevel, } from "./logUtils.ts"; -import { runWithPrisma } from "./runHelpers.ts"; +import { runWithPrisma } from "./runUtils.ts"; export function api(prisma: PrismaClient) { const hexToBuffer = (hex: string): Buffer => { diff --git a/dev.ts b/dev.ts index f52bd45..654bef5 100755 --- a/dev.ts +++ b/dev.ts @@ -28,7 +28,7 @@ import { runWithAmqp, runWithChainDefinition, runWithPrisma, -} from "./runHelpers.ts"; +} from "./runUtils.ts"; import { testWebhookReceiver } from "./testWebhookReceiver.ts"; async function prepareAndMain() { diff --git a/emitter.ts b/emitter.ts index 179e4d0..d5bc8fa 100644 --- a/emitter.ts +++ b/emitter.ts @@ -19,7 +19,7 @@ import { runWithAmqp, runWithChainDefinition, runWithPrisma, -} from "./runHelpers.ts"; +} from "./runUtils.ts"; import { uint8ArrayEquals } from "./uint8ArrayUtils.ts"; import { serializeEventResponse } from "./EventResponse.ts"; import { diff --git a/observer.ts b/observer.ts index 072af11..b5c52a6 100644 --- a/observer.ts +++ b/observer.ts @@ -37,7 +37,7 @@ import { runWithAmqp, runWithChainDefinition, runWithPrisma, -} from "./runHelpers.ts"; +} from "./runUtils.ts"; type Log = LogGeneric< bigint, diff --git a/runHelpers.ts b/runUtils.ts similarity index 100% rename from runHelpers.ts rename to runUtils.ts diff --git a/testWebhookReceiver.ts b/testWebhookReceiver.ts index 5dc7b42..bc1ae0e 100644 --- a/testWebhookReceiver.ts +++ b/testWebhookReceiver.ts @@ -9,7 +9,7 @@ import { defaultLogFormatter, TestWebhookReceiverLoggerName, } from "./logUtils.ts"; -import { runAndCleanup } from "./runHelpers.ts"; +import { runAndCleanup } from "./runUtils.ts"; function numberParser(value: string) { const n = Number(value); diff --git a/web/main.ts b/web/main.ts index 34a8718..fe750dd 100644 --- a/web/main.ts +++ b/web/main.ts @@ -28,7 +28,7 @@ import { WebLoggerName, } from "~/logUtils.ts"; import type { PrismaClient } from "~/prisma-shim.ts"; -import { runWithAmqp, runWithPrisma } from "~/runHelpers.ts"; +import { runWithAmqp, runWithPrisma } from "~/runUtils.ts"; // Used for fresh-session cookie store JWT encryption key Deno.env.set( From 8f8dfa12cf99a3e4491a68bbe782ae0ce7e947ff Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 07:15:15 +0000 Subject: [PATCH 04/15] mv prismaSchemaUtils.ts->scripts/lib --- prismaSchemaUtils.ts => scripts/lib/prismaSchemaUtils.ts | 2 +- scripts/prisma-wrapper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename prismaSchemaUtils.ts => scripts/lib/prismaSchemaUtils.ts (91%) diff --git a/prismaSchemaUtils.ts b/scripts/lib/prismaSchemaUtils.ts similarity index 91% rename from prismaSchemaUtils.ts rename to scripts/lib/prismaSchemaUtils.ts index 67aafc0..1808d26 100644 --- a/prismaSchemaUtils.ts +++ b/scripts/lib/prismaSchemaUtils.ts @@ -1,7 +1,7 @@ import { parse } from "std/flags/mod.ts"; import * as path from "std/path/mod.ts"; -import { baseDir } from "./moduleUtils.ts"; +import { baseDir } from "../../moduleUtils.ts"; type GetSchemaOptions = { useParams: boolean }; diff --git a/scripts/prisma-wrapper.ts b/scripts/prisma-wrapper.ts index 96833fe..087e338 100644 --- a/scripts/prisma-wrapper.ts +++ b/scripts/prisma-wrapper.ts @@ -4,7 +4,7 @@ import * as path from "std/path/mod.ts"; import { parseRange, rangeIntersects } from "std/semver/mod.ts"; import { baseDir } from "../moduleUtils.ts"; -import { getSchema } from "../prismaSchemaUtils.ts"; +import { getSchema } from "./lib/prismaSchemaUtils.ts"; const IncompatibleImportRegex = /(import\s[\s\S]+\sfrom\s+)(?:(')(.+)(? Date: Fri, 18 Aug 2023 07:38:01 +0000 Subject: [PATCH 05/15] mv *Utils->utils/ --- api.ts | 6 +++--- control.ts | 2 +- dev.ts | 6 +++--- emitter.ts | 8 ++++---- observer.ts | 8 ++++---- sample/contracts/deploy-sample-contract.ts | 2 +- sample/seed-prisma.ts | 2 +- scripts/lib/prismaSchemaUtils.ts | 2 +- scripts/prisma-wrapper.ts | 2 +- testWebhookReceiver.ts | 4 ++-- concurrencyUtils.ts => utils/concurrencyUtils.ts | 0 envUtils.ts => utils/envUtils.ts | 0 logUtils.ts => utils/logUtils.ts | 0 moduleUtils.ts => utils/moduleUtils.ts | 4 ++-- runUtils.ts => utils/runUtils.ts | 4 ++-- uint8ArrayUtils.ts => utils/uint8ArrayUtils.ts | 0 web/main.ts | 6 +++--- 17 files changed, 28 insertions(+), 28 deletions(-) rename concurrencyUtils.ts => utils/concurrencyUtils.ts (100%) rename envUtils.ts => utils/envUtils.ts (100%) rename logUtils.ts => utils/logUtils.ts (100%) rename moduleUtils.ts => utils/moduleUtils.ts (92%) rename runUtils.ts => utils/runUtils.ts (96%) rename uint8ArrayUtils.ts => utils/uint8ArrayUtils.ts (100%) diff --git a/api.ts b/api.ts index 00a91aa..52bfdf6 100644 --- a/api.ts +++ b/api.ts @@ -25,14 +25,14 @@ import { ApiBehindReverseProxyEnvKey, ApiUrlEnvKey, combinedEnv, -} from "./envUtils.ts"; +} from "./utils/envUtils.ts"; import { ApiLoggerName, defaultLogFormatter, getInternalLoggers, getLoggingLevel, -} from "./logUtils.ts"; -import { runWithPrisma } from "./runUtils.ts"; +} from "./utils/logUtils.ts"; +import { runWithPrisma } from "./utils/runUtils.ts"; export function api(prisma: PrismaClient) { const hexToBuffer = (hex: string): Buffer => { diff --git a/control.ts b/control.ts index 6e82abb..0d3a909 100644 --- a/control.ts +++ b/control.ts @@ -12,7 +12,7 @@ import { ControlExchangeName, ControlObserverRoutingKey, } from "./constants.ts"; -import { ControlLoggerName } from "./logUtils.ts"; +import { ControlLoggerName } from "./utils/logUtils.ts"; export function reload( amqpChannel: AmqpChannel, diff --git a/dev.ts b/dev.ts index 654bef5..024ab5d 100755 --- a/dev.ts +++ b/dev.ts @@ -11,7 +11,7 @@ import { broker } from "https://deno.land/x/lop@0.0.0-alpha.2/mod.ts"; import { api } from "./api.ts"; import { emitter } from "./emitter.ts"; -import { AmqpBrokerUrlEnvKey, combinedEnv } from "./envUtils.ts"; +import { AmqpBrokerUrlEnvKey, combinedEnv } from "./utils/envUtils.ts"; import { ApiLoggerName, defaultLogFormatter, @@ -21,14 +21,14 @@ import { ObserverLoggerName, TestWebhookReceiverLoggerName, WebLoggerName, -} from "./logUtils.ts"; +} from "./utils/logUtils.ts"; import { observer } from "./observer.ts"; import { block, runWithAmqp, runWithChainDefinition, runWithPrisma, -} from "./runUtils.ts"; +} from "./utils/runUtils.ts"; import { testWebhookReceiver } from "./testWebhookReceiver.ts"; async function prepareAndMain() { diff --git a/emitter.ts b/emitter.ts index d5bc8fa..8882f21 100644 --- a/emitter.ts +++ b/emitter.ts @@ -19,16 +19,16 @@ import { runWithAmqp, runWithChainDefinition, runWithPrisma, -} from "./runUtils.ts"; -import { uint8ArrayEquals } from "./uint8ArrayUtils.ts"; +} from "./utils/runUtils.ts"; +import { uint8ArrayEquals } from "./utils/uint8ArrayUtils.ts"; import { serializeEventResponse } from "./EventResponse.ts"; import { defaultLogFormatter, EmitterLoggerName, getInternalLoggers, getLoggingLevel, -} from "./logUtils.ts"; -import { createMutex } from "./concurrencyUtils.ts"; +} from "./utils/logUtils.ts"; +import { createMutex } from "./utils/concurrencyUtils.ts"; export async function emitter( chain: Chain, diff --git a/observer.ts b/observer.ts index b5c52a6..15d034f 100644 --- a/observer.ts +++ b/observer.ts @@ -17,27 +17,27 @@ import { import Prisma, { type PrismaClient } from "./prisma-shim.ts"; -import { createMutex } from "./concurrencyUtils.ts"; +import { createMutex } from "./utils/concurrencyUtils.ts"; import { ControlExchangeName, ControlObserverRoutingKey, EvmEventsQueueName, } from "./constants.ts"; import { deserializeControlMessage } from "./ControlMessage.ts"; -import { BlockFinalityEnvKey, combinedEnv } from "./envUtils.ts"; +import { BlockFinalityEnvKey, combinedEnv } from "./utils/envUtils.ts"; import { serializeEventMessage } from "./EventMessage.ts"; import { defaultLogFormatter, getInternalLoggers, getLoggingLevel, ObserverLoggerName, -} from "./logUtils.ts"; +} from "./utils/logUtils.ts"; import { block, runWithAmqp, runWithChainDefinition, runWithPrisma, -} from "./runUtils.ts"; +} from "./utils/runUtils.ts"; type Log = LogGeneric< bigint, diff --git a/sample/contracts/deploy-sample-contract.ts b/sample/contracts/deploy-sample-contract.ts index cfc92a8..9915407 100644 --- a/sample/contracts/deploy-sample-contract.ts +++ b/sample/contracts/deploy-sample-contract.ts @@ -15,7 +15,7 @@ import { getRelativeScriptPath, importESOrJson, normalizeImports, -} from "../../moduleUtils.ts"; +} from "../../utils/moduleUtils.ts"; const deterministicDeployerAddress = "0x7A0D94F55792C434d74a40883C6ed8545E406D12"; diff --git a/sample/seed-prisma.ts b/sample/seed-prisma.ts index 0c3f230..6345313 100644 --- a/sample/seed-prisma.ts +++ b/sample/seed-prisma.ts @@ -6,7 +6,7 @@ import { keccak256, toBytes } from "npm:viem"; import { PrismaClient } from "~/prisma-shim.ts"; import { formatAbiItemPrototype } from "../abitype.ts"; -import { combinedEnv, DatabaseUrlEnvKey } from "../envUtils.ts"; +import { combinedEnv, DatabaseUrlEnvKey } from "../utils/envUtils.ts"; import sampleAbiJson from "./contracts/sampleAbi.json" assert { type: "json" }; diff --git a/scripts/lib/prismaSchemaUtils.ts b/scripts/lib/prismaSchemaUtils.ts index 1808d26..d87cfe7 100644 --- a/scripts/lib/prismaSchemaUtils.ts +++ b/scripts/lib/prismaSchemaUtils.ts @@ -1,7 +1,7 @@ import { parse } from "std/flags/mod.ts"; import * as path from "std/path/mod.ts"; -import { baseDir } from "../../moduleUtils.ts"; +import { baseDir } from "../../utils/moduleUtils.ts"; type GetSchemaOptions = { useParams: boolean }; diff --git a/scripts/prisma-wrapper.ts b/scripts/prisma-wrapper.ts index 087e338..93960b8 100644 --- a/scripts/prisma-wrapper.ts +++ b/scripts/prisma-wrapper.ts @@ -3,7 +3,7 @@ import * as path from "std/path/mod.ts"; import { parseRange, rangeIntersects } from "std/semver/mod.ts"; -import { baseDir } from "../moduleUtils.ts"; +import { baseDir } from "../utils/moduleUtils.ts"; import { getSchema } from "./lib/prismaSchemaUtils.ts"; const IncompatibleImportRegex = diff --git a/testWebhookReceiver.ts b/testWebhookReceiver.ts index bc1ae0e..3608f4a 100644 --- a/testWebhookReceiver.ts +++ b/testWebhookReceiver.ts @@ -8,8 +8,8 @@ import { parse, stringify as losslessJsonStringify } from "npm:lossless-json"; import { defaultLogFormatter, TestWebhookReceiverLoggerName, -} from "./logUtils.ts"; -import { runAndCleanup } from "./runUtils.ts"; +} from "./utils/logUtils.ts"; +import { runAndCleanup } from "./utils/runUtils.ts"; function numberParser(value: string) { const n = Number(value); diff --git a/concurrencyUtils.ts b/utils/concurrencyUtils.ts similarity index 100% rename from concurrencyUtils.ts rename to utils/concurrencyUtils.ts diff --git a/envUtils.ts b/utils/envUtils.ts similarity index 100% rename from envUtils.ts rename to utils/envUtils.ts diff --git a/logUtils.ts b/utils/logUtils.ts similarity index 100% rename from logUtils.ts rename to utils/logUtils.ts diff --git a/moduleUtils.ts b/utils/moduleUtils.ts similarity index 92% rename from moduleUtils.ts rename to utils/moduleUtils.ts index 9c333f6..6b6f747 100644 --- a/moduleUtils.ts +++ b/utils/moduleUtils.ts @@ -1,8 +1,8 @@ import * as path from "std/path/mod.ts"; -// TODO: this const should be changed to point to the root source code directory whenever this +// XXX: this const should be changed to point to the root source code directory whenever this // file is moved. -const relativeToBase = "."; +const relativeToBase = ".."; export const baseDir = path.join( path.dirname(path.fromFileUrl(import.meta.url)), relativeToBase, diff --git a/runUtils.ts b/utils/runUtils.ts similarity index 96% rename from runUtils.ts rename to utils/runUtils.ts index 0f00eb4..9bb1c3c 100644 --- a/runUtils.ts +++ b/utils/runUtils.ts @@ -7,8 +7,8 @@ import { parseOptions } from "amqp/src/amqp_connect_options.ts"; import type { Chain } from "npm:viem"; -import type Prisma from "./prisma-shim.ts"; -import { PrismaClient } from "./prisma-shim.ts"; +import type Prisma from "../prisma-shim.ts"; +import { PrismaClient } from "../prisma-shim.ts"; import { Awaitable } from "./concurrencyUtils.ts"; import { diff --git a/uint8ArrayUtils.ts b/utils/uint8ArrayUtils.ts similarity index 100% rename from uint8ArrayUtils.ts rename to utils/uint8ArrayUtils.ts diff --git a/web/main.ts b/web/main.ts index fe750dd..7f2601f 100644 --- a/web/main.ts +++ b/web/main.ts @@ -20,15 +20,15 @@ import { combinedEnv, WebUISessionAppKeyEnvKey, WebUIUrlEnvKey, -} from "~/envUtils.ts"; +} from "~/utils/envUtils.ts"; import { defaultLogFormatter, getInternalLoggers, getLoggingLevel, WebLoggerName, -} from "~/logUtils.ts"; +} from "~/utils/logUtils.ts"; import type { PrismaClient } from "~/prisma-shim.ts"; -import { runWithAmqp, runWithPrisma } from "~/runUtils.ts"; +import { runWithAmqp, runWithPrisma } from "~/utils/runUtils.ts"; // Used for fresh-session cookie store JWT encryption key Deno.env.set( From 320e4cbb0e930d17809393ba35862aebcef34cc4 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 07:47:57 +0000 Subject: [PATCH 06/15] mv *Message->messages/ --- api.ts | 2 +- emitter.ts | 9 ++++++--- ControlMessage.ts => messages/ControlMessage.ts | 0 EventMessage.ts => messages/EventMessage.ts | 0 EventResponse.ts => messages/EventResponse.ts | 4 ++-- control.ts => messages/control.ts | 4 ++-- observer.ts | 4 ++-- scripts/signal-reload.ts | 2 +- web/routes/api/sources.ts | 2 +- web/routes/api/sources/testWebhook.ts | 2 +- web/routes/api/webhook.ts | 2 +- 11 files changed, 17 insertions(+), 14 deletions(-) rename ControlMessage.ts => messages/ControlMessage.ts (100%) rename EventMessage.ts => messages/EventMessage.ts (100%) rename EventResponse.ts => messages/EventResponse.ts (91%) rename control.ts => messages/control.ts (92%) diff --git a/api.ts b/api.ts index 52bfdf6..b0a56ec 100644 --- a/api.ts +++ b/api.ts @@ -18,7 +18,7 @@ import { getAddress, keccak256, toHex } from "npm:viem"; import type { PrismaClient } from "./prisma-shim.ts"; -import { serializeEventResponse } from "./EventResponse.ts"; +import { serializeEventResponse } from "./messages/EventResponse.ts"; import { formatAbiItemPrototype } from "./abitype.ts"; import { validateEventRequest } from "./apiSchema.ts"; import { diff --git a/emitter.ts b/emitter.ts index 8882f21..1c7148f 100644 --- a/emitter.ts +++ b/emitter.ts @@ -8,8 +8,11 @@ import { type Chain, getAddress, toHex } from "npm:viem"; import type { PrismaClient } from "./prisma-shim.ts"; -import { deserializeControlMessage } from "./ControlMessage.ts"; -import { deserializeEventMessage, EventMessage } from "./EventMessage.ts"; +import { deserializeControlMessage } from "./messages/ControlMessage.ts"; +import { + deserializeEventMessage, + EventMessage, +} from "./messages/EventMessage.ts"; import { ControlEmitterRoutingKey, ControlExchangeName, @@ -21,7 +24,7 @@ import { runWithPrisma, } from "./utils/runUtils.ts"; import { uint8ArrayEquals } from "./utils/uint8ArrayUtils.ts"; -import { serializeEventResponse } from "./EventResponse.ts"; +import { serializeEventResponse } from "./messages/EventResponse.ts"; import { defaultLogFormatter, EmitterLoggerName, diff --git a/ControlMessage.ts b/messages/ControlMessage.ts similarity index 100% rename from ControlMessage.ts rename to messages/ControlMessage.ts diff --git a/EventMessage.ts b/messages/EventMessage.ts similarity index 100% rename from EventMessage.ts rename to messages/EventMessage.ts diff --git a/EventResponse.ts b/messages/EventResponse.ts similarity index 91% rename from EventResponse.ts rename to messages/EventResponse.ts index f7f56db..d8b1823 100644 --- a/EventResponse.ts +++ b/messages/EventResponse.ts @@ -1,7 +1,7 @@ import { getAddress, toHex } from "npm:viem"; -import { formatAbiItemPrototype } from "./abitype.ts"; -import { decodeEventLog } from "./decodeEventLog.ts"; +import { formatAbiItemPrototype } from "../abitype.ts"; +import { decodeEventLog } from "../decodeEventLog.ts"; import { EventMessage } from "./EventMessage.ts"; export const serializeEventResponse = (evtMsg: EventMessage) => { diff --git a/control.ts b/messages/control.ts similarity index 92% rename from control.ts rename to messages/control.ts index 0d3a909..0a7e935 100644 --- a/control.ts +++ b/messages/control.ts @@ -11,8 +11,8 @@ import { ControlEmitterRoutingKey, ControlExchangeName, ControlObserverRoutingKey, -} from "./constants.ts"; -import { ControlLoggerName } from "./utils/logUtils.ts"; +} from "../constants.ts"; +import { ControlLoggerName } from "../utils/logUtils.ts"; export function reload( amqpChannel: AmqpChannel, diff --git a/observer.ts b/observer.ts index 15d034f..17acc45 100644 --- a/observer.ts +++ b/observer.ts @@ -23,9 +23,9 @@ import { ControlObserverRoutingKey, EvmEventsQueueName, } from "./constants.ts"; -import { deserializeControlMessage } from "./ControlMessage.ts"; +import { deserializeControlMessage } from "./messages/ControlMessage.ts"; +import { serializeEventMessage } from "./messages/EventMessage.ts"; import { BlockFinalityEnvKey, combinedEnv } from "./utils/envUtils.ts"; -import { serializeEventMessage } from "./EventMessage.ts"; import { defaultLogFormatter, getInternalLoggers, diff --git a/scripts/signal-reload.ts b/scripts/signal-reload.ts index 5d1bf10..bc0315b 100644 --- a/scripts/signal-reload.ts +++ b/scripts/signal-reload.ts @@ -5,7 +5,7 @@ import { ControlExchangeName, ControlObserverRoutingKey, } from "../constants.ts"; -import { reload } from "../control.ts"; +import { reload } from "../messages/control.ts"; const conn = await connect(); const chan = await conn.openChannel(); diff --git a/web/routes/api/sources.ts b/web/routes/api/sources.ts index bf15ba2..25f562d 100644 --- a/web/routes/api/sources.ts +++ b/web/routes/api/sources.ts @@ -9,7 +9,7 @@ import { getAddress, toBytes, toHex } from "npm:viem"; import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; import { formatAbiItemPrototype } from "~/abitype.ts"; -import { reload as reloadControl } from "~/control.ts"; +import { reload as reloadControl } from "~/messages/control.ts"; import { ControlObserverRoutingKey } from "~/constants.ts"; import type { User } from "~/generated/client/index.d.ts"; diff --git a/web/routes/api/sources/testWebhook.ts b/web/routes/api/sources/testWebhook.ts index 8295d2d..50ae041 100644 --- a/web/routes/api/sources/testWebhook.ts +++ b/web/routes/api/sources/testWebhook.ts @@ -7,7 +7,7 @@ import { toBytes } from "npm:viem"; import { amqpChannel, prisma } from "web/main.ts"; import { logRequest } from "web/util.ts"; -import { serializeEventMessage } from "~/EventMessage.ts"; +import { serializeEventMessage } from "~/messages/EventMessage.ts"; import { EvmEventsQueueName } from "~/constants.ts"; export const handler: Handlers = { diff --git a/web/routes/api/webhook.ts b/web/routes/api/webhook.ts index f51b80d..068355b 100644 --- a/web/routes/api/webhook.ts +++ b/web/routes/api/webhook.ts @@ -8,7 +8,7 @@ import { getAddress, toBytes, toHex } from "npm:viem"; import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; -import { reload as reloadControl } from "~/control.ts"; +import { reload as reloadControl } from "~/messages/control.ts"; import { ControlEmitterRoutingKey } from "~/constants.ts"; import type { User } from "~/generated/client/index.d.ts"; From dfbe3f9d0c62584ca8a64a595261b46faf782de3 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 09:05:20 +0000 Subject: [PATCH 07/15] Isolate prisma --- .dockerignore | 11 ++++---- .gitignore | 12 ++++---- Dockerfile | 6 ++-- README.md | 29 ++++++++++---------- api.ts | 2 +- deno.jsonc | 2 +- docker-compose.yml | 2 +- emitter.ts | 2 +- observer.ts | 2 +- prisma/deno.jsonc | 9 ++++++ package.json => prisma/package.json | 3 +- {scripts => prisma}/prisma-wrapper.ts | 20 ++++++++++---- prisma/{ => prisma}/schema.prisma | 6 ++-- {scripts/lib => prisma}/prismaSchemaUtils.ts | 12 ++++++-- prisma-shim.ts => prisma/shim-inner.ts | 8 +++--- prisma/shim.ts | 4 +++ yarn.lock => prisma/yarn.lock | 0 sample/seed-prisma.ts | 2 +- utils/runUtils.ts | 3 +- web/main.ts | 2 +- web/routes/_middleware.ts | 2 +- web/routes/api/abi.ts | 2 +- web/routes/api/sources.ts | 2 +- web/routes/api/webhook.ts | 2 +- web/util.ts | 3 +- 25 files changed, 87 insertions(+), 61 deletions(-) create mode 100644 prisma/deno.jsonc rename package.json => prisma/package.json (82%) rename {scripts => prisma}/prisma-wrapper.ts (88%) rename prisma/{ => prisma}/schema.prisma (96%) rename {scripts/lib => prisma}/prismaSchemaUtils.ts (59%) rename prisma-shim.ts => prisma/shim-inner.ts (63%) create mode 100644 prisma/shim.ts rename yarn.lock => prisma/yarn.lock (100%) diff --git a/.dockerignore b/.dockerignore index 83b6661..b164ce1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -13,13 +13,14 @@ **/node_modules # Keep environment variables out of version control **/.env -/prisma/dev.db -/prisma/dev.db-journal -/prisma/dev.db-shm -/prisma/dev.db-wal +/prisma/prisma/dev.db +/prisma/prisma/dev.db-journal +/prisma/prisma/dev.db-shm +/prisma/prisma/dev.db-wal **/.vscode /dev.crt /dev.key -/generated +/prisma/client +/prisma/node_modules /docker-compose-data **/.DS_Store diff --git a/.gitignore b/.gitignore index 0f18765..d8f3005 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,13 @@ -node_modules # Keep environment variables out of version control .env -/prisma/dev.db -/prisma/dev.db-journal -/prisma/dev.db-shm -/prisma/dev.db-wal +/prisma/prisma/dev.db +/prisma/prisma/dev.db-journal +/prisma/prisma/dev.db-shm +/prisma/prisma/dev.db-wal /.vscode/launch.json /dev.crt /dev.key -/generated +/prisma/client +/prisma/node_modules /docker-compose-data .DS_Store diff --git a/Dockerfile b/Dockerfile index 3fc77e7..41cbfc7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,11 +3,11 @@ WORKDIR /Corvette COPY . . RUN apk add npm && \ corepack enable && \ - cat prisma/schema.prisma | \ + cat prisma/prisma/schema.prisma | \ awk '/generator[[:space:]]+[^[:space:]]+[[:space:]]*\{/{ print; print " binaryTargets = [\"native\", \"linux-musl-openssl-3.0.x\"]"; next }1' | \ awk '/datasource[[:space:]]+[^[:space:]]+[[:space:]]*\{/{ flag=1 } /}/ { flag=0 } (flag && /provider[[:space:]]*=/) { print " provider = \"postgresql\""; next } (flag && /directUrl[[:space:]]*=/) { next } { print }' \ - > prisma/schema.prisma.new && \ - mv prisma/schema.prisma.new prisma/schema.prisma && \ + > prisma/prisma/schema.prisma.new && \ + mv prisma/prisma/schema.prisma.new prisma/prisma/schema.prisma && \ deno task prisma format && \ deno task prisma generate diff --git a/README.md b/README.md index 093983a..914bd02 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ To run Corvette, you must have Deno and Node.js with corepack enabled installed included. In that case, you must install `yarn` separately with your package manager. -Before you can run the components, you must apply the database migrations to -your DB. Adjust the database provider in `prisma/schema.prisma`, copy the -configuration from the example `.env.example` to `.env`, and configure -`DATABASE_URL` accordingly. Only SQLite (for development only) and PostgreSQL -has been tested at the moment. +If you intend to run the components without `deno task dev`, you must apply the +database migrations to your DB. Adjust the database provider in +`prisma/prisma/schema.prisma`, copy the configuration from the example +`.env.example` to `.env`, and configure `DATABASE_URL` accordingly. Only SQLite +and PostgreSQL has been tested at the moment. After database configuration, run `deno task prisma db push`. Your database will be populated with required tables. @@ -38,13 +38,12 @@ will be populated with required tables. ## Running the application for development A convenience script (`dev.ts`) for running all the components (except for web -at the moment) is provided, along with a deno task (`deno task dev`) tha runs -this script with required permissions. After applying the database migrations -and configuring `.env`, start the components with `deno task dev`. The database -will automatically be connected with the default configuration. The script will -also launch an instance of an embedded AMQP broker -(https://deno.land/x/lop/mod.ts) and a webhook receiver for testing -(`testWebhookReceiver.ts`) on http://localhost:8888. +at the moment) is provided, along with a deno task (`deno task dev`) that runs +this script with required permissions. After configuring `.env`, start the +components with `deno task dev`. The database will automatically be connected +with the default configuration. The script will also launch an instance of an +embedded AMQP broker (https://deno.land/x/lop/mod.ts) and a webhook receiver +for development (`testWebhookReceiver.ts`) on http://localhost:8888. The web component can be started with `deno task dev-web`. @@ -63,9 +62,9 @@ of the same components sharing the same network, DB, and message queue may also be run simultaneously to achieve failsafe and/or load balancing. Before running the components, you must generate the Prisma client. You must do -this each time you change the `prisma/schema.prisma` file or Prisma version. -Generate the client with `deno task prisma generate`. You must also prepare the -database and configure the components accordingly. +this each time you change the `prisma/prisma/schema.prisma` file or Prisma +version. Generate the client with `deno task prisma generate`. You must also +prepare the database and configure the components accordingly. You also need an AMQP 0-9-1 compliant message broker (such as RabbitMQ.) For testing, you may also use [lop](https://deno.land/x/lop/mod.ts), which is run diff --git a/api.ts b/api.ts index b0a56ec..cd0a984 100644 --- a/api.ts +++ b/api.ts @@ -16,7 +16,7 @@ import { Buffer } from "node:buffer"; import { stringify as losslessJsonStringify } from "npm:lossless-json"; import { getAddress, keccak256, toHex } from "npm:viem"; -import type { PrismaClient } from "./prisma-shim.ts"; +import type { PrismaClient } from "./prisma/shim.ts"; import { serializeEventResponse } from "./messages/EventResponse.ts"; import { formatAbiItemPrototype } from "./abitype.ts"; diff --git a/deno.jsonc b/deno.jsonc index 2adadb5..0401f4f 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,6 +1,6 @@ { "tasks": { - "prisma": "deno run --allow-read --allow-sys --allow-run --allow-write scripts/prisma-wrapper.ts", + "prisma": "deno task --cwd prisma prisma", "dev": "deno run --allow-read --allow-env --allow-run --allow-sys dev.ts", "dev-web": "deno run -A --watch=web/static/,web/routes/ web/dev.ts" }, diff --git a/docker-compose.yml b/docker-compose.yml index 1ead1eb..e1b214d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -70,7 +70,7 @@ services: target: common environment: DATABASE_URL: postgres://postgres:V0uYcxk2dFixtPWfK4Nb@db/postgres - command: sh -c 'deno task prisma migrate diff --from-schema-datasource prisma/schema.prisma --to-schema-datamodel prisma/schema.prisma --script > migrate.sql && deno task prisma db execute --file ./migrate.sql --schema prisma/schema.prisma' + command: sh -c 'deno task prisma migrate diff --from-schema-datasource prisma/prisma/schema.prisma --to-schema-datamodel prisma/prisma/schema.prisma --script > migrate.sql && deno task prisma db execute --file ./migrate.sql --schema prisma/prisma/schema.prisma' message-queue-broker: image: rabbitmq:3.12.2-alpine restart: always diff --git a/emitter.ts b/emitter.ts index 1c7148f..2e416be 100644 --- a/emitter.ts +++ b/emitter.ts @@ -6,7 +6,7 @@ import type { AmqpConnection } from "amqp/mod.ts"; import { stringify as losslessJsonStringify } from "npm:lossless-json"; import { type Chain, getAddress, toHex } from "npm:viem"; -import type { PrismaClient } from "./prisma-shim.ts"; +import type { PrismaClient } from "./prisma/shim.ts"; import { deserializeControlMessage } from "./messages/ControlMessage.ts"; import { diff --git a/observer.ts b/observer.ts index 17acc45..b9b7371 100644 --- a/observer.ts +++ b/observer.ts @@ -15,7 +15,7 @@ import { toHex, } from "npm:viem"; -import Prisma, { type PrismaClient } from "./prisma-shim.ts"; +import { Prisma, type PrismaClient } from "./prisma/shim.ts"; import { createMutex } from "./utils/concurrencyUtils.ts"; import { diff --git a/prisma/deno.jsonc b/prisma/deno.jsonc new file mode 100644 index 0000000..54b91a9 --- /dev/null +++ b/prisma/deno.jsonc @@ -0,0 +1,9 @@ +{ + "tasks": { + "prisma": "deno run --allow-env --allow-read --allow-sys --allow-run --allow-write prisma-wrapper.ts" + }, + "lock": false, + "imports": { + "std/": "https://deno.land/std@0.196.0/" + } +} \ No newline at end of file diff --git a/package.json b/prisma/package.json similarity index 82% rename from package.json rename to prisma/package.json index e6eee6a..4776f44 100644 --- a/package.json +++ b/prisma/package.json @@ -4,5 +4,6 @@ }, "dependencies": { "@prisma/client": "5.1.1" - } + }, + "private": true } diff --git a/scripts/prisma-wrapper.ts b/prisma/prisma-wrapper.ts similarity index 88% rename from scripts/prisma-wrapper.ts rename to prisma/prisma-wrapper.ts index 93960b8..b341e4c 100644 --- a/scripts/prisma-wrapper.ts +++ b/prisma/prisma-wrapper.ts @@ -1,10 +1,11 @@ +import { load as loadEnv } from "std/dotenv/mod.ts"; import { parse } from "std/flags/mod.ts"; import * as path from "std/path/mod.ts"; import { parseRange, rangeIntersects } from "std/semver/mod.ts"; -import { baseDir } from "../utils/moduleUtils.ts"; -import { getSchema } from "./lib/prismaSchemaUtils.ts"; +import { baseDir as projectBaseDir } from "../utils/moduleUtils.ts"; +import { getSchema, prismaBaseDir } from "./prismaSchemaUtils.ts"; const IncompatibleImportRegex = /(import\s[\s\S]+\sfrom\s+)(?:(')(.+)(? diff --git a/prisma/schema.prisma b/prisma/prisma/schema.prisma similarity index 96% rename from prisma/schema.prisma rename to prisma/prisma/schema.prisma index 1a06b5b..6a5bde9 100644 --- a/prisma/schema.prisma +++ b/prisma/prisma/schema.prisma @@ -3,12 +3,12 @@ generator client { provider = "prisma-client-js" - output = "../generated/client" + output = "../client" } datasource db { - provider = "sqlite" - url = env("DATABASE_URL") + provider = "sqlite" + url = env("DATABASE_URL") } model Event { diff --git a/scripts/lib/prismaSchemaUtils.ts b/prisma/prismaSchemaUtils.ts similarity index 59% rename from scripts/lib/prismaSchemaUtils.ts rename to prisma/prismaSchemaUtils.ts index d87cfe7..b62cad3 100644 --- a/scripts/lib/prismaSchemaUtils.ts +++ b/prisma/prismaSchemaUtils.ts @@ -1,16 +1,22 @@ import { parse } from "std/flags/mod.ts"; import * as path from "std/path/mod.ts"; -import { baseDir } from "../../utils/moduleUtils.ts"; - type GetSchemaOptions = { useParams: boolean }; +// XXX: this const should be changed to point to the root source code directory whenever this +// file is moved. +const relativeToBase = "."; +export const prismaBaseDir = path.join( + path.dirname(path.fromFileUrl(import.meta.url)), + relativeToBase, +); + export function getSchemaPath( options: GetSchemaOptions = { useParams: false }, ) { return options.useParams && parse(Deno.args, { string: ["schema"] }).schema || - path.join(baseDir, "prisma", "schema.prisma"); + path.join(prismaBaseDir, "prisma", "schema.prisma"); } export async function getSchema( diff --git a/prisma-shim.ts b/prisma/shim-inner.ts similarity index 63% rename from prisma-shim.ts rename to prisma/shim-inner.ts index dfedb56..653816a 100644 --- a/prisma-shim.ts +++ b/prisma/shim-inner.ts @@ -1,14 +1,14 @@ +import { Buffer } from "node:buffer"; import { createRequire } from "node:module"; import process from "node:process"; -import { Buffer } from "node:buffer"; -import * as _prismaTypes from "./generated/client/index.d.ts"; +import * as _prismaTypes from "./client/index.d.ts"; Object.assign(globalThis, { process, Buffer }); const require = createRequire(import.meta.url); -const prisma: typeof _prismaTypes = require("./generated/client"); +const prisma: typeof _prismaTypes = require("./client"); +export type * from "./client/index.d.ts"; export class PrismaClient extends prisma.PrismaClient {} -// re-export namespace Prisma as default export export default prisma.Prisma; diff --git a/prisma/shim.ts b/prisma/shim.ts new file mode 100644 index 0000000..a31b6ed --- /dev/null +++ b/prisma/shim.ts @@ -0,0 +1,4 @@ +import PrismaInner from "./shim-inner.ts"; +export * from "./shim-inner.ts"; +// deno-lint-ignore no-unused-vars +export import Prisma = PrismaInner; diff --git a/yarn.lock b/prisma/yarn.lock similarity index 100% rename from yarn.lock rename to prisma/yarn.lock diff --git a/sample/seed-prisma.ts b/sample/seed-prisma.ts index 6345313..972b12f 100644 --- a/sample/seed-prisma.ts +++ b/sample/seed-prisma.ts @@ -3,7 +3,7 @@ import { type AbiEvent, narrow } from "abitype"; import { Buffer } from "node:buffer"; import { keccak256, toBytes } from "npm:viem"; -import { PrismaClient } from "~/prisma-shim.ts"; +import { PrismaClient } from "~/prisma/shim.ts"; import { formatAbiItemPrototype } from "../abitype.ts"; import { combinedEnv, DatabaseUrlEnvKey } from "../utils/envUtils.ts"; diff --git a/utils/runUtils.ts b/utils/runUtils.ts index 9bb1c3c..71211f0 100644 --- a/utils/runUtils.ts +++ b/utils/runUtils.ts @@ -7,8 +7,7 @@ import { parseOptions } from "amqp/src/amqp_connect_options.ts"; import type { Chain } from "npm:viem"; -import type Prisma from "../prisma-shim.ts"; -import { PrismaClient } from "../prisma-shim.ts"; +import { type Prisma, PrismaClient } from "../prisma/shim.ts"; import { Awaitable } from "./concurrencyUtils.ts"; import { diff --git a/web/main.ts b/web/main.ts index 7f2601f..1e6a7f6 100644 --- a/web/main.ts +++ b/web/main.ts @@ -27,7 +27,7 @@ import { getLoggingLevel, WebLoggerName, } from "~/utils/logUtils.ts"; -import type { PrismaClient } from "~/prisma-shim.ts"; +import type { PrismaClient } from "~/prisma/shim.ts"; import { runWithAmqp, runWithPrisma } from "~/utils/runUtils.ts"; // Used for fresh-session cookie store JWT encryption key diff --git a/web/routes/_middleware.ts b/web/routes/_middleware.ts index 7c95221..8406b1e 100644 --- a/web/routes/_middleware.ts +++ b/web/routes/_middleware.ts @@ -4,7 +4,7 @@ import { cookieSession, type WithSession } from "fresh-session"; import { type MiddlewareHandlerContext, Status } from "fresh/server.ts"; import { logRequest, redirect } from "web/util.ts"; -import type { User } from "~/generated/client/index.d.ts"; +import type { User } from "~/prisma/shim.ts"; const session = cookieSession(); diff --git a/web/routes/api/abi.ts b/web/routes/api/abi.ts index 36ba871..88206f9 100644 --- a/web/routes/api/abi.ts +++ b/web/routes/api/abi.ts @@ -11,7 +11,7 @@ import { keccak256, toBytes, toHex } from "npm:viem"; import { prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; import { formatAbiItemPrototype } from "~/abitype.ts"; -import type { User } from "~/generated/client/index.d.ts"; +import type { User } from "~/prisma/shim.ts"; import type { AbiEntry } from "web/islands/ListAbi.tsx"; diff --git a/web/routes/api/sources.ts b/web/routes/api/sources.ts index 25f562d..24463ef 100644 --- a/web/routes/api/sources.ts +++ b/web/routes/api/sources.ts @@ -11,7 +11,7 @@ import { checkPermission, logRequest } from "web/util.ts"; import { formatAbiItemPrototype } from "~/abitype.ts"; import { reload as reloadControl } from "~/messages/control.ts"; import { ControlObserverRoutingKey } from "~/constants.ts"; -import type { User } from "~/generated/client/index.d.ts"; +import type { User } from "~/prisma/shim.ts"; import type { SourceEntry } from "web/islands/ListSources.tsx"; diff --git a/web/routes/api/webhook.ts b/web/routes/api/webhook.ts index 068355b..5b44ef0 100644 --- a/web/routes/api/webhook.ts +++ b/web/routes/api/webhook.ts @@ -10,7 +10,7 @@ import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; import { reload as reloadControl } from "~/messages/control.ts"; import { ControlEmitterRoutingKey } from "~/constants.ts"; -import type { User } from "~/generated/client/index.d.ts"; +import type { User } from "~/prisma/shim.ts"; import type { WebhookEntry } from "web/islands/ListWebhook.tsx"; diff --git a/web/util.ts b/web/util.ts index ec9a9a1..03a7185 100644 --- a/web/util.ts +++ b/web/util.ts @@ -5,8 +5,7 @@ import { LogLevels } from "std/log/mod.ts"; import { type ServeHandlerInfo, Status } from "fresh/server.ts"; import { listenUrl, logger, prisma } from "web/main.ts"; -import type Prisma from "~/prisma-shim.ts"; -import type { User } from "~/generated/client/index.d.ts"; +import type { Prisma, User } from "~/prisma/shim.ts"; export const getOrigin = (req: Request) => new URL(req.url).origin; export const getServerSideUrl = (pathname: string) => From d82e3e25e1bdd040492b0b5d5f32f2af3997baba Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 09:08:19 +0000 Subject: [PATCH 08/15] mv testWebhookReceiver.ts->devTools/ --- README.md | 2 +- dev.ts | 2 +- testWebhookReceiver.ts => devTools/testWebhookReceiver.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename testWebhookReceiver.ts => devTools/testWebhookReceiver.ts (95%) diff --git a/README.md b/README.md index 914bd02..a38f44c 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ this script with required permissions. After configuring `.env`, start the components with `deno task dev`. The database will automatically be connected with the default configuration. The script will also launch an instance of an embedded AMQP broker (https://deno.land/x/lop/mod.ts) and a webhook receiver -for development (`testWebhookReceiver.ts`) on http://localhost:8888. +for development (`devTools/testWebhookReceiver.ts`) on http://localhost:8888. The web component can be started with `deno task dev-web`. diff --git a/dev.ts b/dev.ts index 024ab5d..8ce8d2e 100755 --- a/dev.ts +++ b/dev.ts @@ -29,7 +29,7 @@ import { runWithChainDefinition, runWithPrisma, } from "./utils/runUtils.ts"; -import { testWebhookReceiver } from "./testWebhookReceiver.ts"; +import { testWebhookReceiver } from "./devTools/testWebhookReceiver.ts"; async function prepareAndMain() { const status = await new Deno.Command("deno", { diff --git a/testWebhookReceiver.ts b/devTools/testWebhookReceiver.ts similarity index 95% rename from testWebhookReceiver.ts rename to devTools/testWebhookReceiver.ts index 3608f4a..9f2245b 100644 --- a/testWebhookReceiver.ts +++ b/devTools/testWebhookReceiver.ts @@ -8,8 +8,8 @@ import { parse, stringify as losslessJsonStringify } from "npm:lossless-json"; import { defaultLogFormatter, TestWebhookReceiverLoggerName, -} from "./utils/logUtils.ts"; -import { runAndCleanup } from "./utils/runUtils.ts"; +} from "../utils/logUtils.ts"; +import { runAndCleanup } from "../utils/runUtils.ts"; function numberParser(value: string) { const n = Number(value); From 02e4d42a6377f667e246a00f3fab6ae59c38a236 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 09:25:01 +0000 Subject: [PATCH 09/15] mv constants.ts, apiSchema.ts->constants --- api.ts | 2 +- apiSchema.ts => constants/apiSchema.ts | 0 constants.ts => constants/constants.ts | 0 emitter.ts | 2 +- messages/control.ts | 2 +- observer.ts | 2 +- scripts/signal-reload.ts | 2 +- web/main.ts | 5 ++++- web/routes/api/sources.ts | 2 +- web/routes/api/sources/testWebhook.ts | 2 +- web/routes/api/webhook.ts | 2 +- 11 files changed, 12 insertions(+), 9 deletions(-) rename apiSchema.ts => constants/apiSchema.ts (100%) rename constants.ts => constants/constants.ts (100%) diff --git a/api.ts b/api.ts index cd0a984..bac5862 100644 --- a/api.ts +++ b/api.ts @@ -20,7 +20,7 @@ import type { PrismaClient } from "./prisma/shim.ts"; import { serializeEventResponse } from "./messages/EventResponse.ts"; import { formatAbiItemPrototype } from "./abitype.ts"; -import { validateEventRequest } from "./apiSchema.ts"; +import { validateEventRequest } from "./constants/apiSchema.ts"; import { ApiBehindReverseProxyEnvKey, ApiUrlEnvKey, diff --git a/apiSchema.ts b/constants/apiSchema.ts similarity index 100% rename from apiSchema.ts rename to constants/apiSchema.ts diff --git a/constants.ts b/constants/constants.ts similarity index 100% rename from constants.ts rename to constants/constants.ts diff --git a/emitter.ts b/emitter.ts index 2e416be..cfb85ce 100644 --- a/emitter.ts +++ b/emitter.ts @@ -17,7 +17,7 @@ import { ControlEmitterRoutingKey, ControlExchangeName, EvmEventsQueueName, -} from "./constants.ts"; +} from "./constants/constants.ts"; import { runWithAmqp, runWithChainDefinition, diff --git a/messages/control.ts b/messages/control.ts index 0a7e935..c5bbc0c 100644 --- a/messages/control.ts +++ b/messages/control.ts @@ -11,7 +11,7 @@ import { ControlEmitterRoutingKey, ControlExchangeName, ControlObserverRoutingKey, -} from "../constants.ts"; +} from "../constants/constants.ts"; import { ControlLoggerName } from "../utils/logUtils.ts"; export function reload( diff --git a/observer.ts b/observer.ts index b9b7371..33467fd 100644 --- a/observer.ts +++ b/observer.ts @@ -22,7 +22,7 @@ import { ControlExchangeName, ControlObserverRoutingKey, EvmEventsQueueName, -} from "./constants.ts"; +} from "./constants/constants.ts"; import { deserializeControlMessage } from "./messages/ControlMessage.ts"; import { serializeEventMessage } from "./messages/EventMessage.ts"; import { BlockFinalityEnvKey, combinedEnv } from "./utils/envUtils.ts"; diff --git a/scripts/signal-reload.ts b/scripts/signal-reload.ts index bc0315b..0999e84 100644 --- a/scripts/signal-reload.ts +++ b/scripts/signal-reload.ts @@ -4,7 +4,7 @@ import { ControlEmitterRoutingKey, ControlExchangeName, ControlObserverRoutingKey, -} from "../constants.ts"; +} from "../constants/constants.ts"; import { reload } from "../messages/control.ts"; const conn = await connect(); diff --git a/web/main.ts b/web/main.ts index 1e6a7f6..d127e99 100644 --- a/web/main.ts +++ b/web/main.ts @@ -15,7 +15,10 @@ import twindConfig from "./twind.config.ts"; import type { AmqpChannel, AmqpConnection } from "amqp/mod.ts"; -import { ControlExchangeName, EvmEventsQueueName } from "~/constants.ts"; +import { + ControlExchangeName, + EvmEventsQueueName, +} from "~/constants/constants.ts"; import { combinedEnv, WebUISessionAppKeyEnvKey, diff --git a/web/routes/api/sources.ts b/web/routes/api/sources.ts index 24463ef..10f8f61 100644 --- a/web/routes/api/sources.ts +++ b/web/routes/api/sources.ts @@ -10,7 +10,7 @@ import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; import { formatAbiItemPrototype } from "~/abitype.ts"; import { reload as reloadControl } from "~/messages/control.ts"; -import { ControlObserverRoutingKey } from "~/constants.ts"; +import { ControlObserverRoutingKey } from "~/constants/constants.ts"; import type { User } from "~/prisma/shim.ts"; import type { SourceEntry } from "web/islands/ListSources.tsx"; diff --git a/web/routes/api/sources/testWebhook.ts b/web/routes/api/sources/testWebhook.ts index 50ae041..63583d6 100644 --- a/web/routes/api/sources/testWebhook.ts +++ b/web/routes/api/sources/testWebhook.ts @@ -8,7 +8,7 @@ import { toBytes } from "npm:viem"; import { amqpChannel, prisma } from "web/main.ts"; import { logRequest } from "web/util.ts"; import { serializeEventMessage } from "~/messages/EventMessage.ts"; -import { EvmEventsQueueName } from "~/constants.ts"; +import { EvmEventsQueueName } from "~/constants/constants.ts"; export const handler: Handlers = { async POST(req, ctx) { diff --git a/web/routes/api/webhook.ts b/web/routes/api/webhook.ts index 5b44ef0..ce67b13 100644 --- a/web/routes/api/webhook.ts +++ b/web/routes/api/webhook.ts @@ -9,7 +9,7 @@ import { getAddress, toBytes, toHex } from "npm:viem"; import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; import { reload as reloadControl } from "~/messages/control.ts"; -import { ControlEmitterRoutingKey } from "~/constants.ts"; +import { ControlEmitterRoutingKey } from "~/constants/constants.ts"; import type { User } from "~/prisma/shim.ts"; import type { WebhookEntry } from "web/islands/ListWebhook.tsx"; From 2c8cc8dbb04c1499569c635e58ac37365afee371 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 09:36:15 +0000 Subject: [PATCH 10/15] Add viem to import map, combine abi utils --- api.ts | 4 +- decodeEventLog.ts | 205 --------------------- deno.jsonc | 2 + emitter.ts | 4 +- messages/EventResponse.ts | 5 +- observer.ts | 3 +- sample/chainDefinitions/goerli.ts | 3 +- sample/contracts/deploy-sample-contract.ts | 4 +- sample/seed-prisma.ts | 4 +- abitype.ts => utils/abiUtils.ts | 201 ++++++++++++++++++++ utils/runUtils.ts | 2 +- web/routes/api/abi.ts | 4 +- web/routes/api/sources.ts | 4 +- web/routes/api/sources/testWebhook.ts | 2 +- web/routes/api/webhook.ts | 2 +- 15 files changed, 224 insertions(+), 225 deletions(-) delete mode 100644 decodeEventLog.ts rename abitype.ts => utils/abiUtils.ts (51%) diff --git a/api.ts b/api.ts index bac5862..c6fe7c4 100644 --- a/api.ts +++ b/api.ts @@ -14,12 +14,12 @@ import { oakCors } from "https://deno.land/x/cors@v1.2.2/mod.ts"; import { Buffer } from "node:buffer"; import { stringify as losslessJsonStringify } from "npm:lossless-json"; -import { getAddress, keccak256, toHex } from "npm:viem"; +import { getAddress, keccak256, toHex } from "viem"; import type { PrismaClient } from "./prisma/shim.ts"; import { serializeEventResponse } from "./messages/EventResponse.ts"; -import { formatAbiItemPrototype } from "./abitype.ts"; +import { formatAbiItemPrototype } from "./utils/abiUtils.ts"; import { validateEventRequest } from "./constants/apiSchema.ts"; import { ApiBehindReverseProxyEnvKey, diff --git a/decodeEventLog.ts b/decodeEventLog.ts deleted file mode 100644 index 2d4b8b9..0000000 --- a/decodeEventLog.ts +++ /dev/null @@ -1,205 +0,0 @@ -import type { - Abi, - AbiParameter, - ExtractAbiEventNames, - Narrow, -} from "https://esm.sh/abitype"; - -import { - AbiDecodingDataSizeTooSmallError, - AbiEventSignatureEmptyTopicsError, - AbiEventSignatureNotFoundError, - DecodeLogDataMismatch, - DecodeLogTopicsMismatch, -} from "https://esm.sh/viem/dist/esm/errors/abi.js"; -import type { - EventDefinition, - GetEventArgsFromTopics, - InferEventName, -} from "https://esm.sh/viem/dist/types/types/contract.d.ts"; -import type { Hex } from "https://esm.sh/viem/dist/types/types/misc.d.ts"; -import type { Prettify } from "https://esm.sh/viem/dist/types/types/utils.d.ts"; -import { getEventSelector } from "https://esm.sh/viem/dist/esm/utils/hash/getEventSelector.js"; -import { decodeAbiParameters } from "https://esm.sh/viem/dist/esm/utils/abi/decodeAbiParameters.js"; -import type { DecodeAbiParametersReturnType } from "https://esm.sh/viem/dist/types/utils/abi/decodeAbiParameters.d.ts"; -import { formatAbiItem } from "https://esm.sh/viem/dist/esm/utils/abi/formatAbiItem.js"; - -export type DecodeEventLogParameters< - TAbi extends Abi | readonly unknown[] = Abi, - TEventName extends string | undefined = string, - TTopics extends Hex[] = Hex[], - TData extends Hex | undefined = undefined, - TStrict extends boolean = true, -> = { - abi: Narrow; - data?: TData; - eventName?: InferEventName; - strict?: TStrict; - topics: [signature: Hex, ...args: TTopics] | []; -}; - -export type DecodeEventLogReturnType< - TAbi extends Abi | readonly unknown[] = Abi, - TEventName extends string | undefined = string, - TTopics extends Hex[] = Hex[], - TData extends Hex | undefined = undefined, - TStrict extends boolean = true, - _EventNames extends string = TAbi extends Abi ? Abi extends TAbi ? string - : ExtractAbiEventNames - : string, -> = TEventName extends _EventNames[number] ? Prettify< - { - eventName: TEventName; - } & GetEventArgsFromTopics - > - : { - [TName in _EventNames]: Prettify< - { - eventName: TName; - } & GetEventArgsFromTopics - >; - }[_EventNames]; - -const docsPath = "/docs/contract/decodeEventLog"; - -export function decodeEventLog< - TAbi extends Abi | readonly unknown[], - TEventName extends string | undefined = undefined, - TTopics extends Hex[] = Hex[], - TData extends Hex | undefined = undefined, - TStrict extends boolean = true, ->({ - abi, - data, - strict: strict_, - topics, -}: DecodeEventLogParameters< - TAbi, - TEventName, - TTopics, - TData, - TStrict ->): DecodeEventLogReturnType { - const strict = strict_ ?? true; - const [signature, ...argTopics] = topics; - if (!signature) { - throw new AbiEventSignatureEmptyTopicsError({ - docsPath, - }); - } - const abiItem = (abi as Abi).find( - (x) => - x.type === "event" && - signature === getEventSelector(formatAbiItem(x) as EventDefinition), - ); - if (!(abiItem && "name" in abiItem) || abiItem.type !== "event") { - throw new AbiEventSignatureNotFoundError(signature, { - docsPath, - }); - } - - const { name, inputs } = abiItem; - const indexedInputs = inputs.filter((x) => "indexed" in x && x.indexed); - const nonIndexedInputs = inputs.filter((x) => !indexedInputs.includes(x)); - - const decodedTopics: DecodeAbiParametersReturnType = []; - type decodedDataType = DecodeAbiParametersReturnType; - let decodedData: decodedDataType = undefined as unknown as decodedDataType; - - // Decode topics (indexed args). - if (argTopics.length > 0) { - for (let i = 0; i < indexedInputs.length; i++) { - const param = indexedInputs[i]; - const topic = argTopics[i]; - if (!topic) { - throw new DecodeLogTopicsMismatch({ - abiItem, - param: param as AbiParameter & { indexed: boolean }, - }); - } - decodedTopics.push(decodeTopic({ param, value: topic })); - } - } - - // Decode data (non-indexed args). - if (nonIndexedInputs.length > 0) { - if (data && data !== "0x") { - try { - decodedData = decodeAbiParameters(nonIndexedInputs, data); - } catch (err) { - if (strict) { - if (err instanceof AbiDecodingDataSizeTooSmallError) { - throw new DecodeLogDataMismatch({ - abiItem, - data: err.data, - params: err.params, - size: err.size, - }); - } - throw err; - } - } - } else if (strict) { - throw new DecodeLogDataMismatch({ - abiItem, - data: "0x", - params: nonIndexedInputs, - size: 0, - }); - } - } - - const names = inputs.filter((x) => ("name" in x && x.name)).map((x) => - x.name! - ); - type ValueOf = T[keyof T]; - type Args = - & Array> - & { - [K in typeof names[number]]: ValueOf< - typeof decodedTopics | typeof decodedData - >[K]; - }; - const args: Args = inputs - .reduce( - (acc, x) => { - const arg = indexedInputs.includes(x) - ? decodedTopics.shift() - : decodedData - ? decodedData.shift() - : undefined; - if (arg !== undefined) { - acc.push(arg); - } - if (x.name) { - acc[x.name] = arg; - } - return acc; - }, - [] as unknown as Args, - ); - - return { - eventName: name, - args: args.length > 0 ? args : undefined, - } as unknown as DecodeEventLogReturnType< - TAbi, - TEventName, - TTopics, - TData, - TStrict - >; -} - -function decodeTopic({ param, value }: { param: AbiParameter; value: Hex }) { - if ( - param.type === "string" || - param.type === "bytes" || - param.type === "tuple" || - param.type.match(/^(.*)\[(\d+)?\]$/) - ) { - return value; - } - const decodedArg = decodeAbiParameters([param], value) || []; - return decodedArg[0]; -} diff --git a/deno.jsonc b/deno.jsonc index 0401f4f..b709d53 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -15,6 +15,8 @@ "std/": "https://deno.land/std@0.196.0/", "abitype": "https://esm.sh/v130/abitype@0.9.0", "abitype/": "https://esm.sh/v130/abitype@0.9.0/", + "viem": "https://esm.sh/v130/viem@1.6.0", + "viem/": "https://esm.sh/v130/viem@1.6.0/", "amqp/": "https://deno.land/x/amqp@v0.23.1/", "bencodex": "https://deno.land/x/bencodex@0.2.2/mod.ts", "oak": "https://deno.land/x/oak@v12.5.0/mod.ts", diff --git a/emitter.ts b/emitter.ts index cfb85ce..bf1ffb5 100644 --- a/emitter.ts +++ b/emitter.ts @@ -4,7 +4,7 @@ import { getLogger, setup as setupLog } from "std/log/mod.ts"; import type { AmqpConnection } from "amqp/mod.ts"; import { stringify as losslessJsonStringify } from "npm:lossless-json"; -import { type Chain, getAddress, toHex } from "npm:viem"; +import { type Chain, getAddress, toHex } from "viem"; import type { PrismaClient } from "./prisma/shim.ts"; @@ -80,7 +80,7 @@ export async function emitter( .map((topic, i) => [topic, i + 1]) .filter(([topic, _]) => topic != null) .map(([topic, i]) => - `[${i}] ${toHex(topic)}` + `[${i}] ${toHex(topic as Uint8Array)}` ).join(" ") } destination: ${dest.webhookUrl}` ).join(", ") diff --git a/messages/EventResponse.ts b/messages/EventResponse.ts index d8b1823..d2cded5 100644 --- a/messages/EventResponse.ts +++ b/messages/EventResponse.ts @@ -1,7 +1,6 @@ -import { getAddress, toHex } from "npm:viem"; +import { getAddress, toHex } from "viem"; -import { formatAbiItemPrototype } from "../abitype.ts"; -import { decodeEventLog } from "../decodeEventLog.ts"; +import { decodeEventLog, formatAbiItemPrototype } from "../utils/abiUtils.ts"; import { EventMessage } from "./EventMessage.ts"; export const serializeEventResponse = (evtMsg: EventMessage) => { diff --git a/observer.ts b/observer.ts index 33467fd..995d926 100644 --- a/observer.ts +++ b/observer.ts @@ -13,7 +13,7 @@ import { type Log as LogGeneric, toBytes, toHex, -} from "npm:viem"; +} from "viem"; import { Prisma, type PrismaClient } from "./prisma/shim.ts"; @@ -42,6 +42,7 @@ import { type Log = LogGeneric< bigint, number, + false, AbiEvent | undefined, undefined, [AbiEvent | undefined], diff --git a/sample/chainDefinitions/goerli.ts b/sample/chainDefinitions/goerli.ts index 3da0bf8..0c709da 100644 --- a/sample/chainDefinitions/goerli.ts +++ b/sample/chainDefinitions/goerli.ts @@ -1,4 +1,5 @@ -import type { Chain } from "npm:viem"; +// intentionally used URL to give an example +import type { Chain } from "https://esm.sh/v130/viem@1.6.0"; export default { id: 5, diff --git a/sample/contracts/deploy-sample-contract.ts b/sample/contracts/deploy-sample-contract.ts index 9915407..4850ff4 100644 --- a/sample/contracts/deploy-sample-contract.ts +++ b/sample/contracts/deploy-sample-contract.ts @@ -8,8 +8,8 @@ import { formatEther, formatGwei, http, -} from "npm:viem"; -import { privateKeyToAccount } from "npm:viem/accounts"; +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; import { getRelativeScriptPath, diff --git a/sample/seed-prisma.ts b/sample/seed-prisma.ts index 972b12f..6651c5e 100644 --- a/sample/seed-prisma.ts +++ b/sample/seed-prisma.ts @@ -1,11 +1,11 @@ import { type AbiEvent, narrow } from "abitype"; import { Buffer } from "node:buffer"; -import { keccak256, toBytes } from "npm:viem"; +import { keccak256, toBytes } from "viem"; import { PrismaClient } from "~/prisma/shim.ts"; -import { formatAbiItemPrototype } from "../abitype.ts"; +import { formatAbiItemPrototype } from "../utils/abiUtils.ts"; import { combinedEnv, DatabaseUrlEnvKey } from "../utils/envUtils.ts"; import sampleAbiJson from "./contracts/sampleAbi.json" assert { type: "json" }; diff --git a/abitype.ts b/utils/abiUtils.ts similarity index 51% rename from abitype.ts rename to utils/abiUtils.ts index 3ca557c..bd8b21e 100644 --- a/abitype.ts +++ b/utils/abiUtils.ts @@ -8,11 +8,32 @@ import type { AbiFunction, AbiParameter, AbiReceive, + ExtractAbiEventNames, + Narrow, } from "abitype"; import type { Join } from "abitype/dist/types/types.d.ts"; // @deno-types="abitype/dist/types/regex.d.ts" import { execTyped } from "abitype/dist/esm/regex.js"; +import { + AbiDecodingDataSizeTooSmallError, + AbiEventSignatureEmptyTopicsError, + AbiEventSignatureNotFoundError, + DecodeLogDataMismatch, + DecodeLogTopicsMismatch, +} from "viem/dist/esm/errors/abi.js"; +import type { + EventDefinition, + GetEventArgsFromTopics, + InferEventName, +} from "viem/dist/types/types/contract.d.ts"; +import type { Hex } from "viem/dist/types/types/misc.d.ts"; +import type { Prettify } from "viem/dist/types/types/utils.d.ts"; +import { getEventSelector } from "viem/dist/esm/utils/hash/getEventSelector.js"; +import { decodeAbiParameters } from "viem/dist/esm/utils/abi/decodeAbiParameters.js"; +import type { DecodeAbiParametersReturnType } from "viem/dist/types/utils/abi/decodeAbiParameters.d.ts"; +import { formatAbiItem } from "viem/dist/esm/utils/abi/formatAbiItem.js"; + /** * Formats {@link AbiParameter} to human-readable ABI parameter prototype. * @@ -192,3 +213,183 @@ export function formatAbiItemPrototype( } else if (abiItem.type === "fallback") return "fallback()" as Result; return "receive()" as Result; } + +export type DecodeEventLogParameters< + TAbi extends Abi | readonly unknown[] = Abi, + TEventName extends string | undefined = string, + TTopics extends Hex[] = Hex[], + TData extends Hex | undefined = undefined, + TStrict extends boolean = true, +> = { + abi: Narrow; + data?: TData; + eventName?: InferEventName; + strict?: TStrict; + topics: [signature: Hex, ...args: TTopics] | []; +}; + +export type DecodeEventLogReturnType< + TAbi extends Abi | readonly unknown[] = Abi, + TEventName extends string | undefined = string, + TTopics extends Hex[] = Hex[], + TData extends Hex | undefined = undefined, + TStrict extends boolean = true, + _EventNames extends string = TAbi extends Abi ? Abi extends TAbi ? string + : ExtractAbiEventNames + : string, +> = TEventName extends _EventNames[number] ? Prettify< + { + eventName: TEventName; + } & GetEventArgsFromTopics + > + : { + [TName in _EventNames]: Prettify< + { + eventName: TName; + } & GetEventArgsFromTopics + >; + }[_EventNames]; + +const docsPath = "/docs/contract/decodeEventLog"; + +export function decodeEventLog< + TAbi extends Abi | readonly unknown[], + TEventName extends string | undefined = undefined, + TTopics extends Hex[] = Hex[], + TData extends Hex | undefined = undefined, + TStrict extends boolean = true, +>({ + abi, + data, + strict: strict_, + topics, +}: DecodeEventLogParameters< + TAbi, + TEventName, + TTopics, + TData, + TStrict +>): DecodeEventLogReturnType { + const strict = strict_ ?? true; + const [signature, ...argTopics] = topics; + if (!signature) { + throw new AbiEventSignatureEmptyTopicsError({ + docsPath, + }); + } + const abiItem = (abi as Abi).find( + (x) => + x.type === "event" && + signature === getEventSelector(formatAbiItem(x) as EventDefinition), + ); + if (!(abiItem && "name" in abiItem) || abiItem.type !== "event") { + throw new AbiEventSignatureNotFoundError(signature, { + docsPath, + }); + } + + const { name, inputs } = abiItem; + const indexedInputs = inputs.filter((x) => "indexed" in x && x.indexed); + const nonIndexedInputs = inputs.filter((x) => !indexedInputs.includes(x)); + + const decodedTopics: DecodeAbiParametersReturnType = []; + type decodedDataType = DecodeAbiParametersReturnType; + let decodedData: decodedDataType = undefined as unknown as decodedDataType; + + // Decode topics (indexed args). + if (argTopics.length > 0) { + for (let i = 0; i < indexedInputs.length; i++) { + const param = indexedInputs[i]; + const topic = argTopics[i]; + if (!topic) { + throw new DecodeLogTopicsMismatch({ + abiItem, + param: param as AbiParameter & { indexed: boolean }, + }); + } + decodedTopics.push(decodeTopic({ param, value: topic })); + } + } + + // Decode data (non-indexed args). + if (nonIndexedInputs.length > 0) { + if (data && data !== "0x") { + try { + decodedData = decodeAbiParameters(nonIndexedInputs, data); + } catch (err) { + if (strict) { + if (err instanceof AbiDecodingDataSizeTooSmallError) { + throw new DecodeLogDataMismatch({ + abiItem, + data: err.data, + params: err.params, + size: err.size, + }); + } + throw err; + } + } + } else if (strict) { + throw new DecodeLogDataMismatch({ + abiItem, + data: "0x", + params: nonIndexedInputs, + size: 0, + }); + } + } + + const names = inputs.filter((x) => ("name" in x && x.name)).map((x) => + x.name! + ); + type ValueOf = T[keyof T]; + type Args = + & Array> + & { + [K in typeof names[number]]: ValueOf< + typeof decodedTopics | typeof decodedData + >[K]; + }; + const args: Args = inputs + .reduce( + (acc, x) => { + const arg = indexedInputs.includes(x) + ? decodedTopics.shift() + : decodedData + ? decodedData.shift() + : undefined; + if (arg !== undefined) { + acc.push(arg); + } + if (x.name) { + acc[x.name] = arg; + } + return acc; + }, + [] as unknown as Args, + ); + + return { + eventName: name, + args: args.length > 0 ? args : undefined, + } as unknown as DecodeEventLogReturnType< + TAbi, + TEventName, + TTopics, + TData, + TStrict + >; +} + +function decodeTopic({ param, value }: { param: AbiParameter; value: Hex }) { + if ( + param.type === "string" || + param.type === "bytes" || + param.type === "tuple" || + param.type.match(/^(.*)\[(\d+)?\]$/) + ) { + return value; + } + const decodedArg = decodeAbiParameters([param], value) || []; + return decodedArg[0]; +} diff --git a/utils/runUtils.ts b/utils/runUtils.ts index 71211f0..1bcf5e8 100644 --- a/utils/runUtils.ts +++ b/utils/runUtils.ts @@ -5,7 +5,7 @@ import { } from "amqp/mod.ts"; import { parseOptions } from "amqp/src/amqp_connect_options.ts"; -import type { Chain } from "npm:viem"; +import type { Chain } from "viem"; import { type Prisma, PrismaClient } from "../prisma/shim.ts"; diff --git a/web/routes/api/abi.ts b/web/routes/api/abi.ts index 88206f9..dd90ef4 100644 --- a/web/routes/api/abi.ts +++ b/web/routes/api/abi.ts @@ -6,11 +6,11 @@ import type { WithSession } from "fresh-session"; import type { Abi, AbiEvent } from "abitype"; import { Buffer } from "node:buffer"; -import { keccak256, toBytes, toHex } from "npm:viem"; +import { keccak256, toBytes, toHex } from "viem"; import { prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; -import { formatAbiItemPrototype } from "~/abitype.ts"; +import { formatAbiItemPrototype } from "~/utils/abiUtils.ts"; import type { User } from "~/prisma/shim.ts"; import type { AbiEntry } from "web/islands/ListAbi.tsx"; diff --git a/web/routes/api/sources.ts b/web/routes/api/sources.ts index 10f8f61..679514d 100644 --- a/web/routes/api/sources.ts +++ b/web/routes/api/sources.ts @@ -4,11 +4,11 @@ import { type Handlers, Status } from "fresh/server.ts"; import type { WithSession } from "fresh-session"; import { Buffer } from "node:buffer"; -import { getAddress, toBytes, toHex } from "npm:viem"; +import { getAddress, toBytes, toHex } from "viem"; import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; -import { formatAbiItemPrototype } from "~/abitype.ts"; +import { formatAbiItemPrototype } from "~/utils/abiUtils.ts"; import { reload as reloadControl } from "~/messages/control.ts"; import { ControlObserverRoutingKey } from "~/constants/constants.ts"; import type { User } from "~/prisma/shim.ts"; diff --git a/web/routes/api/sources/testWebhook.ts b/web/routes/api/sources/testWebhook.ts index 63583d6..85e99d6 100644 --- a/web/routes/api/sources/testWebhook.ts +++ b/web/routes/api/sources/testWebhook.ts @@ -3,7 +3,7 @@ import { LogLevels } from "std/log/levels.ts"; import { type Handlers, Status } from "fresh/server.ts"; import { Buffer } from "node:buffer"; -import { toBytes } from "npm:viem"; +import { toBytes } from "viem"; import { amqpChannel, prisma } from "web/main.ts"; import { logRequest } from "web/util.ts"; diff --git a/web/routes/api/webhook.ts b/web/routes/api/webhook.ts index ce67b13..0724c0f 100644 --- a/web/routes/api/webhook.ts +++ b/web/routes/api/webhook.ts @@ -4,7 +4,7 @@ import { type Handlers, Status } from "fresh/server.ts"; import type { WithSession } from "fresh-session"; import { Buffer } from "node:buffer"; -import { getAddress, toBytes, toHex } from "npm:viem"; +import { getAddress, toBytes, toHex } from "viem"; import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; From 7485d6a22e5ed634aa8cc09e9753dde7aa1481ed Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 09:53:16 +0000 Subject: [PATCH 11/15] Fix prisma type errors --- api.ts | 24 +++++++++++++++++------- observer.ts | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/api.ts b/api.ts index c6fe7c4..83b597b 100644 --- a/api.ts +++ b/api.ts @@ -71,20 +71,27 @@ export function api(prisma: PrismaClient) { ctx.response.body = losslessJsonStringify((await prisma.event.findMany({ where: { - blockHash: request.blockHash && hexToBuffer(request.blockHash), + blockHash: (request.blockHash && hexToBuffer(request.blockHash)) as + | Buffer + | undefined, blockNumber: request.blockIndex ?? { gte: blockFrom, lte: blockTo }, logIndex: request.logIndex, - txHash: request.transactionHash && hexToBuffer(request.transactionHash), - sourceAddress: request.sourceAddress && - hexToBuffer(request.sourceAddress), - abiHash: (request.abiHash && hexToBuffer(request.abiHash)) ?? + txHash: + (request.transactionHash && hexToBuffer(request.transactionHash)) as + | Buffer + | undefined, + sourceAddress: + (request.sourceAddress && hexToBuffer(request.sourceAddress)) as + | Buffer + | undefined, + abiHash: ((request.abiHash && hexToBuffer(request.abiHash)) ?? (request.abiSignature && Buffer.from( keccak256( new TextEncoder().encode(request.abiSignature), "bytes", ), - )), + ))) as Buffer | undefined, }, include: { Abi: true }, })).map((evt) => @@ -92,7 +99,10 @@ export function api(prisma: PrismaClient) { address: evt.sourceAddress, sigHash: evt.abiHash, abi: evt.Abi.json, - topics: [evt.topic1, evt.topic2, evt.topic3], + topics: [evt.topic3, evt.topic2, evt.topic1].reduce( + (acc, x) => x != undefined ? [x, ...acc] : [], + [] as Uint8Array[], + ), data: evt.data, logIndex: BigInt(evt.logIndex), blockNumber: BigInt(evt.blockNumber), diff --git a/observer.ts b/observer.ts index 995d926..622fb71 100644 --- a/observer.ts +++ b/observer.ts @@ -456,7 +456,7 @@ export async function observer( abi: abis[toHex(x.abiHash)], topics: [x.topic3, x.topic2, x.topic1].reduce( (acc, x) => x != undefined ? [x, ...acc] : [], - [], + [] as Uint8Array[], ), data: x.data, logIndex: BigInt(x.logIndex), From 18b5c0660f875f5af9cce9273b82bd6799b41499 Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 10:02:03 +0000 Subject: [PATCH 12/15] Organize import order --- api.ts | 2 +- constants/apiSchema.ts | 2 +- dev.ts | 6 +++--- emitter.ts | 22 +++++++++++----------- messages/control.ts | 10 +++++----- observer.ts | 4 ++-- utils/abiUtils.ts | 6 +++--- utils/logUtils.ts | 2 +- web/routes/api/abi.ts | 4 ++-- web/routes/api/sources.ts | 6 +++--- web/routes/api/sources/testWebhook.ts | 2 +- web/routes/api/webhook.ts | 4 ++-- web/util.ts | 2 +- 13 files changed, 36 insertions(+), 36 deletions(-) diff --git a/api.ts b/api.ts index 83b597b..bf92f7e 100644 --- a/api.ts +++ b/api.ts @@ -18,9 +18,9 @@ import { getAddress, keccak256, toHex } from "viem"; import type { PrismaClient } from "./prisma/shim.ts"; +import { validateEventRequest } from "./constants/apiSchema.ts"; import { serializeEventResponse } from "./messages/EventResponse.ts"; import { formatAbiItemPrototype } from "./utils/abiUtils.ts"; -import { validateEventRequest } from "./constants/apiSchema.ts"; import { ApiBehindReverseProxyEnvKey, ApiUrlEnvKey, diff --git a/constants/apiSchema.ts b/constants/apiSchema.ts index 6418333..7e8ec5a 100644 --- a/constants/apiSchema.ts +++ b/constants/apiSchema.ts @@ -1,5 +1,5 @@ -import Ajv, { type JSONSchemaType } from "https://esm.sh/ajv@8.12.0"; import ajvFormats from "https://esm.sh/ajv-formats@2.1.1"; +import Ajv, { type JSONSchemaType } from "https://esm.sh/ajv@8.12.0"; const ajv = new Ajv({ allowUnionTypes: true }); ajvFormats(ajv); diff --git a/dev.ts b/dev.ts index 8ce8d2e..d1f01f9 100755 --- a/dev.ts +++ b/dev.ts @@ -1,16 +1,18 @@ #!/usr/bin/env -S deno run --allow-read --allow-env --allow-run --allow-sys import { parse } from "std/flags/mod.ts"; -import * as path from "std/path/mod.ts"; import { ConsoleHandler } from "std/log/handlers.ts"; import { getLogger, setup as setupLog } from "std/log/mod.ts"; +import * as path from "std/path/mod.ts"; import { parseOptions } from "amqp/src/amqp_connect_options.ts"; import { broker } from "https://deno.land/x/lop@0.0.0-alpha.2/mod.ts"; import { api } from "./api.ts"; +import { testWebhookReceiver } from "./devTools/testWebhookReceiver.ts"; import { emitter } from "./emitter.ts"; +import { observer } from "./observer.ts"; import { AmqpBrokerUrlEnvKey, combinedEnv } from "./utils/envUtils.ts"; import { ApiLoggerName, @@ -22,14 +24,12 @@ import { TestWebhookReceiverLoggerName, WebLoggerName, } from "./utils/logUtils.ts"; -import { observer } from "./observer.ts"; import { block, runWithAmqp, runWithChainDefinition, runWithPrisma, } from "./utils/runUtils.ts"; -import { testWebhookReceiver } from "./devTools/testWebhookReceiver.ts"; async function prepareAndMain() { const status = await new Deno.Command("deno", { diff --git a/emitter.ts b/emitter.ts index bf1ffb5..69c050a 100644 --- a/emitter.ts +++ b/emitter.ts @@ -8,30 +8,30 @@ import { type Chain, getAddress, toHex } from "viem"; import type { PrismaClient } from "./prisma/shim.ts"; -import { deserializeControlMessage } from "./messages/ControlMessage.ts"; -import { - deserializeEventMessage, - EventMessage, -} from "./messages/EventMessage.ts"; import { ControlEmitterRoutingKey, ControlExchangeName, EvmEventsQueueName, } from "./constants/constants.ts"; +import { deserializeControlMessage } from "./messages/ControlMessage.ts"; import { - runWithAmqp, - runWithChainDefinition, - runWithPrisma, -} from "./utils/runUtils.ts"; -import { uint8ArrayEquals } from "./utils/uint8ArrayUtils.ts"; + deserializeEventMessage, + EventMessage, +} from "./messages/EventMessage.ts"; import { serializeEventResponse } from "./messages/EventResponse.ts"; +import { createMutex } from "./utils/concurrencyUtils.ts"; import { defaultLogFormatter, EmitterLoggerName, getInternalLoggers, getLoggingLevel, } from "./utils/logUtils.ts"; -import { createMutex } from "./utils/concurrencyUtils.ts"; +import { + runWithAmqp, + runWithChainDefinition, + runWithPrisma, +} from "./utils/runUtils.ts"; +import { uint8ArrayEquals } from "./utils/uint8ArrayUtils.ts"; export async function emitter( chain: Chain, diff --git a/messages/control.ts b/messages/control.ts index c5bbc0c..51ecfcf 100644 --- a/messages/control.ts +++ b/messages/control.ts @@ -2,17 +2,17 @@ import { getLogger } from "std/log/mod.ts"; import type { AmqpChannel } from "amqp/mod.ts"; -import { - type EmitterControlMessages, - type ObserverControlMessages, - serializeControlMessage, -} from "./ControlMessage.ts"; import { ControlEmitterRoutingKey, ControlExchangeName, ControlObserverRoutingKey, } from "../constants/constants.ts"; import { ControlLoggerName } from "../utils/logUtils.ts"; +import { + type EmitterControlMessages, + type ObserverControlMessages, + serializeControlMessage, +} from "./ControlMessage.ts"; export function reload( amqpChannel: AmqpChannel, diff --git a/observer.ts b/observer.ts index 622fb71..7abef7d 100644 --- a/observer.ts +++ b/observer.ts @@ -1,8 +1,8 @@ import { ConsoleHandler } from "std/log/handlers.ts"; import { getLogger, setup as setupLog } from "std/log/mod.ts"; -import type { AmqpConnection } from "amqp/mod.ts"; import type { AbiEvent } from "abitype"; +import type { AmqpConnection } from "amqp/mod.ts"; import { Buffer } from "node:buffer"; import { @@ -17,7 +17,6 @@ import { import { Prisma, type PrismaClient } from "./prisma/shim.ts"; -import { createMutex } from "./utils/concurrencyUtils.ts"; import { ControlExchangeName, ControlObserverRoutingKey, @@ -25,6 +24,7 @@ import { } from "./constants/constants.ts"; import { deserializeControlMessage } from "./messages/ControlMessage.ts"; import { serializeEventMessage } from "./messages/EventMessage.ts"; +import { createMutex } from "./utils/concurrencyUtils.ts"; import { BlockFinalityEnvKey, combinedEnv } from "./utils/envUtils.ts"; import { defaultLogFormatter, diff --git a/utils/abiUtils.ts b/utils/abiUtils.ts index bd8b21e..9143d7b 100644 --- a/utils/abiUtils.ts +++ b/utils/abiUtils.ts @@ -22,6 +22,9 @@ import { DecodeLogDataMismatch, DecodeLogTopicsMismatch, } from "viem/dist/esm/errors/abi.js"; +import { decodeAbiParameters } from "viem/dist/esm/utils/abi/decodeAbiParameters.js"; +import { formatAbiItem } from "viem/dist/esm/utils/abi/formatAbiItem.js"; +import { getEventSelector } from "viem/dist/esm/utils/hash/getEventSelector.js"; import type { EventDefinition, GetEventArgsFromTopics, @@ -29,10 +32,7 @@ import type { } from "viem/dist/types/types/contract.d.ts"; import type { Hex } from "viem/dist/types/types/misc.d.ts"; import type { Prettify } from "viem/dist/types/types/utils.d.ts"; -import { getEventSelector } from "viem/dist/esm/utils/hash/getEventSelector.js"; -import { decodeAbiParameters } from "viem/dist/esm/utils/abi/decodeAbiParameters.js"; import type { DecodeAbiParametersReturnType } from "viem/dist/types/utils/abi/decodeAbiParameters.d.ts"; -import { formatAbiItem } from "viem/dist/esm/utils/abi/formatAbiItem.js"; /** * Formats {@link AbiParameter} to human-readable ABI parameter prototype. diff --git a/utils/logUtils.ts b/utils/logUtils.ts index e7f5b3e..0d0c65a 100644 --- a/utils/logUtils.ts +++ b/utils/logUtils.ts @@ -1,11 +1,11 @@ import { format as formatDate } from "std/datetime/mod.ts"; +import { type LevelName, LogLevelNames } from "std/log/levels.ts"; import { Logger, type LoggerConfig, LogLevels, type LogRecord, } from "std/log/mod.ts"; -import { type LevelName, LogLevelNames } from "std/log/levels.ts"; import { combinedEnv, LogLevelEnvKey } from "./envUtils.ts"; diff --git a/web/routes/api/abi.ts b/web/routes/api/abi.ts index dd90ef4..ebc0f54 100644 --- a/web/routes/api/abi.ts +++ b/web/routes/api/abi.ts @@ -1,7 +1,7 @@ import { LogLevels } from "std/log/levels.ts"; -import { type Handlers, Status } from "fresh/server.ts"; import type { WithSession } from "fresh-session"; +import { type Handlers, Status } from "fresh/server.ts"; import type { Abi, AbiEvent } from "abitype"; @@ -10,8 +10,8 @@ import { keccak256, toBytes, toHex } from "viem"; import { prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; -import { formatAbiItemPrototype } from "~/utils/abiUtils.ts"; import type { User } from "~/prisma/shim.ts"; +import { formatAbiItemPrototype } from "~/utils/abiUtils.ts"; import type { AbiEntry } from "web/islands/ListAbi.tsx"; diff --git a/web/routes/api/sources.ts b/web/routes/api/sources.ts index 679514d..c5733b3 100644 --- a/web/routes/api/sources.ts +++ b/web/routes/api/sources.ts @@ -1,17 +1,17 @@ import { LogLevels } from "std/log/levels.ts"; -import { type Handlers, Status } from "fresh/server.ts"; import type { WithSession } from "fresh-session"; +import { type Handlers, Status } from "fresh/server.ts"; import { Buffer } from "node:buffer"; import { getAddress, toBytes, toHex } from "viem"; import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; -import { formatAbiItemPrototype } from "~/utils/abiUtils.ts"; -import { reload as reloadControl } from "~/messages/control.ts"; import { ControlObserverRoutingKey } from "~/constants/constants.ts"; +import { reload as reloadControl } from "~/messages/control.ts"; import type { User } from "~/prisma/shim.ts"; +import { formatAbiItemPrototype } from "~/utils/abiUtils.ts"; import type { SourceEntry } from "web/islands/ListSources.tsx"; diff --git a/web/routes/api/sources/testWebhook.ts b/web/routes/api/sources/testWebhook.ts index 85e99d6..a06a42d 100644 --- a/web/routes/api/sources/testWebhook.ts +++ b/web/routes/api/sources/testWebhook.ts @@ -7,8 +7,8 @@ import { toBytes } from "viem"; import { amqpChannel, prisma } from "web/main.ts"; import { logRequest } from "web/util.ts"; -import { serializeEventMessage } from "~/messages/EventMessage.ts"; import { EvmEventsQueueName } from "~/constants/constants.ts"; +import { serializeEventMessage } from "~/messages/EventMessage.ts"; export const handler: Handlers = { async POST(req, ctx) { diff --git a/web/routes/api/webhook.ts b/web/routes/api/webhook.ts index 0724c0f..e3ba516 100644 --- a/web/routes/api/webhook.ts +++ b/web/routes/api/webhook.ts @@ -1,15 +1,15 @@ import { LogLevels } from "std/log/mod.ts"; -import { type Handlers, Status } from "fresh/server.ts"; import type { WithSession } from "fresh-session"; +import { type Handlers, Status } from "fresh/server.ts"; import { Buffer } from "node:buffer"; import { getAddress, toBytes, toHex } from "viem"; import { amqpChannel, prisma } from "web/main.ts"; import { checkPermission, logRequest } from "web/util.ts"; -import { reload as reloadControl } from "~/messages/control.ts"; import { ControlEmitterRoutingKey } from "~/constants/constants.ts"; +import { reload as reloadControl } from "~/messages/control.ts"; import type { User } from "~/prisma/shim.ts"; import type { WebhookEntry } from "web/islands/ListWebhook.tsx"; diff --git a/web/util.ts b/web/util.ts index 03a7185..6edd05b 100644 --- a/web/util.ts +++ b/web/util.ts @@ -1,6 +1,6 @@ import { getCookies } from "std/http/cookie.ts"; -import { join, resolve } from "std/path/mod.ts"; import { LogLevels } from "std/log/mod.ts"; +import { join, resolve } from "std/path/mod.ts"; import { type ServeHandlerInfo, Status } from "fresh/server.ts"; From 8a27d0503ee0f9daa46d3911e027ca2cd4fc1c5c Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 10:10:49 +0000 Subject: [PATCH 13/15] Fix .env.example --- .env.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.example b/.env.example index 4ddf3f3..d983057 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,7 @@ # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. # See the documentation for all the connection string options: https://pris.ly/d/connection-strings -DATABASE_URL="prisma://localhost:8088?api_key=x5aol207TyRdhHuanAXlpTgVWDcK8jm2rVKJOQGbwaMqfvZbmGJnIbwBjUxYaBrFx1XOfyF4" # arbitrary api key +DATABASE_URL="file:./dev.db" CHAIN_DEFINITION_URL="file:./sample/chainDefinitions/goerli.json" AMQP_BROKER_URL="amqp://localhost" API_URL="http://localhost:8000" From 88f198f4cd89fe9e7f0a76f8da9c2a8dda5295ba Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 10:18:35 +0000 Subject: [PATCH 14/15] Enable deno task dev without any prerequisite --- dev.ts | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/dev.ts b/dev.ts index d1f01f9..f136ce3 100755 --- a/dev.ts +++ b/dev.ts @@ -9,10 +9,6 @@ import { parseOptions } from "amqp/src/amqp_connect_options.ts"; import { broker } from "https://deno.land/x/lop@0.0.0-alpha.2/mod.ts"; -import { api } from "./api.ts"; -import { testWebhookReceiver } from "./devTools/testWebhookReceiver.ts"; -import { emitter } from "./emitter.ts"; -import { observer } from "./observer.ts"; import { AmqpBrokerUrlEnvKey, combinedEnv } from "./utils/envUtils.ts"; import { ApiLoggerName, @@ -24,12 +20,6 @@ import { TestWebhookReceiverLoggerName, WebLoggerName, } from "./utils/logUtils.ts"; -import { - block, - runWithAmqp, - runWithChainDefinition, - runWithPrisma, -} from "./utils/runUtils.ts"; async function prepareAndMain() { const status = await new Deno.Command("deno", { @@ -60,6 +50,13 @@ async function prepareAndMain() { } async function main() { + const { + block, + runWithAmqp, + runWithChainDefinition, + runWithPrisma, + } = await import("./utils/runUtils.ts"); + setupLog({ handlers: { console: new ConsoleHandler("DEBUG", { @@ -114,17 +111,21 @@ async function main() { await runWithChainDefinition((chain) => ({ runningPromise: runWithPrisma(async (prisma) => { const runningPromise = runWithAmqp(async (amqpConnection) => { - const { cleanup: cleanupObserver } = await observer( - chain, - prisma, - amqpConnection, - ); - const { cleanup: cleanupEmitter } = await emitter( - chain, + const { cleanup: cleanupObserver } = + await (await import("./observer.ts")).observer( + chain, + prisma, + amqpConnection, + ); + const { cleanup: cleanupEmitter } = + await (await import("./emitter.ts")).emitter( + chain, + prisma, + amqpConnection, + ); + const { cleanup: cleanupApi } = await (await import("./api.ts")).api( prisma, - amqpConnection, ); - const { cleanup: cleanupApi } = await api(prisma); return { runningPromise: block(), cleanup: async () => { @@ -135,7 +136,8 @@ async function main() { }; }); const { cleanup: cleanupTestWebhookReceiver } = - await testWebhookReceiver(); + await (await import("./devTools/testWebhookReceiver.ts")) + .testWebhookReceiver(); return { runningPromise, From d9f08859c6e5fbdf870159c65f593070f5d612ed Mon Sep 17 00:00:00 2001 From: "Seo Myunggyun (Jonathan)" Date: Fri, 18 Aug 2023 10:24:24 +0000 Subject: [PATCH 15/15] Remove web/README.md --- web/README.md | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 web/README.md diff --git a/web/README.md b/web/README.md deleted file mode 100644 index ec0e33e..0000000 --- a/web/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Fresh project - -Your new Fresh project is ready to go. You can follow the Fresh "Getting -Started" guide here: https://fresh.deno.dev/docs/getting-started - -### Usage - -Make sure to install Deno: https://deno.land/manual/getting_started/installation - -Then start the project: - -``` -deno task start -``` - -This will watch the project directory and restart as necessary.