diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl
index 9b15430..b5e5edd 100644
--- a/.beads/issues.jsonl
+++ b/.beads/issues.jsonl
@@ -24,6 +24,7 @@
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
+{"_type":"issue","id":"islandflow-wvz","title":"Add repository typecheck command","description":"The repository has TypeScript tsconfig files across apps, services, and packages, but no root command that runs typechecking consistently. Add a Bun-first typecheck entry point and validate it.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-29T06:11:57Z","created_by":"dirtydishes","updated_at":"2026-05-29T06:19:09Z","started_at":"2026-05-29T06:12:02Z","closed_at":"2026-05-29T06:19:09Z","close_reason":"Added and validated a repository-wide Bun typecheck command.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-ddm","title":"Redesign home as command deck","description":"Implement the mock1-inspired production command deck on / while preserving focused /options and /news workspaces plus existing legacy redirects. Scope includes apps/web terminal layout, production command-deck CSS, validation, turn documentation, and Forgejo publish.","notes":"Scope: redesign / as a mock1-inspired production command deck using live useTerminal state and existing panes; preserve /options, /news, /mock1, and current legacy redirects. Leave unrelated apps/web/next-env.d.ts and piolium/ changes untouched.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-28T08:59:14Z","created_by":"dirtydishes","updated_at":"2026-05-28T09:09:43Z","started_at":"2026-05-28T08:59:29Z","closed_at":"2026-05-28T09:09:43Z","close_reason":"Implemented / as a mock1-inspired production command deck using live terminal state, preserved focused /options and /news routes plus legacy redirects, validated tests/build/screenshots, and documented the turn.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-4xb","title":"Create dashboard structure mock routes","description":"Prototype four alternate islandflow dashboard structures at /mock1 through /mock4 based on the supplied reference so the main dashboard direction can be evaluated live.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-28T08:30:33Z","created_by":"dirtydishes","updated_at":"2026-05-28T08:38:35Z","started_at":"2026-05-28T08:30:39Z","closed_at":"2026-05-28T08:38:35Z","close_reason":"Added four dashboard mock routes, documented the implementation, and validated build/tests plus route responses.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-1gq","title":"Set up Forgejo-native CI baseline","description":"Create a Forgejo-native CI workflow under .forgejo/workflows that runs the existing fast, high-signal validation checks on pull requests, pushes to main, and manual dispatch. Document the runner label expectations, scope of the job, and manual rerun path in repository docs. Keep heavier container/integration work out of the initial PR gate.","status":"closed","priority":2,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-24T00:31:55Z","created_by":"dirtydishes","updated_at":"2026-05-24T00:36:03Z","closed_at":"2026-05-24T00:36:03Z","close_reason":"Implemented a Forgejo-native CI baseline under .forgejo/workflows, documented runner expectations in the README, and synced the docker workspace snapshot so the fast validate path passes.","dependency_count":0,"dependent_count":0,"comment_count":0}
diff --git a/apps/web/app/api/admin/synthetic/routes.test.ts b/apps/web/app/api/admin/synthetic/routes.test.ts
index 0372d90..eec575d 100644
--- a/apps/web/app/api/admin/synthetic/routes.test.ts
+++ b/apps/web/app/api/admin/synthetic/routes.test.ts
@@ -40,7 +40,7 @@ describe("synthetic admin proxy helpers", () => {
}
});
});
- globalThis.fetch = fetchMock as typeof fetch;
+ globalThis.fetch = fetchMock as unknown as typeof fetch;
const route = await import("./status/route");
const response = await route.GET();
diff --git a/apps/web/app/terminal.test.ts b/apps/web/app/terminal.test.ts
index eb666c4..e6ed106 100644
--- a/apps/web/app/terminal.test.ts
+++ b/apps/web/app/terminal.test.ts
@@ -245,7 +245,7 @@ describe("live manifest", () => {
const filters = {
...buildDefaultFlowFilters(),
minNotional: 500_000,
- optionTypes: ["put"] as const
+ optionTypes: ["put" as const]
};
const manifest = getLiveManifest(
"/options",
@@ -366,7 +366,7 @@ describe("contract-focused option helpers", () => {
const filters = {
...buildDefaultFlowFilters(),
minNotional: 500_000,
- optionTypes: ["put"] as const
+ optionTypes: ["put" as const]
};
expect(
diff --git a/bun.lock b/bun.lock
index db93a84..59bbee4 100644
--- a/bun.lock
+++ b/bun.lock
@@ -8,6 +8,9 @@
"@pierre/diffs": "^1.2.2",
},
"devDependencies": {
+ "@types/bun": "^1.3.3",
+ "@types/ws": "^8.18.1",
+ "typescript": "^5.9.3",
"typescript-language-server": "^5.1.3",
},
},
@@ -426,6 +429,8 @@
"@tootallnate/once": ["@tootallnate/once@2.0.1", "", {}, "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ=="],
+ "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="],
+
"@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="],
"@types/eslint": ["@types/eslint@9.6.1", "", { "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag=="],
@@ -458,6 +463,8 @@
"@types/wrap-ansi": ["@types/wrap-ansi@3.0.0", "", {}, "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g=="],
+ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
+
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
@@ -552,6 +559,8 @@
"buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
+ "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
+
"cacache": ["cacache@16.1.3", "", { "dependencies": { "@npmcli/fs": "^2.1.0", "@npmcli/move-file": "^2.0.0", "chownr": "^2.0.0", "fs-minipass": "^2.1.0", "glob": "^8.0.1", "infer-owner": "^1.0.4", "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^9.0.0", "tar": "^6.1.11", "unique-filename": "^2.0.0" } }, "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ=="],
"cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="],
diff --git a/docs/turns/2026-05-29-add-typecheck-command.html b/docs/turns/2026-05-29-add-typecheck-command.html
new file mode 100644
index 0000000..938f026
--- /dev/null
+++ b/docs/turns/2026-05-29-add-typecheck-command.html
@@ -0,0 +1,260 @@
+
+
+
+
+
+ Add repository typecheck command
+
+
+
+
+
+ Turn document
+ Add repository typecheck command
+
+ Added a root bun run typecheck command that scans the monorepo workspaces and runs
+ TypeScript checks for every workspace with a tsconfig.json. The command now passes across apps,
+ packages, and services.
+
+
+ Created: 2026-05-29 02:18 EDT
+ Beads: islandflow-wvz
+ Validation: typecheck and test suite passed
+
+
+
+
+ Summary
+
+ The repository now has a first-class typecheck gate. Running bun run typecheck checks every
+ workspace TypeScript project under apps, services, and packages, reports
+ failures per workspace, and exits non-zero if any project fails.
+
+
+
+
+ Changes Made
+
+ - Added
scripts/typecheck.ts, a Bun runner that discovers workspace tsconfig.json files.
+ - Added the root
typecheck package script.
+ - Added root development dependencies for
typescript, @types/bun, and @types/ws.
+ - Updated workspace
tsconfig.json files to include Bun runtime types instead of stripping all globals.
+ - Fixed type errors exposed by the new gate in tests, JetStream config, storage JSON decoding, API live fanout, and WebSocket payload decoding.
+
+
+
+
+ Context
+
+ Before this change, the desktop app had a local typecheck script, but the repository did not have a single
+ command for checking the whole Bun and TypeScript monorepo. The first run surfaced both configuration issues
+ and real type mismatches that were not visible from existing validation commands.
+
+
+
+
+ Important Implementation Details
+
+ The typecheck runner intentionally discovers workspace projects from the existing folder structure rather than
+ maintaining a hard-coded list. It passes --incremental false so checking the Next.js workspace does
+ not leave tracked tsconfig.tsbuildinfo churn behind.
+
+
+ Workspace configs now use "types": ["bun"]. This matches the runtime and test environment used by
+ the repo while preserving explicit control over global types.
+
+
+
+
+ Relevant Diff Snippets
+
+ Attempted to use @pierre/diffs as requested by the repository instructions, but the installed
+ package exposes library exports and no executable CLI. The snippets below are therefore the documented plain
+ diff fallback.
+
+ diff --git a/package.json b/package.json
+@@
+ "deploy:current-branch": "./deploy current-branch",
++ "typecheck": "bun run scripts/typecheck.ts",
+@@
+ "devDependencies": {
++ "@types/bun": "^1.3.3",
++ "@types/ws": "^8.18.1",
++ "typescript": "^5.9.3",
+ "typescript-language-server": "^5.1.3"
+ }
+ diff --git a/scripts/typecheck.ts b/scripts/typecheck.ts
++const workspaceRoots = ["apps", "services", "packages"];
++const tsconfigs = workspaceRoots.flatMap((root) => findTsconfigs(root)).sort();
++
++for (const tsconfig of tsconfigs) {
++ const result = Bun.spawnSync([
++ "bunx",
++ "tsc",
++ "-p",
++ tsconfig,
++ "--noEmit",
++ "--incremental",
++ "false",
++ "--pretty",
++ "false"
++ ]);
++}
+ diff --git a/packages/bus/src/jetstream.ts b/packages/bus/src/jetstream.ts
+@@
+- retention: "limits",
+- storage: "file",
+- discard: "old",
++ retention: RetentionPolicy.Limits,
++ storage: StorageType.File,
++ discard: DiscardPolicy.Old,
+ diff --git a/packages/bus/tsconfig.json b/packages/bus/tsconfig.json
+@@
+- "types": []
++ "types": ["bun"]
+
+
+
+ Expected Impact for End-Users
+
+ Developers now have one obvious command to validate TypeScript correctness before handoff or deployment:
+ bun run typecheck. This should catch drift across shared packages and services earlier, especially
+ when changes cross workspace boundaries.
+
+
+
+
+ Validation
+
+ bun run typecheck passed across all discovered workspace tsconfig.json files.
+ bun test passed: 250 tests, 0 failures, 994 assertions.
+ - Confirmed the typecheck script no longer modifies
apps/web/tsconfig.tsbuildinfo.
+
+
+
+
+ Issues, Limitations, and Mitigations
+
+ The command checks workspace TypeScript projects that already have a tsconfig.json. If a new
+ workspace is added without a config file, it will not be checked until that config exists. The runner prints
+ each checked config path to make coverage visible during validation.
+
+
+
+
+ Follow-up Work
+
+ No required follow-up remains for this task. A useful future improvement would be adding the new typecheck
+ command to CI once the Forgejo pipeline is ready for a broader quality gate.
+
+
+
+
+
diff --git a/package.json b/package.json
index b83476b..d2482d0 100644
--- a/package.json
+++ b/package.json
@@ -20,11 +20,15 @@
"deploy": "bun run scripts/deploy.ts",
"deploy:main": "./deploy main",
"deploy:current-branch": "./deploy current-branch",
+ "typecheck": "bun run scripts/typecheck.ts",
"check:public-api-routes": "bun run scripts/check-public-api-routes.ts",
"sync:docker-workspace": "bun run scripts/sync-docker-workspace.ts",
"check:docker-workspace": "bun run scripts/check-docker-workspace.ts"
},
"devDependencies": {
+ "@types/bun": "^1.3.3",
+ "@types/ws": "^8.18.1",
+ "typescript": "^5.9.3",
"typescript-language-server": "^5.1.3"
},
"overrides": {
diff --git a/packages/bus/src/jetstream.ts b/packages/bus/src/jetstream.ts
index 04bfa85..b14ea01 100644
--- a/packages/bus/src/jetstream.ts
+++ b/packages/bus/src/jetstream.ts
@@ -1,10 +1,13 @@
import {
connect,
consumerOpts,
+ DiscardPolicy,
type ConsumerOptsBuilder,
type JetStreamClient,
type JetStreamManager,
type NatsConnection,
+ RetentionPolicy,
+ StorageType,
type StreamConfig,
type StreamUpdateConfig,
JSONCodec,
@@ -182,17 +185,18 @@ export const buildStreamConfig = (
subject: string,
streamClass: StreamRetentionClass,
env: Record = process.env
-): StreamConfig => ({
- name,
- subjects: [subject],
- retention: "limits",
- storage: "file",
- discard: "old",
- max_msgs_per_subject: -1,
- max_msgs: -1,
- ...resolveStreamRetention(streamClass, env),
- num_replicas: 1
-});
+): StreamConfig =>
+ ({
+ name,
+ subjects: [subject],
+ retention: RetentionPolicy.Limits,
+ storage: StorageType.File,
+ discard: DiscardPolicy.Old,
+ max_msgs_per_subject: -1,
+ max_msgs: -1,
+ ...resolveStreamRetention(streamClass, env),
+ num_replicas: 1
+ }) as StreamConfig;
export const buildKnownStreamConfig = (
name: string,
diff --git a/packages/bus/tsconfig.json b/packages/bus/tsconfig.json
index d8c6443..d1df923 100644
--- a/packages/bus/tsconfig.json
+++ b/packages/bus/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/packages/config/tsconfig.json b/packages/config/tsconfig.json
index d8c6443..d1df923 100644
--- a/packages/config/tsconfig.json
+++ b/packages/config/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/packages/observability/tsconfig.json b/packages/observability/tsconfig.json
index d8c6443..d1df923 100644
--- a/packages/observability/tsconfig.json
+++ b/packages/observability/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/packages/storage/src/equity-print-joins.ts b/packages/storage/src/equity-print-joins.ts
index 8d20eec..0a7fe19 100644
--- a/packages/storage/src/equity-print-joins.ts
+++ b/packages/storage/src/equity-print-joins.ts
@@ -14,6 +14,8 @@ export type EquityPrintJoinRecord = {
join_quality_json: string;
};
+type JsonPrimitiveRecord = Record;
+
export const equityPrintJoinsTableDDL = (): string => {
return `
CREATE TABLE IF NOT EXISTS ${EQUITY_PRINT_JOINS_TABLE} (
@@ -46,11 +48,11 @@ export const toEquityPrintJoinRecord = (join: EquityPrintJoin): EquityPrintJoinR
};
};
-const safeJson = (value: string, fallback: Record): Record => {
+const safeJson = (value: string, fallback: JsonPrimitiveRecord): JsonPrimitiveRecord => {
try {
const parsed = JSON.parse(value);
if (parsed && typeof parsed === "object") {
- return parsed as Record;
+ return parsed as JsonPrimitiveRecord;
}
} catch {
// ignore
diff --git a/packages/storage/src/flow-packets.ts b/packages/storage/src/flow-packets.ts
index 0324663..6ab43d5 100644
--- a/packages/storage/src/flow-packets.ts
+++ b/packages/storage/src/flow-packets.ts
@@ -13,6 +13,8 @@ export type FlowPacketRecord = {
join_quality_json: string;
};
+type JsonPrimitiveRecord = Record;
+
export const flowPacketsTableDDL = (): string => {
return `
CREATE TABLE IF NOT EXISTS ${FLOW_PACKETS_TABLE} (
@@ -43,11 +45,11 @@ export const toFlowPacketRecord = (packet: FlowPacket): FlowPacketRecord => {
};
};
-const safeJson = (value: string, fallback: Record): Record => {
+const safeJson = (value: string, fallback: JsonPrimitiveRecord): JsonPrimitiveRecord => {
try {
const parsed = JSON.parse(value);
if (parsed && typeof parsed === "object") {
- return parsed as Record;
+ return parsed as JsonPrimitiveRecord;
}
} catch {
// ignore
diff --git a/packages/storage/tsconfig.json b/packages/storage/tsconfig.json
index 43ef119..2898c0f 100644
--- a/packages/storage/tsconfig.json
+++ b/packages/storage/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts", "tests/**/*.ts"]
}
diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json
index d8c6443..d1df923 100644
--- a/packages/types/tsconfig.json
+++ b/packages/types/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/scripts/typecheck.ts b/scripts/typecheck.ts
new file mode 100644
index 0000000..9e3ba06
--- /dev/null
+++ b/scripts/typecheck.ts
@@ -0,0 +1,56 @@
+#!/usr/bin/env bun
+
+import { readdirSync, statSync } from "node:fs";
+import { join, relative } from "node:path";
+
+const workspaceRoots = ["apps", "services", "packages"];
+
+const findTsconfigs = (dir: string): string[] => {
+ const entries = readdirSync(dir, { withFileTypes: true });
+ const tsconfigs: string[] = [];
+
+ for (const entry of entries) {
+ if (!entry.isDirectory()) {
+ continue;
+ }
+
+ const workspacePath = join(dir, entry.name);
+ const tsconfigPath = join(workspacePath, "tsconfig.json");
+
+ if (statSync(tsconfigPath, { throwIfNoEntry: false })?.isFile()) {
+ tsconfigs.push(tsconfigPath);
+ }
+ }
+
+ return tsconfigs;
+};
+
+const tsconfigs = workspaceRoots.flatMap((root) => findTsconfigs(root)).sort();
+
+if (tsconfigs.length === 0) {
+ console.log("No workspace tsconfig.json files found.");
+ process.exit(0);
+}
+
+let failed = false;
+
+for (const tsconfig of tsconfigs) {
+ const label = relative(process.cwd(), tsconfig);
+ console.log(`\nTypechecking ${label}`);
+
+ const result = Bun.spawnSync(["bunx", "tsc", "-p", tsconfig, "--noEmit", "--incremental", "false", "--pretty", "false"], {
+ stdout: "inherit",
+ stderr: "inherit"
+ });
+
+ if (result.exitCode !== 0) {
+ failed = true;
+ }
+}
+
+if (failed) {
+ console.error("\nTypecheck failed.");
+ process.exit(1);
+}
+
+console.log("\nTypecheck passed.");
diff --git a/services/api/src/index.ts b/services/api/src/index.ts
index 562fb6b..ffcd560 100644
--- a/services/api/src/index.ts
+++ b/services/api/src/index.ts
@@ -59,7 +59,6 @@ import {
fetchSmartMoneyEventsBefore,
fetchFlowPacketsAfter,
fetchFlowPacketById,
- fetchAlertContextByTraceId,
fetchFlowPacketsByMemberTraceIds,
fetchFlowPacketsBefore,
fetchRecentAlerts,
@@ -108,6 +107,7 @@ import {
InferredDarkEventSchema,
NewsStorySchema,
LiveClientMessageSchema,
+ type LiveChannel,
LiveServerMessage,
LiveSubscription,
LiveSubscriptionSchema,
@@ -118,6 +118,7 @@ import {
SmartMoneyEventSchema,
OptionNBBOSchema,
OptionPrintSchema,
+ type OptionPrint,
getSubscriptionKey
} from "@islandflow/types";
import { createClient } from "redis";
@@ -598,11 +599,8 @@ const parseLiveEquityPrintFilters = (url: URL): EquityPrintQueryFilters => ({
const matchesScopedOptionSubscription = (
print: { underlying_id?: string; option_contract_id: string },
- subscription: LiveSubscription
+ subscription: Extract
): boolean => {
- if (subscription.channel !== "options") {
- return false;
- }
if (subscription.option_contract_id && subscription.option_contract_id !== print.option_contract_id) {
return false;
}
@@ -1016,7 +1014,7 @@ const run = async () => {
const fanoutLive = async (
subscription: LiveSubscription,
item: unknown,
- ingestChannel: "options" | "nbbo" | "equities" | "equity-quotes" | "equity-candles" | "equity-overlay" | "equity-joins" | "flow" | "classifier-hits" | "alerts" | "inferred-dark" | "news"
+ ingestChannel: LiveChannel
) => {
const watermark = await liveState.ingest(ingestChannel, item);
@@ -1033,7 +1031,7 @@ const run = async () => {
return;
}
- const optionItem = ingestChannel === "options" ? (item as Parameters[0]) : null;
+ const optionItem = ingestChannel === "options" ? (item as OptionPrint) : null;
const equityItem = ingestChannel === "equities" ? (item as Parameters[0]) : null;
const flowItem = ingestChannel === "flow" ? (item as Parameters[0]) : null;
let matchedSubscriptions = 0;
diff --git a/services/api/src/live.ts b/services/api/src/live.ts
index c8d2886..40bbd20 100644
--- a/services/api/src/live.ts
+++ b/services/api/src/live.ts
@@ -489,7 +489,7 @@ const matchesScopedOptionSnapshot = (
}
const allowed = new Set(subscription.underlying_ids.map((value) => value.toUpperCase()));
- return allowed.has(item.underlying_id.toUpperCase());
+ return item.underlying_id ? allowed.has(item.underlying_id.toUpperCase()) : false;
};
const matchesScopedEquitySnapshot = (
diff --git a/services/api/tsconfig.json b/services/api/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/api/tsconfig.json
+++ b/services/api/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/services/candles/tsconfig.json b/services/candles/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/candles/tsconfig.json
+++ b/services/candles/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/services/compute/tsconfig.json b/services/compute/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/compute/tsconfig.json
+++ b/services/compute/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/services/eod-enricher/tsconfig.json b/services/eod-enricher/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/eod-enricher/tsconfig.json
+++ b/services/eod-enricher/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/services/ingest-equities/src/adapters/alpaca.ts b/services/ingest-equities/src/adapters/alpaca.ts
index 7a1447f..b7fa871 100644
--- a/services/ingest-equities/src/adapters/alpaca.ts
+++ b/services/ingest-equities/src/adapters/alpaca.ts
@@ -88,7 +88,7 @@ const decodePayload = (data: WebSocket.RawData): unknown => {
return JSON.parse(new TextDecoder().decode(new Uint8Array(data.buffer, data.byteOffset, data.byteLength))) as unknown;
}
- return JSON.parse(new TextDecoder().decode(new Uint8Array(data as ArrayBuffer))) as unknown;
+ return JSON.parse(new TextDecoder().decode(new Uint8Array(data as unknown as ArrayBuffer))) as unknown;
};
const extractExchangeMeta = (payload: unknown): AlpacaExchangeMetaEntry[] => {
diff --git a/services/ingest-equities/tsconfig.json b/services/ingest-equities/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/ingest-equities/tsconfig.json
+++ b/services/ingest-equities/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/services/ingest-news/src/index.ts b/services/ingest-news/src/index.ts
index 95cca42..421eaf3 100644
--- a/services/ingest-news/src/index.ts
+++ b/services/ingest-news/src/index.ts
@@ -128,7 +128,7 @@ const decodePayload = (data: WebSocket.RawData): unknown => {
if (ArrayBuffer.isView(data)) {
return JSON.parse(new TextDecoder().decode(new Uint8Array(data.buffer, data.byteOffset, data.byteLength))) as unknown;
}
- return JSON.parse(new TextDecoder().decode(new Uint8Array(data as ArrayBuffer))) as unknown;
+ return JSON.parse(new TextDecoder().decode(new Uint8Array(data as unknown as ArrayBuffer))) as unknown;
};
const run = async () => {
diff --git a/services/ingest-news/tsconfig.json b/services/ingest-news/tsconfig.json
index 43ef119..2898c0f 100644
--- a/services/ingest-news/tsconfig.json
+++ b/services/ingest-news/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts", "tests/**/*.ts"]
}
diff --git a/services/ingest-options/src/adapters/alpaca.ts b/services/ingest-options/src/adapters/alpaca.ts
index 00645b8..9ea844d 100644
--- a/services/ingest-options/src/adapters/alpaca.ts
+++ b/services/ingest-options/src/adapters/alpaca.ts
@@ -380,7 +380,7 @@ const decodePayload = (data: WebSocket.RawData): unknown => {
return decode(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
}
- return decode(new Uint8Array(data as ArrayBuffer));
+ return decode(new Uint8Array(data as unknown as ArrayBuffer));
};
const parseTimestamp = (value: string): number => {
diff --git a/services/ingest-options/src/index.ts b/services/ingest-options/src/index.ts
index 301632e..f416121 100644
--- a/services/ingest-options/src/index.ts
+++ b/services/ingest-options/src/index.ts
@@ -157,7 +157,7 @@ const nbboHistoryByContract: ContextHistory = new Map();
const equityQuoteHistoryByUnderlying: ContextHistory = new Map();
const OPTION_CONTEXT_PRUNE_INTERVAL_MS = 60_000;
-const pruneContextHistory = (
+const pruneContextHistory = (
history: ContextHistory,
maxKeys: number,
ttlMs: number,
diff --git a/services/ingest-options/tsconfig.json b/services/ingest-options/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/ingest-options/tsconfig.json
+++ b/services/ingest-options/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/services/refdata/tsconfig.json b/services/refdata/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/refdata/tsconfig.json
+++ b/services/refdata/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}
diff --git a/services/replay/tsconfig.json b/services/replay/tsconfig.json
index d8c6443..d1df923 100644
--- a/services/replay/tsconfig.json
+++ b/services/replay/tsconfig.json
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
- "types": []
+ "types": ["bun"]
},
"include": ["src/**/*.ts"]
}