diff --git a/.vscode/launch.json b/.vscode/launch.json index 7a3b6f6b10..d7dc5d07de 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -109,6 +109,34 @@ }, "killBehavior": "polite", }, + { + "type": "node", + "request": "launch", + "name": "Launch: mocknet offline-mode", + "skipFiles": [ + "/**" + ], + "runtimeArgs": [ + "-r", + "ts-node/register/transpile-only", + "-r", + "tsconfig-paths/register" + ], + "args": [ + "${workspaceFolder}/src/index.ts" + ], + "outputCapture": "std", + "internalConsoleOptions": "openOnSessionStart", + "preLaunchTask": "stacks-node:start-mocknet", + "postDebugTask": "stacks-node:stop-mocknet", + "env": { + "STACKS_CHAIN_ID": "0x80000000", + "NODE_ENV": "development", + "STACKS_API_MODE": "offline", + "TS_NODE_SKIP_IGNORE": "true" + }, + "killBehavior": "polite", + }, { "type": "node", "request": "launch", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b044e7fc48..60d1120a81 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -22,6 +22,28 @@ }, "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "dedicated", "clear": false } }, + { + "label": "stacks-node:start-mocknet", + "type": "shell", + "command": "docker compose -f docker/docker-compose.dev.stacks-blockchain.yml up --force-recreate -V", + "isBackground": true, + "problemMatcher": { + "pattern": { "regexp": ".", "file": 1, "location": 2, "message": 3, }, + "background": { "activeOnStart": true, "beginsPattern": ".", "endsPattern": "." } + }, + "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "dedicated", "clear": false } + }, + { + "label": "stacks-node:stop-mocknet", + "type": "shell", + "command": "docker compose -f docker/docker-compose.dev.stacks-blockchain.yml down -v -t 0", + "isBackground": true, + "problemMatcher": { + "pattern": { "regexp": ".", "file": 1, "location": 2, "message": 3, }, + "background": { "activeOnStart": true, "beginsPattern": ".", "endsPattern": "." } + }, + "presentation": { "echo": true, "reveal": "always", "focus": false, "panel": "dedicated", "clear": false } + }, { "label": "deploy:krypton", "type": "shell", diff --git a/src/datastore/offline-dummy-store.ts b/src/datastore/offline-dummy-store.ts index c39a156d6c..a1d8d54ead 100644 --- a/src/datastore/offline-dummy-store.ts +++ b/src/datastore/offline-dummy-store.ts @@ -1,17 +1,20 @@ import { EventEmitter } from 'events'; import { PgStoreEventEmitter } from './pg-store-event-emitter'; -import { PgStore } from './pg-store'; +import { PgWriteStore } from './pg-write-store'; -export const OfflineDummyStore: PgStore = new Proxy(new EventEmitter() as PgStoreEventEmitter, { - get(target: any, propKey) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - if (propKey === 'eventEmitter') return target; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - if (propKey in target) return target[propKey]; - return function () { - throw new Error( - `Cannot call function on the Dummy datastore. Check if the application is running in offline mode.` - ); - }; - }, -}); +export const OfflineDummyStore: PgWriteStore = new Proxy( + new EventEmitter() as PgStoreEventEmitter, + { + get(target: any, propKey) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + if (propKey === 'eventEmitter') return target; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + if (propKey in target) return target[propKey]; + return function () { + throw new Error( + `Cannot call function on the Dummy datastore. Check if the application is running in offline mode.` + ); + }; + }, + } +); diff --git a/src/index.ts b/src/index.ts index f153a24edc..9727cc6cdf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -111,18 +111,21 @@ async function init(): Promise { ); } const apiMode = getApiMode(); - const dbStore = - apiMode === StacksApiMode.offline - ? OfflineDummyStore - : await PgStore.connect({ - usageName: `datastore-${apiMode}`, - }); - const dbWriteStore = await PgWriteStore.connect({ - usageName: `write-datastore-${apiMode}`, - skipMigrations: apiMode === StacksApiMode.readOnly, - }); - - registerMempoolPromStats(dbWriteStore.eventEmitter); + let dbStore: PgStore; + let dbWriteStore: PgWriteStore; + if (apiMode === StacksApiMode.offline) { + dbStore = OfflineDummyStore; + dbWriteStore = OfflineDummyStore; + } else { + dbStore = await PgStore.connect({ + usageName: `datastore-${apiMode}`, + }); + dbWriteStore = await PgWriteStore.connect({ + usageName: `write-datastore-${apiMode}`, + skipMigrations: apiMode === StacksApiMode.readOnly, + }); + registerMempoolPromStats(dbWriteStore.eventEmitter); + } if (apiMode === StacksApiMode.default || apiMode === StacksApiMode.writeOnly) { const configuredChainID = getApiConfiguredChainID(); @@ -168,7 +171,11 @@ async function init(): Promise { } } - if (apiMode === StacksApiMode.default || apiMode === StacksApiMode.readOnly) { + if ( + apiMode === StacksApiMode.default || + apiMode === StacksApiMode.readOnly || + apiMode === StacksApiMode.offline + ) { const apiServer = await startApiServer({ datastore: dbStore, writeDatastore: dbWriteStore, @@ -193,14 +200,16 @@ async function init(): Promise { }); } - registerShutdownConfig({ - name: 'DB', - handler: async () => { - await dbStore.close(); - await dbWriteStore.close(); - }, - forceKillable: false, - }); + if (apiMode !== StacksApiMode.offline) { + registerShutdownConfig({ + name: 'DB', + handler: async () => { + await dbStore.close(); + await dbWriteStore.close(); + }, + forceKillable: false, + }); + } if (isProdEnv) { const prometheusServer = await createPrometheusServer({ port: 9153 });