fix(ci): resolve TypeScript typecheck OOM and add workspace typecheck step

- Scope root tsconfig to exclude workspace packages and restore declaration resolution

- Align MCP package zod dependency to ^4.6.5 and update record schemas to explicit key/value syntax

- Upgrade ai dependency to ^7.0.118 to restore bundled dist/index.d.ts declarations

- Fix rate limiter inFlightLease promise initialization in TS strict mode

- Add typecheck:workspaces script and wire into CI workflow
This commit is contained in:
coo1estguy
2026-09-28 13:11:07 +05:30
parent 6b35029577
commit 516ea88bb1
7 changed files with 47 additions and 47 deletions
+3 -3
View File
@@ -35,12 +35,12 @@ jobs:
- name: Typecheck
run: npm run typecheck
- name: Typecheck workspace packages
run: npm run typecheck:workspaces
- name: Build
run: npm run build
- name: Typecheck workspace packages
run: npm run typecheck --workspaces --if-present
- name: Lint
# Not `npm run lint` — that script carries --fix, which would paper over
# violations instead of reporting them.
+9 -18
View File
@@ -13,7 +13,7 @@
"packages/*"
],
"dependencies": {
"ai": "^7.0.116",
"ai": "^7.0.118",
"dotenv": "^18.0.4",
"drizzle-orm": "^0.45.3",
"drizzle-zod": "^0.8.3",
@@ -55,9 +55,9 @@
}
},
"node_modules/@ai-sdk/gateway": {
"version": "4.0.94",
"resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.94.tgz",
"integrity": "sha512-hcp7GLnMynH6NfMnemgvW9acn7j3WsUe/dHFzX3/fhPit08xhm1aYmDGHMiIT6YwsXVBxkit9TriKb2SGAOg+Q==",
"version": "4.0.96",
"resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-4.0.96.tgz",
"integrity": "sha512-1aniRTzEf9T7uRHdKnBQ36WgEVQswK6wDgkPVsNMl+sPy7otcmrBa2HEurdFBS51s1q3/UXlh8nNcguwU79Hkg==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/provider": "4.0.18",
@@ -3582,12 +3582,12 @@
}
},
"node_modules/ai": {
"version": "7.0.116",
"resolved": "https://registry.npmjs.org/ai/-/ai-7.0.116.tgz",
"integrity": "sha512-gmkVGPzTNPcJixBiG9zUvP3gmvwko85dHiKrxFKK0zcVJIIu1FP6YDo3jna+ZrC2twMAsUPLr5htCwZjCINH3Q==",
"version": "7.0.118",
"resolved": "https://registry.npmjs.org/ai/-/ai-7.0.118.tgz",
"integrity": "sha512-ZjkbbOffH09yQAjeZ5yGXd8ePTQlFIyKH5aW0s0LOMmvaR1TSoeg5wVURwLvFry/IzzaLndmw9glTKwWRoog3Q==",
"license": "Apache-2.0",
"dependencies": {
"@ai-sdk/gateway": "4.0.94",
"@ai-sdk/gateway": "4.0.96",
"@ai-sdk/provider": "4.0.18",
"@ai-sdk/provider-utils": "5.0.49"
},
@@ -12864,7 +12864,7 @@
"dependencies": {
"@cfworker/json-schema": "^4.1.1",
"@modelcontextprotocol/sdk": "^1.30.0",
"zod": "^3.25.76"
"zod": "^4.6.5"
},
"bin": {
"notifkit-mcp": "dist/index.js"
@@ -12877,15 +12877,6 @@
"node": ">=22.0.0"
}
},
"packages/mcp/node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"packages/provider-console": {
"name": "@notifkit/provider-console",
"version": "0.1.1",
+3 -2
View File
@@ -68,11 +68,12 @@
"test:watch": "vitest",
"lint": "eslint . --fix",
"typecheck": "tsc --noEmit",
"typecheck:workspaces": "npm run typecheck --workspaces --if-present",
"prepare": "husky",
"prepublishOnly": "npm run typecheck && npm run build && npm run build:dashboard"
"prepublishOnly": "npm run typecheck && npm run typecheck:workspaces && npm run build && npm run build:dashboard"
},
"dependencies": {
"ai": "^7.0.116",
"ai": "^7.0.118",
"dotenv": "^18.0.4",
"drizzle-orm": "^0.45.3",
"drizzle-zod": "^0.8.3",
+1 -1
View File
@@ -22,7 +22,7 @@
"dependencies": {
"@cfworker/json-schema": "^4.1.1",
"@modelcontextprotocol/sdk": "^1.30.0",
"zod": "^3.25.76"
"zod": "^4.6.5"
},
"devDependencies": {
"@types/node": "^22.0.0",
+20 -17
View File
@@ -178,7 +178,7 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
"Send to every user opted in to this topic. Mutually exclusive with user and segment.",
),
data: z
.record(z.unknown())
.record(z.string(), z.unknown())
.optional()
.describe("Variables interpolated into the template's {{placeholders}}."),
channels: z
@@ -271,7 +271,7 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
"Recipient email addresses (alias for recipients when sending on email channel).",
),
data: z
.record(z.unknown())
.record(z.string(), z.unknown())
.optional()
.describe("Variables interpolated into the template, shared by every recipient."),
priority: z
@@ -682,10 +682,10 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
.optional()
.describe("Topic(s) this template belongs to, used for per-user topic opt-outs."),
content: z
.record(z.unknown())
.record(z.string(), z.unknown())
.describe("Channel-specific fields, e.g. { subject, body } for email."),
aiPrompts: z
.record(z.string())
.record(z.string(), z.string())
.optional()
.describe("Optional per-field LLM prompts applied before rendering."),
},
@@ -777,13 +777,13 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
inputSchema: {
id: z.string().optional().describe("Template id to fetch and preview."),
content: z
.record(z.unknown())
.record(z.string(), z.unknown())
.optional()
.describe(
"Raw template content object (e.g. { subject, body }) to render without saving.",
),
data: z
.record(z.unknown())
.record(z.string(), z.unknown())
.optional()
.describe("Sample variable data to interpolate into the template's placeholders."),
channel: z
@@ -808,11 +808,11 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
inputSchema: {
id: z.string().optional().describe("Template id to fetch and render."),
content: z
.record(z.unknown())
.record(z.string(), z.unknown())
.optional()
.describe("Raw template content object to render."),
data: z
.record(z.unknown())
.record(z.string(), z.unknown())
.optional()
.describe("Variables to interpolate into placeholders."),
channel: z
@@ -934,10 +934,10 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
preferences: z
.object({
channels: z
.record(z.boolean())
.record(z.string(), z.boolean())
.optional()
.describe("Per-channel opt-in map, e.g. { email: true, sms: false }."),
topics: z.record(z.boolean()).optional().describe("Per-topic opt-in map."),
topics: z.record(z.string(), z.boolean()).optional().describe("Per-topic opt-in map."),
quietHours: z
.array(
z.object({
@@ -1005,10 +1005,10 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
preferences: z
.object({
channels: z
.record(z.boolean())
.record(z.string(), z.boolean())
.optional()
.describe("Per-channel opt-in map, e.g. { email: true, sms: false }."),
topics: z.record(z.boolean()).optional().describe("Per-topic opt-in map."),
topics: z.record(z.string(), z.boolean()).optional().describe("Per-topic opt-in map."),
quietHours: z
.array(
z.object({
@@ -1130,10 +1130,10 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
inputSchema: {
userId: z.string().min(1).describe("User id."),
channels: z
.record(z.boolean())
.record(z.string(), z.boolean())
.optional()
.describe("Per-channel opt-in map, e.g. { email: true, sms: false }."),
topics: z.record(z.boolean()).optional().describe("Per-topic opt-in map."),
topics: z.record(z.string(), z.boolean()).optional().describe("Per-topic opt-in map."),
quietHours: z
.array(
z.object({
@@ -1172,7 +1172,7 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
z.object({
action: z.literal("notify"),
payload: z
.record(z.unknown())
.record(z.string(), z.unknown())
.describe(
"Same shape as send_notification's arguments. Omit the target to use the instance's own user.",
),
@@ -1239,7 +1239,10 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
.min(1)
.optional()
.describe("User id this run is for. Notify steps default to them."),
input: z.record(z.unknown()).optional().describe("Data made available to the run's steps."),
input: z
.record(z.string(), z.unknown())
.optional()
.describe("Data made available to the run's steps."),
},
annotations: { title: "Trigger a workflow", readOnlyHint: false, idempotentHint: false },
},
@@ -1294,7 +1297,7 @@ export function registerTools(server: McpServer, api: NotifkitApi): void {
inputSchema: {
name: z.string().min(1).describe("Event name, matching a workflow's waitForEvent step."),
properties: z
.record(z.unknown())
.record(z.string(), z.unknown())
.describe("Event payload. Include the user id the event is about."),
},
annotations: { title: "Ingest an event", readOnlyHint: false, idempotentHint: false },
+6 -4
View File
@@ -336,8 +336,8 @@ export class ApiRateLimiter {
const currentKey = `${rlTag}:${bucketIndex}`;
const prevKey = `${rlTag}:${prevBucket}`;
const requested = state.batchSize;
const promise = (async () => {
let inFlightRef: Promise<number> | null = null;
const executeLease = async (): Promise<number> => {
try {
let granted: number;
if (typeof this.redis.leaseApiRateLimit === "function") {
@@ -382,12 +382,14 @@ export class ApiRateLimiter {
}
return requested;
} finally {
if (state.inFlightLease === promise) {
if (state.inFlightLease === inFlightRef) {
state.inFlightLease = null;
}
}
})();
};
const promise = executeLease();
inFlightRef = promise;
state.inFlightLease = promise;
return promise;
}
+5 -2
View File
@@ -4,6 +4,10 @@
"moduleResolution": "Bundler",
"outDir": "./dist",
"baseUrl": ".",
"declaration": false,
"declarationMap": false,
"sourceMap": false,
"inlineSources": false,
"paths": {
"@/*": ["./src/*"],
"notifkit": ["./src/index.ts"]
@@ -12,11 +16,10 @@
"include": [
"src/**/*",
"tests/**/*",
"packages/*/src/**/*",
"vitest.config.ts",
"tsdown.config.ts",
"tsup.config.ts",
"drizzle.config.ts"
],
"exclude": ["node_modules", "dist", "coverage", "packages/*/dist", "**/*.d.ts"]
"exclude": ["node_modules", "dist", "coverage", "packages/*/dist"]
}