Skip to content

Comments

feat: async tasker for google calendar events#23494

Closed
ThyMinimalDev wants to merge 7 commits intomainfrom
init-tasker-google-calendar
Closed

feat: async tasker for google calendar events#23494
ThyMinimalDev wants to merge 7 commits intomainfrom
init-tasker-google-calendar

Conversation

@ThyMinimalDev
Copy link
Contributor

What does this PR do?

  • Fixes #XXXX (GitHub issue number)
  • Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings

@ThyMinimalDev ThyMinimalDev requested a review from a team September 1, 2025 13:09
@ThyMinimalDev ThyMinimalDev requested a review from a team as a code owner September 1, 2025 13:09
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 1, 2025

Walkthrough

Adds a tasker framework and Google Calendar taskers across the monorepo: new ICalendarTasker type, a generic BaseTaskerService for safe dispatch with fallback, GoogleCalendarTriggerDevTasker and GoogleCalendarSyncTasker implementations, and a GoogleCalendarTasker aggregator. Introduces a new @calcom/trigger package (tasks, config, re-exports). Exposes taskers from packages/platform/libraries (package version -> 9.9.9 and new ./taskers export) and updates Vite config and TS paths. Integrates taskers into apps/api/v2 (Nest services and GoogleCalendarTaskerModule), adds LoggerAdapter and DI modules/tokens/containers, and adds a workspace dependency to apps/web.

Possibly related PRs

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch init-tasker-google-calendar

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Contributor

github-actions bot commented Sep 1, 2025

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

Unknown release type "feature" found in pull request title "feature: async tasker for google calendar events". 

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@keithwillcode keithwillcode added core area: core, team members only foundation platform Anything related to our platform plan labels Sep 1, 2025
Comment on lines +1 to +12
import { Logger } from "@/lib/logger.bridge";
import { GoogleCalendarSyncTasker } from "@/lib/services/taskers/google-calendar-sync-tasker.service";
import { GoogleCalendarTasker } from "@/lib/services/taskers/google-calendar-tasker.service";
import { GoogleCalendarTriggerDevTasker } from "@/lib/services/taskers/google-calendar-trigger-tasker.service";
import { Module } from "@nestjs/common";

@Module({
imports: [],
providers: [GoogleCalendarSyncTasker, GoogleCalendarTriggerDevTasker, Logger, GoogleCalendarTasker],
exports: [GoogleCalendarTasker],
})
export class GoogleCalendarTaskerModule {}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

module which can be used within api v2 controller to inject google calendar tasker

@dosubot dosubot bot added calendar-apps area: calendar, google calendar, outlook, lark, microsoft 365, apple calendar ✨ feature New feature or request labels Sep 1, 2025
Comment on lines +1 to +19
import { nanoid } from "nanoid";

import type { ITaskerDependencies } from "../types/tasker";
import type { ICalendarTasker } from "./calendar";

export class GoogleCalendarSyncTasker implements ICalendarTasker {
constructor(public readonly dependencies: ITaskerDependencies) {}

async createEvent(payload: Parameters<ICalendarTasker["createEvent"]>[0]) {
this.dependencies.logger.info(`Hello ${payload.name}`);
const runId = `sync_${nanoid(10)}`;
return { runId };
}
async updateEvent(payload: Parameters<ICalendarTasker["updateEvent"]>[0]) {
this.dependencies.logger.info(`Hello ${payload.surname}`);
const runId = `sync_${nanoid(10)}`;
return { runId };
}
}

This comment was marked as outdated.

Comment on lines +15 to +19
export class GoogleCalendarTasker extends BaseTaskerService<ICalendarTasker> {
constructor(public readonly dependencies: ICalendarTaskerDependencies) {
super(dependencies);
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main GoogleCalendarTasker interface, typed using BaseTaskerService to make sure typescript is able to typecheck the dispatch as shown in the comments right below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the class in which we will inject the primary / fallback tasker

Comment on lines +1 to +22
import { create } from "@calcom/trigger/tasks/calendars/create";

import type { ITaskerDependencies } from "../types/tasker";
import type { ICalendarTasker } from "./calendar";

export class GoogleCalendarTriggerDevTasker implements ICalendarTasker {
constructor(public readonly dependencies: ITaskerDependencies) {}

async createEvent(payload: Parameters<ICalendarTasker["createEvent"]>[0]) {
this.dependencies.logger.debug("createEvent", payload);
const handle = await create.trigger({ name: payload.name });
this.dependencies.logger.debug("createEvent handle id", handle.id);
return { runId: handle.id };
}

async updateEvent(payload: Parameters<ICalendarTasker["updateEvent"]>[0]) {
this.dependencies.logger.debug("createEvent", payload);
const handle = await create.trigger({ name: payload.surname });
this.dependencies.logger.debug("createEvent handle id", handle.id);
return { runId: handle.id };
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skeleton for the trigger.dev tasker, in this file we will import the google calendar tasks defined in packages/trigger

Comment on lines +1 to +44
// 1. The interface for the logger's configuration settings.
// This is extracted directly from your class.
export interface IMyLoggerSettings {
minLevel: number;
displayTimestamp: boolean;
logFormat: "pretty" | "json" | "simple";
}

// 2. The main interface for the Logger instance.
// This describes the public "shape" of your Logger class.
export interface ILogger {
/**
* The configuration settings for this logger instance.
*/
readonly settings: IMyLoggerSettings;

/**
* Creates a new Logger instance with a specific prefix for contextual logging.
* @param options - Configuration for the sub-logger.
* @returns A new ILogger instance with the specified prefix.
*/
getSubLogger(options: { prefix?: string[] }): ILogger;

/** Logs an informational message. */
info(...args: any[]): void;

/** Logs a warning message. */
warn(...args: any[]): void;

/** Logs an error message. */
error(...args: any[]): void;

/** Logs a debug message. */
debug(...args: any[]): void;

/** Logs a detailed trace message. */
trace(...args: any[]): void;

/** Logs a critical error that may require immediate attention. */
fatal(...args: any[]): void;

/** Logs a message with the lowest level of detail. */
silly(...args: any[]): void;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interface of a logger than can be dependency injected, so we can DI trigger.dev logger, tslog, or api v2 logger

Comment on lines +1 to +48
// No need for a generic base 'TaskerInterface'. We will use the specific domain interface directly.
// For example, ICalendarTasker.
import type { ILogger } from "./types/logger";

export abstract class BaseTaskerService<T> {
// T is now just a placeholder for the class that extends this
// The dependencies are now typed with the specific interfaces
protected readonly primaryTasker: T;
protected readonly fallbackTasker: T;
protected readonly logger: ILogger;

constructor(dependencies: { primaryTasker: T; fallbackTasker: T; logger: ILogger }) {
this.primaryTasker = dependencies.primaryTasker;
this.fallbackTasker = dependencies.fallbackTasker;
this.logger = dependencies.logger;
}

public async dispatch<K extends keyof T>(
taskName: K,
...args: T[K] extends (...args: any[]) => any ? Parameters<T[K]> : never
): Promise<T[K] extends (...args: any[]) => any ? Awaited<ReturnType<T[K]>> : never> {
this.logger.info(`Dispatching task '${String(taskName)}'`, { args });
return this.safeDispatch(taskName, ...args);
}

// The dispatch method is now strongly typed to the keys of T
protected async safeDispatch<K extends keyof T>(
taskName: K,
// We infer the arguments directly from the method on T
...args: T[K] extends (...args: any[]) => any ? Parameters<T[K]> : never
): Promise<T[K] extends (...args: any[]) => any ? Awaited<ReturnType<T[K]>> : never> {
try {
const method = this.primaryTasker[taskName] as (...args: any[]) => any;
return await method.apply(this.primaryTasker, args);
} catch (err) {
this.logger.error(`Primary tasker failed for '${String(taskName)}'.`, err as Error);

if (this.primaryTasker === this.fallbackTasker) {
throw err;
}

this.logger.warn(`Falling back to SyncTasker for '${String(taskName)}'.`);

const fallbackMethod = this.fallbackTasker[taskName] as (...args: any[]) => any;
return fallbackMethod.apply(this.fallbackTasker, args);
}
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the base tasker class which specialized taskers will extends, this ensures the fallback tasker is called if the primary tasker fails (for example is trigger.dev is down)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also abstract the logic of dispatch and safeDispatch so that people implementing Specialized taskers do not have to worry with it

Comment on lines +1 to +3
export { GoogleCalendarTasker } from "@calcom/features/tasker/calendars/GoogleCalendarTasker";
export { GoogleCalendarSyncTasker } from "@calcom/features/tasker/calendars/GoogleCalendarSyncTasker";
export { GoogleCalendarTriggerDevTasker } from "@calcom/features/tasker/calendars/GoogleCalendarTriggerTasker";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exporting tassker so that apiV2 can build it's own modules with them

Comment on lines +1 to +12
import { schemaTask, logger } from "@trigger.dev/sdk";
import { z } from "zod";

export const create = schemaTask({
id: "calendar.google.create.event",
schema: z.object({
name: z.string(),
}),
run: async (payload: { name: string }) => {
logger.info(`Hello create ${payload.name}!`);
},
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

example trigger.dev task

Copy link
Contributor Author

@ThyMinimalDev ThyMinimalDev Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should aim for something like so

import { schemaTask, logger } from "@trigger.dev/sdk";
import { z } from "zod";
import GoogleCalendarService from '@calcom/app-store/googlecalendar/lib'
export const create = schemaTask({
  id: "calendar.google.create",
  schema: z.object({
    name: z.string(),
  }),
  run: async (payload: { name: string }) => {
       const service =    new GoogleCalendarService(logger)
       service.create(payload)
  },
});```

"scripts": {
"lint": "eslint .",
"type-check": "tsc --noEmit",
"dev:trigger": "trigger dev",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dev trigger, this will start a local worker to tests tasks

"lint": "eslint .",
"type-check": "tsc --noEmit",
"dev:trigger": "trigger dev",
"deploy:trigger": "trigger deploy"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this deploys our tasks to trigger.dev production env, we will need to have github actions for this

Comment on lines +14 to +20
default: {
maxAttempts: 3,
minTimeoutInMs: 1000,
maxTimeoutInMs: 10000,
factor: 2,
randomize: true,
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@keithwillcode let's look into what kind of config we need, this can also be overridenn on a per individual task basis

Comment on lines +24 to +25
primaryTasker: new GoogleCalendarTriggerDevTasker({ logger: logger as unknown as ILogger }),
fallbackTasker: new GoogleCalendarSyncTasker({ logger: logger as unknown as ILogger }),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can handle which tasker is used using env variable, this ensures self-host can either implement their own third party tasker or stay with the synchronous tasker

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/api/v2/src/lib/logger.bridge.ts (1)

35-42: Guard env parsing: invalid LOGGER_BRIDGE_LOG_LEVEL currently disables all logging (NaN comparisons are always false).

Clamp and validate the parsed level.

-  private static readonly defaultSettings: IMyLoggerSettings = {
-    minLevel: process?.env?.LOGGER_BRIDGE_LOG_LEVEL
-      ? Number(process.env.LOGGER_BRIDGE_LOG_LEVEL)
-      : LogLevel.INFO, // Default to INFO level
-    displayTimestamp: true,
-    logFormat: "pretty",
-  };
+  private static readonly defaultSettings: IMyLoggerSettings = {
+    minLevel: (() => {
+      const raw = process?.env?.LOGGER_BRIDGE_LOG_LEVEL;
+      const n = raw !== undefined ? Number(raw) : NaN;
+      return Number.isFinite(n) && n >= LogLevel.DEBUG && n <= LogLevel.ERROR
+        ? n
+        : LogLevel.INFO; // Default to INFO level
+    })(),
+    displayTimestamp: true,
+    logFormat: "pretty",
+  };
🧹 Nitpick comments (33)
packages/trigger/tsconfig.json (1)

3-4: Narrow the include for safer builds.
Including "." may pick up stray files (tests, temp). Consider limiting to source entries used by this package.

-  "include": ["."],
+  "include": ["src", "tasks", "index.ts", "trigger.config.ts"],
packages/trigger/.gitignore (1)

1-1: Ignore typical build artifacts here (if not already covered at repo root).
If the root .gitignore doesn’t catch these, add them to avoid accidental commits.

 .trigger
+dist
+build
+node_modules
+.turbo
+*.tsbuildinfo
packages/trigger/index.ts (1)

1-1: Consider limiting the public surface to a curated export list.
Prevents accidental API expansion when the SDK adds new exports; keeps downstream bundles lean.

packages/trigger/package.json (1)

24-26: Redundant subpath export "./trigger".

It resolves to the same file as "."; drop unless you truly need the alias.

-    ".": "./index.ts",
-    "./trigger": "./index.ts"
+    ".": "./index.ts"
packages/trigger/trigger.config.ts (3)

4-7: Prefer named export over default export.

Aligns with repo guideline to avoid default exports.

-export default defineConfig({
+export const triggerConfig = defineConfig({
 ...
-});
+});
+export default triggerConfig; // if consumers still default-import; remove later

6-6: Project ID should be env-driven for multi-env setups.

Hard-coding ties configs to one Trigger.dev project.

-  project: "proj_klpqoyhxnkrxlceolbfy", // e.g., "proj_abc123"
+  project: process.env.TRIGGER_PROJECT ?? "proj_klpqoyhxnkrxlceolbfy",

Optionally fail fast if missing in non-dev envs.


13-14: Comment contradicts value.

"Enable retries in development" but enabledInDev is false. Fix the comment or the flag.

-    enabledInDev: false, // Enable retries in development
+    enabledInDev: false, // Disable retries in development
packages/features/tasker/types/logger.ts (3)

4-7: Model log level as a union instead of number for type-safety.

-export interface IMyLoggerSettings {
-  minLevel: number;
+export type LogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal" | "silly";
+export interface IMyLoggerSettings {
+  minLevel: LogLevel;
   displayTimestamp: boolean;
   logFormat: "pretty" | "json" | "simple";
 }

24-44: Prefer unknown[] over any[] for variadic args.

Improves type safety without burdening callers.

-  info(...args: any[]): void;
-  warn(...args: any[]): void;
-  error(...args: any[]): void;
-  debug(...args: any[]): void;
-  trace(...args: any[]): void;
-  fatal(...args: any[]): void;
-  silly(...args: any[]): void;
+  info(...args: unknown[]): void;
+  warn(...args: unknown[]): void;
+  error(...args: unknown[]): void;
+  debug(...args: unknown[]): void;
+  trace(...args: unknown[]): void;
+  fatal(...args: unknown[]): void;
+  silly(...args: unknown[]): void;

3-7: Naming nit: drop "My" from IMyLoggerSettings.

Use LoggerSettings for clarity and consistency.

-export interface IMyLoggerSettings {
+export interface LoggerSettings {

Update the ILogger.settings accordingly.

apps/api/v2/src/lib/logger.bridge.ts (6)

60-65: Sub-logger should inherit settings and chain prefix.

Right now it resets minLevel and drops the parent’s prefix/context.

-  getSubLogger(options: { prefix?: string[] }): Logger {
-    const newLogger = new Logger();
-    // Set the prefix for the *new* instance
-    newLogger.prefix = options?.prefix?.join(" ") ?? "";
-    return newLogger;
-  }
+  getSubLogger(options: { prefix?: string[] }): Logger {
+    const newLogger = new Logger(this.settings);
+    const child = options?.prefix?.join(" ") ?? "";
+    newLogger.prefix = [this.prefix, child].filter(Boolean).join(" ").trim();
+    return newLogger;
+  }

69-99: Normalize level gating with a helper to avoid drift.

Centralize the comparison logic and use level constants.

+  private shouldLog(level: number) {
+    const min = this.settings.minLevel;
+    return Number.isFinite(min) && min <= level;
+  }
@@
-  info(...args: any[]) {
-    this.settings.minLevel <= 1 && this.logInternal("log", ...args);
-  }
+  info(...args: any[]) {
+    this.shouldLog(LogLevel.INFO) && this.logInternal("log", ...args);
+  }
@@
-  warn(...args: any[]) {
-    this.settings.minLevel <= 2 && this.logInternal("warn", ...args);
-  }
+  warn(...args: any[]) {
+    this.shouldLog(LogLevel.WARN) && this.logInternal("warn", ...args);
+  }
@@
-  error(...args: any[]) {
-    this.settings.minLevel <= 3 && this.logInternal("error", ...args);
-  }
+  error(...args: any[]) {
+    this.shouldLog(LogLevel.ERROR) && this.logInternal("error", ...args);
+  }
@@
-  debug(...args: any[]) {
-    this.settings.minLevel === 0 && this.logInternal("debug", ...args);
-  }
+  debug(...args: any[]) {
+    this.shouldLog(LogLevel.DEBUG) && this.logInternal("debug", ...args);
+  }
@@
-  trace(...args: any[]) {
-    this.settings.minLevel === 0 && this.logInternal("verbose", ...args);
-  }
+  trace(...args: any[]) {
+    this.shouldLog(LogLevel.DEBUG) && this.logInternal("verbose", ...args);
+  }
@@
-  silly(...args: any[]) {
-    const sillyMessage = `silly: ${this.formatArgsAsString(args)}`;
-    this.settings.minLevel === 0 && this.logInternal("verbose", sillyMessage);
-  }
+  silly(...args: any[]) {
+    const sillyMessage = `silly: ${this.formatArgsAsString(args)}`;
+    this.shouldLog(LogLevel.DEBUG) && this.logInternal("verbose", sillyMessage);
+  }

102-117: Preserve Error details; JSON.stringify(new Error()) yields "{}".

Special-case Error to include name/message/stack.

   private formatArgsAsString(args: any[]): string {
     return args
       .map((arg) => {
+        if (arg instanceof Error) {
+          const stack = arg.stack ? `\n${arg.stack}` : "";
+          return `${arg.name}: ${arg.message}${stack}`;
+        }
         if (typeof arg === "string") {
           return arg;
         }

118-146: Forward stack to NestLogger.error when available.

Detect an Error arg and pass its stack to Nest logger.

-  private logInternal(level: "log" | "warn" | "error" | "debug" | "verbose", ...args: any[]) {
+  private logInternal(level: "log" | "warn" | "error" | "debug" | "verbose", ...args: any[]) {
     try {
-      // Format message from potentially multiple arguments
-      const formattedMessage = this.formatArgsAsString(args);
+      const errObj = args.find((a) => a instanceof Error) as Error | undefined;
+      const printableArgs = errObj ? args.filter((a) => a !== errObj) : args;
+      const formattedMessage = this.formatArgsAsString(printableArgs);
@@
       switch (level) {
         case "log":
           this.nestLogger.log(message);
           break;
         case "warn":
           this.nestLogger.warn(message);
           break;
         case "error":
-          this.nestLogger.error(message);
+          this.nestLogger.error(message, errObj?.stack);
           break;
         case "debug":
           this.nestLogger.debug(message);
           break;
         case "verbose":
           this.nestLogger.verbose(message);
           break;
       }
     } catch (err) {
-      this.nestLogger.error(
-        `Could not bridge log message. Error: ${err instanceof Error ? err.message : String(err)}`
-      );
+      const msg = `Could not bridge log message. Error: ${err instanceof Error ? err.message : String(err)}`;
+      this.nestLogger.error(msg, err instanceof Error ? err.stack : undefined);
     }
   }

3-10: Avoid duplicating types defined in features; import shared interfaces to prevent drift.

If packages/features/tasker/types/logger.ts defines IMyLoggerSettings/ILogger, consider importing and implementing ILogger here to keep one source of truth.


25-26: Clarify DI plan for request context.

This class is no longer an @Injectable. If you still inject it, ensure the provider is registered explicitly (useClass/useValue) and decide whether you need REQUEST scope for per-request metadata (e.g., requestId).

I can sketch the provider config (singleton vs. request-scoped) and a request-scoped wrapper if helpful.

packages/trigger/tasks/index.ts (1)

1-1: Prefer explicit re-exports over wildcard barrels.

Prevents accidental API surface growth and improves tree-shaking.

-export * from "./calendars/create";
+export { create } from "./calendars/create";
packages/features/tasker/types/tasker.ts (1)

3-5: Make dependencies immutable

Mark logger as readonly to prevent accidental reassignment and better convey intent.

 export interface ITaskerDependencies {
-  logger: ILogger;
+  readonly logger: ILogger;
 }
packages/platform/libraries/vite.config.js (1)

196-196: Consider treating "@calcom/trigger" as external

If @calcom/trigger is a workspace package meant to be consumed separately, add it to rollupOptions.external to avoid bundling it into this lib. Add a corresponding entry in output.globals only if you ship a format requiring it.

Example addition inside rollupOptions.external:

external: [
  // ...
  "@calcom/trigger",
],
apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts (1)

1-13: File naming: avoid “.service.ts” for new files

Guideline suggests avoiding .service.ts for new files. Consider renaming to google-calendar-sync-tasker.ts and updating imports.

apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts (1)

1-13: File naming: avoid “.service.ts” for new files

Consider google-calendar-trigger-tasker.ts to align with the guideline and reduce Nest-style suffix proliferation.

apps/api/v2/src/lib/modules/google-calendar-tasker.module.ts (1)

7-12: Optional: provide Logger via factory for configurable levels

If you need env-driven log levels or prefixes, expose Logger with useFactory (injecting ConfigService) instead of the class directly. Keeps the bridge decoupled from Nest while allowing per-env tuning.

Example:

@Module({
  providers: [
    {
      provide: Logger,
      useFactory: (cfg: ConfigService) =>
        new Logger({ minLevel: Number(cfg.get('LOGGER_BRIDGE_LOG_LEVEL') ?? 1) }),
      inject: [ConfigService],
    },
    GoogleCalendarSyncTasker,
    GoogleCalendarTriggerDevTasker,
    GoogleCalendarTasker,
  ],
  exports: [GoogleCalendarTasker],
})
export class GoogleCalendarTaskerModule {}
packages/trigger/tasks/calendars/create.ts (1)

6-11: Tighten payload validation and fix minor log formatting.

Enforce non-empty names and remove the extra space in the log message.

   schema: z.object({
-    name: z.string(),
+    name: z.string().min(1, "name is required"),
   }),
   run: async (payload: { name: string }) => {
-    logger.info(`Hello create  ${payload.name}!`);
+    logger.info(`Hello create ${payload.name}!`);
   },
packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts (1)

9-18: Lower log verbosity and prefer structured logs to avoid leaking PII in info logs.

Names/surnames can be PII; use debug and structured fields.

   async createEvent(payload: Parameters<ICalendarTasker["createEvent"]>[0]) {
-    this.dependencies.logger.info(`Hello ${payload.name}`);
+    this.dependencies.logger.debug("createEvent", { name: payload.name });
     const runId = `sync_${nanoid(10)}`;
     return { runId };
   }
   async updateEvent(payload: Parameters<ICalendarTasker["updateEvent"]>[0]) {
-    this.dependencies.logger.info(`Hello ${payload.surname}`);
+    this.dependencies.logger.debug("updateEvent", { surname: payload.surname });
     const runId = `sync_${nanoid(10)}`;
     return { runId };
   }
apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts (1)

16-20: Pass a sub-logger with a prefix for clearer log context.

Improves log scanning and source attribution.

     super({
       primaryTasker: primaryTasker,
       fallbackTasker: fallbackTasker,
-      logger,
+      logger: logger.getSubLogger({ prefix: ["GoogleCalendarTasker"] }),
     });
packages/features/tasker/calendars/GoogleCalendarTasker.ts (2)

3-11: Depend on the interface, not concrete implementations, to keep DI flexible.

Loosen types so any ICalendarTasker can be wired as primary/fallback.

-import type { GoogleCalendarSyncTasker } from "./GoogleCalendarSyncTasker";
-import type { GoogleCalendarTriggerDevTasker } from "./GoogleCalendarTriggerTasker";
 import type { ICalendarTasker } from "./calendar";
 
 export interface ICalendarTaskerDependencies {
-  primaryTasker: GoogleCalendarTriggerDevTasker | GoogleCalendarSyncTasker;
-  fallbackTasker: GoogleCalendarSyncTasker;
+  primaryTasker: ICalendarTasker;
+  fallbackTasker: ICalendarTasker;
   logger: ILogger;
 }

21-30: Outdated comment path (“web/v2”).

Adjust to the actual path (apps/api/v2) to avoid confusion.

- this is how we can now create the google calendar tasker, we will use dependency injection in web/v2 
+ this is how we can now create the google calendar tasker; in this repo we use dependency injection in apps/api/v2
packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts (2)

17-19: Fix mislabeled log messages in updateEvent.

They currently say "createEvent".

-    this.dependencies.logger.debug("createEvent", payload);
+    this.dependencies.logger.debug("updateEvent", payload);
-    this.dependencies.logger.debug("createEvent handle id", handle.id);
+    this.dependencies.logger.debug("updateEvent handle id", handle.id);

9-14: createEvent path looks good; verify handle.id is a string.

If handle.id isn’t guaranteed to be string, coerce to String to satisfy the interface.

-    return { runId: handle.id };
+    return { runId: String(handle.id) };
packages/features/tasker/SafeDispatchTasker.ts (4)

33-36: Guard against non-function properties before .apply.

Adds clearer error when an invalid key is dispatched.

-      const method = this.primaryTasker[taskName] as (...args: any[]) => any;
-      return await method.apply(this.primaryTasker, args);
+      const method = this.primaryTasker[taskName] as unknown;
+      if (typeof method !== "function") {
+        throw new TypeError(`Task '${String(taskName)}' is not a function on primaryTasker.`);
+      }
+      return await (method as (...args: any[]) => any).apply(this.primaryTasker, args);

42-42: Log message should not assume a "SyncTasker" fallback.

Generalize wording; fallback may not be sync.

-      this.logger.warn(`Falling back to SyncTasker for '${String(taskName)}'.`);
+      this.logger.warn(`Falling back to fallback tasker for '${String(taskName)}'.`);

44-45: Prefer awaiting the fallback for symmetry and consistent stack traces.

-      const fallbackMethod = this.fallbackTasker[taskName] as (...args: any[]) => any;
-      return fallbackMethod.apply(this.fallbackTasker, args);
+      const fallbackMethod = this.fallbackTasker[taskName] as (...args: any[]) => any;
+      return await fallbackMethod.apply(this.fallbackTasker, args);

22-23: Consider lowering log level or redacting args.

Dispatching with full args at info level may log PII (e.g., names). Use debug or redact sensitive fields.

-    this.logger.info(`Dispatching task '${String(taskName)}'`, { args });
+    this.logger.debug(`Dispatching task '${String(taskName)}'`, { args: "[redacted]" });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 712467a and a11c2d0.

📒 Files selected for processing (25)
  • apps/api/v2/package.json (1 hunks)
  • apps/api/v2/src/lib/logger.bridge.ts (2 hunks)
  • apps/api/v2/src/lib/modules/google-calendar-tasker.module.ts (1 hunks)
  • apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts (1 hunks)
  • apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts (1 hunks)
  • apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts (1 hunks)
  • apps/api/v2/tsconfig.json (1 hunks)
  • apps/web/package.json (1 hunks)
  • packages/features/tasker/SafeDispatchTasker.ts (1 hunks)
  • packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts (1 hunks)
  • packages/features/tasker/calendars/GoogleCalendarTasker.ts (1 hunks)
  • packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts (1 hunks)
  • packages/features/tasker/calendars/calendar.ts (1 hunks)
  • packages/features/tasker/types/logger.ts (1 hunks)
  • packages/features/tasker/types/tasker.ts (1 hunks)
  • packages/platform/libraries/package.json (3 hunks)
  • packages/platform/libraries/taskers.ts (1 hunks)
  • packages/platform/libraries/vite.config.js (2 hunks)
  • packages/trigger/.gitignore (1 hunks)
  • packages/trigger/index.ts (1 hunks)
  • packages/trigger/package.json (1 hunks)
  • packages/trigger/tasks/calendars/create.ts (1 hunks)
  • packages/trigger/tasks/index.ts (1 hunks)
  • packages/trigger/trigger.config.ts (1 hunks)
  • packages/trigger/tsconfig.json (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{service,repository}.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Avoid dot-suffixes like .service.ts or .repository.ts for new files; reserve .test.ts, .spec.ts, .types.ts for their specific purposes

Files:

  • apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts
  • packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts
  • packages/trigger/tasks/calendars/create.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts
  • packages/trigger/trigger.config.ts
  • packages/features/tasker/types/tasker.ts
  • packages/trigger/index.ts
  • packages/features/tasker/types/logger.ts
  • apps/api/v2/src/lib/modules/google-calendar-tasker.module.ts
  • packages/features/tasker/calendars/calendar.ts
  • packages/features/tasker/calendars/GoogleCalendarTasker.ts
  • packages/platform/libraries/taskers.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts
  • packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts
  • apps/api/v2/src/lib/logger.bridge.ts
  • packages/features/tasker/SafeDispatchTasker.ts
  • packages/trigger/tasks/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts
  • packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts
  • packages/trigger/tasks/calendars/create.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts
  • packages/trigger/trigger.config.ts
  • packages/features/tasker/types/tasker.ts
  • packages/trigger/index.ts
  • packages/features/tasker/types/logger.ts
  • apps/api/v2/src/lib/modules/google-calendar-tasker.module.ts
  • packages/features/tasker/calendars/calendar.ts
  • packages/features/tasker/calendars/GoogleCalendarTasker.ts
  • packages/platform/libraries/taskers.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts
  • packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts
  • apps/api/v2/src/lib/logger.bridge.ts
  • packages/features/tasker/SafeDispatchTasker.ts
  • packages/trigger/tasks/index.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts
  • packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts
  • packages/platform/libraries/vite.config.js
  • packages/trigger/tasks/calendars/create.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts
  • packages/trigger/trigger.config.ts
  • packages/features/tasker/types/tasker.ts
  • packages/trigger/index.ts
  • packages/features/tasker/types/logger.ts
  • apps/api/v2/src/lib/modules/google-calendar-tasker.module.ts
  • packages/features/tasker/calendars/calendar.ts
  • packages/features/tasker/calendars/GoogleCalendarTasker.ts
  • packages/platform/libraries/taskers.ts
  • apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts
  • packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts
  • apps/api/v2/src/lib/logger.bridge.ts
  • packages/features/tasker/SafeDispatchTasker.ts
  • packages/trigger/tasks/index.ts
🧠 Learnings (1)
📚 Learning: 2025-08-05T12:04:29.037Z
Learnt from: din-prajapati
PR: calcom/cal.com#21854
File: packages/app-store/office365calendar/__tests__/unit_tests/SubscriptionManager.test.ts:0-0
Timestamp: 2025-08-05T12:04:29.037Z
Learning: In packages/app-store/office365calendar/lib/CalendarService.ts, the fetcher method in Office365CalendarService class is public, not private. It was specifically changed from private to public in this PR to support proper testing and external access patterns.

Applied to files:

  • packages/features/tasker/calendars/calendar.ts
🧬 Code graph analysis (9)
apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts (4)
apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts (1)
  • Injectable (6-13)
apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts (1)
  • Injectable (9-22)
packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts (1)
  • GoogleCalendarTriggerDevTasker (6-22)
apps/api/v2/src/lib/logger.bridge.ts (1)
  • Logger (26-152)
packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts (2)
packages/features/tasker/calendars/calendar.ts (1)
  • ICalendarTasker (1-4)
packages/features/tasker/types/tasker.ts (1)
  • ITaskerDependencies (3-5)
apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts (4)
apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts (1)
  • Injectable (9-22)
apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts (1)
  • Injectable (6-13)
packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts (1)
  • GoogleCalendarSyncTasker (6-19)
apps/api/v2/src/lib/logger.bridge.ts (1)
  • Logger (26-152)
packages/features/tasker/types/tasker.ts (1)
packages/features/tasker/types/logger.ts (1)
  • ILogger (11-44)
apps/api/v2/src/lib/modules/google-calendar-tasker.module.ts (4)
packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts (1)
  • GoogleCalendarSyncTasker (6-19)
packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts (1)
  • GoogleCalendarTriggerDevTasker (6-22)
apps/api/v2/src/lib/logger.bridge.ts (1)
  • Logger (26-152)
packages/features/tasker/calendars/GoogleCalendarTasker.ts (1)
  • GoogleCalendarTasker (15-19)
packages/features/tasker/calendars/GoogleCalendarTasker.ts (4)
packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts (1)
  • GoogleCalendarTriggerDevTasker (6-22)
packages/features/tasker/calendars/GoogleCalendarSyncTasker.ts (1)
  • GoogleCalendarSyncTasker (6-19)
packages/features/tasker/types/logger.ts (1)
  • ILogger (11-44)
packages/features/tasker/calendars/calendar.ts (1)
  • ICalendarTasker (1-4)
apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts (3)
apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts (1)
  • Injectable (6-13)
apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts (1)
  • Injectable (6-13)
apps/api/v2/src/lib/logger.bridge.ts (1)
  • Logger (26-152)
packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts (3)
packages/features/tasker/calendars/calendar.ts (1)
  • ICalendarTasker (1-4)
packages/features/tasker/types/tasker.ts (1)
  • ITaskerDependencies (3-5)
packages/trigger/tasks/calendars/create.ts (1)
  • create (4-12)
packages/features/tasker/SafeDispatchTasker.ts (1)
packages/features/tasker/types/logger.ts (1)
  • ILogger (11-44)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (14)
packages/trigger/tsconfig.json (1)

1-5: Config looks fine and consistent with the monorepo base.
Extends base, scopes includes/excludes sensibly.

apps/web/package.json (1)

44-45: No imports of @calcom/trigger found – no bundling concerns
Audit across apps/web (static import, dynamic import, require) returned zero matches; safe to leave the dependency as-is.

packages/trigger/index.ts (1)

1-1: Good: named re-exports promote tree-shaking and clear imports.
No default export; aligns with guidelines.

packages/trigger/package.json (1)

10-11: CLI availability check.

"trigger dev/deploy" require the CLI. You have trigger.dev in devDependencies—good. Ensure CI env installs dev deps where these scripts run.

Also applies to: 20-20

packages/platform/libraries/package.json (1)

3-3: Placeholder version "9.9.9".

If this is a real publish, set a semver that matches your release process; otherwise ensure dependants pin correctly.

packages/trigger/trigger.config.ts (1)

26-31: No action needed; Prisma schema path resolves correctly

The relative path "../prisma/schema.prisma" from packages/trigger/trigger.config.ts points to packages/prisma/schema.prisma, matching the located schema file—no changes required.

apps/api/v2/tsconfig.json (1)

15-33: Taskers alias resolution verified
All imports of @calcom/platform-libraries/taskers in apps/api/v2 use the tsconfig path alias, no deep relative imports remain, and the ./taskers subpath is exported in packages/platform/libraries/package.json.

packages/platform/libraries/vite.config.js (1)

31-31: Packaging for taskers entry verified: taskers.ts exists, package.json exports include "./taskers" with the correct types path (dist/taskers.d.ts), the file re-exports the expected symbols, and the tsconfig path alias is set. Run your build to produce dist/taskers.d.ts before publishing.

apps/api/v2/src/lib/services/taskers/google-calendar-sync-tasker.service.ts (1)

6-13: LGTM: clean DI wrapper over base tasker

Constructor cleanly forwards the logger; extends base correctly.

apps/api/v2/src/lib/services/taskers/google-calendar-trigger-tasker.service.ts (1)

6-13: LGTM: consistent DI for dev trigger tasker

Matches the sync tasker pattern; forwards logger properly.

packages/platform/libraries/taskers.ts (1)

1-3: Barrel looks good; named exports only.

Centralized, named re-exports aid tree-shaking and refactors.

apps/api/v2/src/lib/services/taskers/google-calendar-tasker.service.ts (1)

1-22: Ignore file naming suggestion; .service.ts suffix aligns with the existing pattern across apps/api/v2.

Likely an incorrect or invalid review comment.

packages/features/tasker/calendars/GoogleCalendarTasker.ts (1)

13-13: Verify TASKS consumption for dispatch gating
ICalendarTasker defines updateEvent but TASKS only lists "createEvent". If TASKS is used to whitelist dispatch names, include "updateEvent":

-export const TASKS = ["createEvent"] as const;
+export const TASKS = ["createEvent", "updateEvent"] as const;

Otherwise confirm TASKS isn’t consumed elsewhere before changing.

packages/features/tasker/SafeDispatchTasker.ts (1)

5-16: Solid base with typed dispatch and DI.

Type constraints and dependency wiring look correct.

Comment on lines +31 to +32
"@calcom/platform-libraries/private-links": ["../../../packages/platform/libraries/private-links.ts"],
"@calcom/platform-libraries/taskers": ["../../../packages/platform/libraries/taskers.ts"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Path alias overrides the package export; remove it to avoid split sources.

@calcom/platform-libraries now exports "taskers". Keeping this alias points imports to a raw TS file and can desync with the published package. Remove the alias and fix the dangling comma.

-      "@calcom/platform-libraries/private-links": ["../../../packages/platform/libraries/private-links.ts"],
-      "@calcom/platform-libraries/taskers": ["../../../packages/platform/libraries/taskers.ts"]
+      "@calcom/platform-libraries/private-links": ["../../../packages/platform/libraries/private-links.ts"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@calcom/platform-libraries/private-links": ["../../../packages/platform/libraries/private-links.ts"],
"@calcom/platform-libraries/taskers": ["../../../packages/platform/libraries/taskers.ts"]
"@calcom/platform-libraries/private-links": ["../../../packages/platform/libraries/private-links.ts"]
🤖 Prompt for AI Agents
In apps/api/v2/tsconfig.json around lines 31 to 32, the path alias for
"@calcom/platform-libraries/taskers" overrides the package export and points to
a raw TS file; remove the entire "@calcom/platform-libraries/taskers" alias
entry from the paths section so imports resolve to the packaged export instead,
and after removing the line ensure the JSON stays valid by deleting the
now-dangling trailing comma (or adjusting commas appropriately).

Comment on lines +2 to +3
createEvent(payload: { name: string }): Promise<{ runId: string }>;
updateEvent(payload: { surname: string }): Promise<{ runId: string }>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent payload shapes ("name" vs "surname") will break implementations.

Likely a typo; align both methods and consider real event fields.

 export interface ICalendarTasker {
-  createEvent(payload: { name: string }): Promise<{ runId: string }>;
-  updateEvent(payload: { surname: string }): Promise<{ runId: string }>;
+  createEvent(payload: { name: string }): Promise<{ runId: string }>;
+  updateEvent(payload: { name: string }): Promise<{ runId: string }>;
 }

Optionally, introduce domain types (calendarId, eventId, start/end, summary) and reuse across taskers.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
createEvent(payload: { name: string }): Promise<{ runId: string }>;
updateEvent(payload: { surname: string }): Promise<{ runId: string }>;
export interface ICalendarTasker {
createEvent(payload: { name: string }): Promise<{ runId: string }>;
updateEvent(payload: { name: string }): Promise<{ runId: string }>;
}
🤖 Prompt for AI Agents
In packages/features/tasker/calendars/calendar.ts around lines 2-3, the method
payloads are inconsistent: createEvent expects { name: string } while
updateEvent expects { surname: string }; change them to use the same field
(prefer a semantically clear field like "summary" or "title") and update both
signatures to the same payload shape, and replace ad-hoc objects with a shared
EventPayload type (and optionally richer domain types such as CalendarId,
EventId, start/end timestamps) so both methods reuse the same definition.

Comment on lines +5 to +6
"main": "./index.ts",
"types": "./index.ts",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Avoid shipping .ts entry points; map exports to built JS + d.ts.

Pointing "main/types" and "exports" to .ts will break Node/bundlers outside your Vite build. Compile and export dist artifacts instead.

Apply after adding a build that outputs to dist:

-  "main": "./index.ts",
-  "types": "./index.ts",
+  "type": "module",
+  "main": "./dist/index.js",
+  "types": "./dist/index.d.ts",
...
-  "exports": {
-    ".": "./index.ts",
-    "./trigger": "./index.ts"
-  }
+  "exports": {
+    ".": {
+      "import": "./dist/index.js",
+      "types": "./dist/index.d.ts"
+    }
+  }

If this package is purely internal and only resolved via a Vite alias, confirm no runtime imports rely on Node resolution.

Also applies to: 23-26


🏁 Script executed:

#!/bin/bash
# Find runtime imports of @calcom/trigger (ESM/CJS)
rg -nP --glob '!**/dist/**' "(from|require\\()\\s*['\"]@calcom/trigger" -C2

Length of output: 407


Map package entrypoints to compiled dist artifacts
Runtime imports (@calcom/trigger) still point at .ts (e.g. packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts). After adding a build that outputs to dist, update packages/trigger/package.json:

-  "main": "./index.ts",
-  "types": "./index.ts",
+  "type": "module",
+  "main": "./dist/index.js",
+  "types": "./dist/index.d.ts",-  "exports": {
-    ".": "./index.ts",
-    "./trigger": "./index.ts"
-  }
+  "exports": {
+    ".": {
+      "import": "./dist/index.js",
+      "types": "./dist/index.d.ts"
+    }
+  }

Ensure no runtime imports rely on .ts entrypoints.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/trigger/package.json around lines 5-6, the package main and types
fields point to TypeScript sources ("./index.ts") instead of compiled artifacts;
update "main" to "./dist/index.js" and "types" to "./dist/index.d.ts" (or the
actual emitted JS and d.ts paths), run the build to produce dist, then search
the repo for any runtime imports that reference .ts entrypoints (e.g.
packages/features/tasker/calendars/GoogleCalendarTriggerTasker.ts) and replace
them with the corresponding package import (@calcom/trigger) or the compiled
paths so no runtime code imports .ts files; finally verify package.json "files"
includes dist and that consumers resolve the new entrypoints.

@Injectable()
export class GoogleCalendarTasker extends BaseGoogleCalendarTasker {
constructor(
primaryTasker: GoogleCalendarTriggerDevTasker,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for example here:

ApiV2 could provide it's own BullMQ tasker

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this could also be controller by env variable

"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.335",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@9.9.9",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

publish libraries before merge

{
"name": "@calcom/platform-libraries",
"version": "0.0.0",
"version": "9.9.9",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

publish libraries before merging

import { z } from "zod";

export const create = schemaTask({
id: "calendar.google.create.event",
Copy link
Contributor

@volnei volnei Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can change this namespace...
suggestion: calendar.google.create-event or calendar.google.event.create

create as part of namespace can lead confusing, I think.

import { z } from "zod";

export const create = schemaTask({
id: "calendar.google.create.event",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.event might be overkill and not needed

@vercel
Copy link

vercel bot commented Sep 2, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Sep 4, 2025 6:41am
cal-eu Ignored Ignored Sep 4, 2025 6:41am

@ThyMinimalDev ThyMinimalDev changed the title feature: async tasker for google calendar events feat: async tasker for google calendar events Sep 2, 2025
devin-ai-integration bot and others added 2 commits September 2, 2025 08:14
…ger dependency

- Add DI tokens for GoogleCalendarTasker, sync/trigger taskers, and logger
- Create LoggerAdapter to bridge tslog to ILogger interface
- Create modules for GoogleCalendarTasker, GoogleCalendarSyncTasker, GoogleCalendarTriggerDevTasker, and Logger
- Create container that wires all dependencies together
- Add index file for easy imports
- Follow existing @evyweb/ioctopus patterns from BusyTimes and other services

Co-Authored-By: morgan@cal.com <morgan@cal.com>
Co-Authored-By: morgan@cal.com <morgan@cal.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
packages/lib/di/tokens.ts (1)

54-61: New DI tokens look good; consider freezing for safety.

Nice separation between service and module tokens. To prevent accidental mutation at runtime and improve type safety, consider freezing and/or asserting const.

Apply:

 export const DI_TOKENS = {
   ...
   GOOGLE_CALENDAR_TRIGGER_DEV_TASKER: Symbol("GoogleCalendarTriggerDevTasker"),
   GOOGLE_CALENDAR_TRIGGER_DEV_TASKER_MODULE: Symbol("GoogleCalendarTriggerDevTaskerModule"),
-};
+} as const;
+Object.freeze(DI_TOKENS);
packages/lib/di/modules/Logger.ts (1)

1-7: Optional: allow configurable settings via DI instead of env-only.

If you anticipate per-context logging (tests, workers), inject settings via a token to avoid hard-coding env lookups in the adapter.

Example:

+import type { IMyLoggerSettings } from "../adapters/LoggerAdapter";
+const LOGGER_SETTINGS = Symbol("LoggerSettings");
 export const loggerModule = createModule();
-loggerModule.bind(DI_TOKENS.LOGGER).toClass(LoggerAdapter);
+loggerModule.bind(LOGGER_SETTINGS).toValue({ /* defaults */ } satisfies IMyLoggerSettings);
+loggerModule.bind(DI_TOKENS.LOGGER).toClass(LoggerAdapter, {
+  settings: LOGGER_SETTINGS,
+});

(Requires LoggerAdapter to accept settings in its constructor.)

packages/lib/di/containers/GoogleCalendarTasker.ts (1)

11-19: Prefer lazy container init + reset hook for tests.

A top-level singleton container can be hard to isolate in tests and can capture stale env/config. Lazy-init and a test-only reset improve ergonomics without affecting prod.

Apply:

-import { createContainer } from "@evyweb/ioctopus";
+import { createContainer, type Container } from "@evyweb/ioctopus";
@@
-const container = createContainer();
-container.load(DI_TOKENS.LOGGER_MODULE, loggerModule);
-container.load(DI_TOKENS.GOOGLE_CALENDAR_SYNC_TASKER_MODULE, googleCalendarSyncTaskerModule);
-container.load(DI_TOKENS.GOOGLE_CALENDAR_TRIGGER_DEV_TASKER_MODULE, googleCalendarTriggerDevTaskerModule);
-container.load(DI_TOKENS.GOOGLE_CALENDAR_TASKER_MODULE, googleCalendarTaskerModule);
+let container: Container | null = null;
+function ensureContainer() {
+  if (container) return container;
+  const c = createContainer();
+  c.load(DI_TOKENS.LOGGER_MODULE, loggerModule);
+  c.load(DI_TOKENS.GOOGLE_CALENDAR_SYNC_TASKER_MODULE, googleCalendarSyncTaskerModule);
+  c.load(DI_TOKENS.GOOGLE_CALENDAR_TRIGGER_DEV_TASKER_MODULE, googleCalendarTriggerDevTaskerModule);
+  c.load(DI_TOKENS.GOOGLE_CALENDAR_TASKER_MODULE, googleCalendarTaskerModule);
+  container = c;
+  return c;
+}
@@
 export function getGoogleCalendarTasker() {
-  return container.get<GoogleCalendarTasker>(DI_TOKENS.GOOGLE_CALENDAR_TASKER);
+  return ensureContainer().get<GoogleCalendarTasker>(DI_TOKENS.GOOGLE_CALENDAR_TASKER);
 }
+
+// Test-only helper
+export function __resetGoogleCalendarTaskerContainerForTests() {
+  container = null;
+}
packages/lib/di/modules/GoogleCalendarSyncTasker.ts (1)

1-11: Nit: make the dependency map immutable.

Small safety to avoid accidental reassignment.

Apply:

-googleCalendarSyncTaskerModule.bind(DI_TOKENS.GOOGLE_CALENDAR_SYNC_TASKER).toClass(GoogleCalendarSyncTasker, {
-  logger: DI_TOKENS.LOGGER,
-} satisfies Record<keyof ITaskerDependencies, symbol>);
+googleCalendarSyncTaskerModule
+  .bind(DI_TOKENS.GOOGLE_CALENDAR_SYNC_TASKER)
+  .toClass(
+    GoogleCalendarSyncTasker,
+    {
+      logger: DI_TOKENS.LOGGER,
+    } as const satisfies Record<keyof ITaskerDependencies, symbol>,
+  );
packages/lib/di/modules/GoogleCalendarTasker.ts (1)

10-12: Avoid shipping the dev trigger as primary in production. Gate selection by env.

Make the primary tasker conditional to prevent accidental use of the dev trigger in prod.

Apply:

 import { DI_TOKENS } from "../tokens";

 export const googleCalendarTaskerModule = createModule();
+const primaryToken =
+  process.env.NODE_ENV === "production"
+    ? DI_TOKENS.GOOGLE_CALENDAR_SYNC_TASKER
+    : DI_TOKENS.GOOGLE_CALENDAR_TRIGGER_DEV_TASKER;
 googleCalendarTaskerModule.bind(DI_TOKENS.GOOGLE_CALENDAR_TASKER).toClass(GoogleCalendarTasker, {
-  primaryTasker: DI_TOKENS.GOOGLE_CALENDAR_TRIGGER_DEV_TASKER,
+  primaryTasker: primaryToken,
   fallbackTasker: DI_TOKENS.GOOGLE_CALENDAR_SYNC_TASKER,
   logger: DI_TOKENS.LOGGER,
 } satisfies Record<keyof ICalendarTaskerDependencies, symbol>);
packages/lib/di/adapters/LoggerAdapter.ts (3)

6-10: Harden env parsing and clamp log level.

Add radix, handle NaN, and clamp to an expected range.

-  readonly settings: IMyLoggerSettings = {
-    minLevel: parseInt(process.env.NEXT_PUBLIC_LOGGER_LEVEL || "4"),
+  readonly settings: IMyLoggerSettings = {
+    minLevel: (() => {
+      const raw = process.env.NEXT_PUBLIC_LOGGER_LEVEL;
+      const n = Number.parseInt(raw ?? "", 10);
+      return Number.isNaN(n) ? 4 : Math.min(Math.max(n, 0), 6);
+    })(),
     displayTimestamp: true,
     logFormat: process.env.NODE_ENV === "production" ? "json" : "pretty",
   };

29-55: Consider tightening arg types for log methods.

Using unknown[] (or overloads for string | object | Error) improves type-safety without affecting usage.

-  info(...args: any[]): void {
+  info(...args: unknown[]): void {
     logger.info(...args);
   }
-  warn(...args: any[]): void {
+  warn(...args: unknown[]): void {
     logger.warn(...args);
   }
-  error(...args: any[]): void {
+  error(...args: unknown[]): void {
     logger.error(...args);
   }
-  debug(...args: any[]): void {
+  debug(...args: unknown[]): void {
     logger.debug(...args);
   }
-  trace(...args: any[]): void {
+  trace(...args: unknown[]): void {
     logger.trace(...args);
   }
-  fatal(...args: any[]): void {
+  fatal(...args: unknown[]): void {
     logger.fatal(...args);
   }
-  silly(...args: any[]): void {
+  silly(...args: unknown[]): void {
     logger.silly(...args);
   }

3-3: Migrate logger to a named export for consistency

packages/lib/logger.ts currently uses export default logger; refactor it to

export const logger = new Logger(/*…*/)

and update consumers (e.g. in packages/lib/di/adapters/LoggerAdapter.ts) to

import { logger } from '../../logger';
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d6681b4 and c861815.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • packages/lib/di/adapters/LoggerAdapter.ts (1 hunks)
  • packages/lib/di/containers/GoogleCalendarTasker.ts (1 hunks)
  • packages/lib/di/index.ts (1 hunks)
  • packages/lib/di/modules/GoogleCalendarSyncTasker.ts (1 hunks)
  • packages/lib/di/modules/GoogleCalendarTasker.ts (1 hunks)
  • packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts (1 hunks)
  • packages/lib/di/modules/Logger.ts (1 hunks)
  • packages/lib/di/tokens.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/lib/di/tokens.ts
  • packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts
  • packages/lib/di/modules/GoogleCalendarSyncTasker.ts
  • packages/lib/di/modules/GoogleCalendarTasker.ts
  • packages/lib/di/index.ts
  • packages/lib/di/modules/Logger.ts
  • packages/lib/di/containers/GoogleCalendarTasker.ts
  • packages/lib/di/adapters/LoggerAdapter.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/lib/di/tokens.ts
  • packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts
  • packages/lib/di/modules/GoogleCalendarSyncTasker.ts
  • packages/lib/di/modules/GoogleCalendarTasker.ts
  • packages/lib/di/index.ts
  • packages/lib/di/modules/Logger.ts
  • packages/lib/di/containers/GoogleCalendarTasker.ts
  • packages/lib/di/adapters/LoggerAdapter.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/lib/di/tokens.ts
  • packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts
  • packages/lib/di/modules/GoogleCalendarSyncTasker.ts
  • packages/lib/di/modules/GoogleCalendarTasker.ts
  • packages/lib/di/index.ts
  • packages/lib/di/modules/Logger.ts
  • packages/lib/di/containers/GoogleCalendarTasker.ts
  • packages/lib/di/adapters/LoggerAdapter.ts
🧬 Code graph analysis (6)
packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts (2)
packages/lib/di/tokens.ts (1)
  • DI_TOKENS (1-62)
packages/features/tasker/types/tasker.ts (1)
  • ITaskerDependencies (3-5)
packages/lib/di/modules/GoogleCalendarSyncTasker.ts (2)
packages/lib/di/tokens.ts (1)
  • DI_TOKENS (1-62)
packages/features/tasker/types/tasker.ts (1)
  • ITaskerDependencies (3-5)
packages/lib/di/modules/GoogleCalendarTasker.ts (2)
packages/lib/di/tokens.ts (1)
  • DI_TOKENS (1-62)
packages/features/tasker/calendars/GoogleCalendarTasker.ts (1)
  • ICalendarTaskerDependencies (7-11)
packages/lib/di/modules/Logger.ts (2)
packages/lib/di/tokens.ts (1)
  • DI_TOKENS (1-62)
packages/lib/di/adapters/LoggerAdapter.ts (1)
  • LoggerAdapter (5-56)
packages/lib/di/containers/GoogleCalendarTasker.ts (6)
packages/lib/di/tokens.ts (1)
  • DI_TOKENS (1-62)
packages/lib/di/modules/Logger.ts (1)
  • loggerModule (6-6)
packages/lib/di/modules/GoogleCalendarSyncTasker.ts (1)
  • googleCalendarSyncTaskerModule (8-8)
packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts (1)
  • googleCalendarTriggerDevTaskerModule (8-8)
packages/lib/di/modules/GoogleCalendarTasker.ts (1)
  • googleCalendarTaskerModule (8-8)
packages/lib/di/index.ts (1)
  • getGoogleCalendarTasker (1-1)
packages/lib/di/adapters/LoggerAdapter.ts (1)
packages/features/tasker/types/logger.ts (2)
  • ILogger (11-44)
  • IMyLoggerSettings (3-7)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (8)
packages/lib/di/modules/Logger.ts (1)

6-7: LGTM: clean DI binding for ILogger.

Binding DI_TOKENS.LOGGER to LoggerAdapter via a module is straightforward and keeps consumers decoupled.

packages/lib/di/index.ts (1)

1-2: LGTM: re-exports are explicit and named.

Keeps tree-shaking friendly and aligns with the no-default-exports guideline.

packages/lib/di/containers/GoogleCalendarTasker.ts (1)

5-15: Confirm binding and import/export for GOOGLE_CALENDAR_TASKER
Binding for DI_TOKENS.GOOGLE_CALENDAR_TASKER is present in packages/lib/di/modules/GoogleCalendarTasker.ts (line 9); ensure that GoogleCalendarTasker is imported at the top of this file and that its defining module exports the class.

packages/lib/di/modules/GoogleCalendarSyncTasker.ts (1)

8-11: LGTM: binding with typed dependency map is solid.

Using satisfies Record<keyof ITaskerDependencies, symbol> ensures the DI map stays in sync with constructor needs.

packages/lib/di/modules/GoogleCalendarTasker.ts (2)

8-13: Type-safe DI binding looks good.

Binding GoogleCalendarTasker with explicit tokens and the satisfies constraint is clean and future-proof.


8-13: DI load order verified
Sync and TriggerDev modules are loaded before the Tasker module in the GoogleCalendarTasker container.

packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts (2)

8-13: Binding and type constraint look correct.

Token mapping to logger via ITaskerDependencies is consistent with the DI pattern used elsewhere.


3-12: Verified named export: GoogleCalendarTriggerDevTasker is correctly exported from @calcom/features/tasker/calendars/GoogleCalendarTriggerTasker.

Comment on lines +5 to +17
export class LoggerAdapter implements ILogger {
readonly settings: IMyLoggerSettings = {
minLevel: parseInt(process.env.NEXT_PUBLIC_LOGGER_LEVEL || "4"),
displayTimestamp: true,
logFormat: process.env.NODE_ENV === "production" ? "json" : "pretty",
};

getSubLogger(options: { prefix?: string[] }): ILogger {
const adapter = new LoggerAdapter();
const originalLogger = logger;

const prefix = options.prefix ? `[${options.prefix.join(":")}] ` : "";

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Sub-logger drops parent prefix; compose prefixes for nested contexts.

Carry forward existing prefix when chaining getSubLogger.

 export class LoggerAdapter implements ILogger {
+  private readonly prefixParts: string[];
+  constructor(prefix: string[] = []) {
+    this.prefixParts = prefix;
+  }
   readonly settings: IMyLoggerSettings = {
@@
-  getSubLogger(options: { prefix?: string[] }): ILogger {
-    const adapter = new LoggerAdapter();
+  getSubLogger(options: { prefix?: string[] }): ILogger {
+    const combined = [...this.prefixParts, ...(options.prefix ?? [])];
+    const adapter = new LoggerAdapter(combined);
     const originalLogger = logger;
-
-    const prefix = options.prefix ? `[${options.prefix.join(":")}] ` : "";
+    const prefix = combined.length ? `[${combined.join(":")}] ` : "";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export class LoggerAdapter implements ILogger {
readonly settings: IMyLoggerSettings = {
minLevel: parseInt(process.env.NEXT_PUBLIC_LOGGER_LEVEL || "4"),
displayTimestamp: true,
logFormat: process.env.NODE_ENV === "production" ? "json" : "pretty",
};
getSubLogger(options: { prefix?: string[] }): ILogger {
const adapter = new LoggerAdapter();
const originalLogger = logger;
const prefix = options.prefix ? `[${options.prefix.join(":")}] ` : "";
export class LoggerAdapter implements ILogger {
private readonly prefixParts: string[];
constructor(prefix: string[] = []) {
this.prefixParts = prefix;
}
readonly settings: IMyLoggerSettings = {
minLevel: parseInt(process.env.NEXT_PUBLIC_LOGGER_LEVEL || "4"),
displayTimestamp: true,
logFormat: process.env.NODE_ENV === "production" ? "json" : "pretty",
};
getSubLogger(options: { prefix?: string[] }): ILogger {
const combined = [...this.prefixParts, ...(options.prefix ?? [])];
const adapter = new LoggerAdapter(combined);
const originalLogger = logger;
const prefix = combined.length ? `[${combined.join(":")}] ` : "";
// …rest of method…
🤖 Prompt for AI Agents
In packages/lib/di/adapters/LoggerAdapter.ts around lines 5 to 17, getSubLogger
currently discards any existing parent prefix when creating a nested logger;
change it to compose prefixes instead: read the parent prefix from the original
logger (if any), combine it with the new options.prefix (joining with ":" and
wrapping in brackets only once), assign the composed prefix to the new adapter
without mutating the parent, copy over relevant settings/context from the
original logger to the new adapter, and return the new adapter so nested
contexts preserve and extend the parent prefix.

@@ -0,0 +1,13 @@
import { createModule } from "@evyweb/ioctopus";

import { GoogleCalendarTriggerDevTasker } from "@calcom/features/tasker/calendars/GoogleCalendarTriggerTasker";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix import path/class mismatch (will fail at build).

Imported name is GoogleCalendarTriggerDevTasker, but the path ends with GoogleCalendarTriggerTasker. Align the path.

-import { GoogleCalendarTriggerDevTasker } from "@calcom/features/tasker/calendars/GoogleCalendarTriggerTasker";
+import { GoogleCalendarTriggerDevTasker } from "@calcom/features/tasker/calendars/GoogleCalendarTriggerDevTasker";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { GoogleCalendarTriggerDevTasker } from "@calcom/features/tasker/calendars/GoogleCalendarTriggerTasker";
import { GoogleCalendarTriggerDevTasker } from "@calcom/features/tasker/calendars/GoogleCalendarTriggerDevTasker";
🤖 Prompt for AI Agents
In packages/lib/di/modules/GoogleCalendarTriggerDevTasker.ts around line 3, the
import statement uses the identifier GoogleCalendarTriggerDevTasker but imports
from a path ending with GoogleCalendarTriggerTasker, causing a mismatch and
build failure; change the import path or the exported class name so they match
(either import from
"@calcom/features/tasker/calendars/GoogleCalendarTriggerDevTasker" if that
file/class exists, or import the correct exported name
GoogleCalendarTriggerTasker if you intended that class), and update any
downstream references to use the matching identifier.

- Add comprehensive build method that reconstructs GoogleCalendarEvent from minimal ID-based parameters
- Support team events with COLLECTIVE and ROUND_ROBIN scheduling types
- Handle delegation credentials with organization membership verification
- Add create method that mirrors GoogleCalendarService.createEvent implementation
- Include buildAndCreate convenience method for combined operations
- Support complex scenarios: team events, delegation credentials, dynamic destination calendar selection
- Generate rich calendar descriptions using getRichDescription
- Maintain compatibility with existing Cal.com event construction patterns

Co-Authored-By: morgan@cal.com <morgan@cal.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
packages/features/tasker/calendars/GoogleCalendarProcessor.ts (3)

127-131: Avoid extra DB hit for team.parentId.

You already select parentId when loading team; reuse it instead of a second prisma.team.findUnique later.

Apply:

   let teamMembers: any[] = [];
   let teamDestinationCalendars: any[] = [];
   let teamObject: CalendarEvent["team"] = undefined;
+  let teamParentId: number | null = null;
@@
-      const allTeamUsers = team.members.map((member) => member.user);
+      teamParentId = team.parentId;
+      const allTeamUsers = team.members.map((member) => member.user);
@@
-    const organizerOrganizationId = organizerUser.profiles[0]?.organizationId;
-    const teamParentId = teamObject?.id
-      ? (await prisma.team.findUnique({ where: { id: teamObject.id }, select: { parentId: true } }))?.parentId
-      : null;
+    const organizerOrganizationId = organizerUser.profiles[0]?.organizationId;

Then the existing getBookerBaseUrl call uses teamParentId as-is.

Also applies to: 158-171, 285-291


410-416: Use centralized logger instead of console.error.

This PR introduces a LoggerAdapter/DI. Prefer logger.error with structured fields for consistency and redaction controls.

Want me to wire ILogger into this class constructor and swap the call?


127-130: Type the arrays (avoid any[]).

Provide concrete types for teamMembers and teamDestinationCalendars to improve safety and editor support.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c861815 and bdd2593.

📒 Files selected for processing (1)
  • packages/features/tasker/calendars/GoogleCalendarProcessor.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/features/tasker/calendars/GoogleCalendarProcessor.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/features/tasker/calendars/GoogleCalendarProcessor.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/features/tasker/calendars/GoogleCalendarProcessor.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-08-27T16:39:38.156Z
Learning: anglerfishlyy successfully implemented CAL-3076 email invitation feature for Cal.com team event-types in PR #23312. The feature allows inviting people via email directly from assignment flow, with automatic team invitation if email doesn't belong to existing team member. Implementation includes Host type modifications (userId?: number, email?: string, isPending?: boolean), CheckedTeamSelect component updates with CreatableSelect, TRPC schema validation with zod email validation, and integration with existing teamInvite system.
📚 Learning: 2025-08-05T12:04:29.037Z
Learnt from: din-prajapati
PR: calcom/cal.com#21854
File: packages/app-store/office365calendar/__tests__/unit_tests/SubscriptionManager.test.ts:0-0
Timestamp: 2025-08-05T12:04:29.037Z
Learning: In packages/app-store/office365calendar/lib/CalendarService.ts, the fetcher method in Office365CalendarService class is public, not private. It was specifically changed from private to public in this PR to support proper testing and external access patterns.

Applied to files:

  • packages/features/tasker/calendars/GoogleCalendarProcessor.ts
🧬 Code graph analysis (1)
packages/features/tasker/calendars/GoogleCalendarProcessor.ts (6)
packages/types/Calendar.d.ts (2)
  • CalendarEvent (163-226)
  • NewCalendarEventType (71-84)
packages/lib/timeFormat.ts (1)
  • getTimeFormatStringFromUserTimeFormat (27-29)
packages/lib/event.ts (1)
  • getEventName (31-113)
packages/lib/EventManager.ts (1)
  • processLocation (99-112)
packages/lib/CalEventParser.ts (1)
  • getRichDescription (375-404)
packages/app-store/googlecalendar/lib/CalendarService.ts (1)
  • GoogleCalendarService (56-1138)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (3)
packages/features/tasker/calendars/GoogleCalendarProcessor.ts (3)

6-6: Likely wrong import path for processLocation.

processLocation lives in packages/lib/EventManager.ts per repo docs/snippets, not CalEventParser. This will break at build if not re-exported.

Apply this diff if it isn’t re-exported:

-import { processLocation } from "@calcom/lib/CalEventParser";
+import { processLocation } from "@calcom/lib/EventManager";

44-74: Unused param and potential state mismatch (externalCalendarId).

build accepts externalCalendarId but never uses it; create uses this.externalCalendarId from the constructor. Either remove this param or persist it to the instance to avoid confusion.

Minimal fix:

   async build({
@@
     type,
   }: {
@@
     type: GoogleCalendarEvent["type"];
   }): Promise<GoogleCalendarEvent> {
+    // Keep instance in sync if caller provided an override
+    if (externalCalendarId) this.externalCalendarId = externalCalendarId;

If you intend constructor-only config, remove externalCalendarId from build and buildAndCreate signatures/usages instead.


348-374: Secrets handling check.

serviceAccountKey and credential.key must never be logged or returned. I don’t see exposure here, but please confirm downstream logs also redact keys.

Comment on lines +14 to +23
export interface GoogleCalendarEvent extends CalendarEvent {
calendarDescription?: string;
seatsPerTimeSlot?: number;
seatsShowAttendees?: boolean;
hideCalendarEventDetails?: boolean;
recurringEvent?: any;
existingRecurringEvent?: any;
iCalUID?: string;
type: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Do not redeclare inherited CalendarEvent props (TypeScript conflict).

You’re narrowing types (e.g., iCalUID, seatsPerTimeSlot, seatsShowAttendees) that already exist on CalendarEvent, which will error in TS. Only add the new field.

Apply:

-export interface GoogleCalendarEvent extends CalendarEvent {
-  calendarDescription?: string;
-  seatsPerTimeSlot?: number;
-  seatsShowAttendees?: boolean;
-  hideCalendarEventDetails?: boolean;
-  recurringEvent?: any;
-  existingRecurringEvent?: any;
-  iCalUID?: string;
-  type: string;
-}
+export interface GoogleCalendarEvent extends CalendarEvent {
+  calendarDescription?: string;
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface GoogleCalendarEvent extends CalendarEvent {
calendarDescription?: string;
seatsPerTimeSlot?: number;
seatsShowAttendees?: boolean;
hideCalendarEventDetails?: boolean;
recurringEvent?: any;
existingRecurringEvent?: any;
iCalUID?: string;
type: string;
}
export interface GoogleCalendarEvent extends CalendarEvent {
calendarDescription?: string;
}

Comment on lines +172 to +190
if (schedulingType === "COLLECTIVE" && teamMembers.length > 0) {
const teamMemberIds = teamMembers.map((member) => member.id);

teamDestinationCalendars = await prisma.destinationCalendar.findMany({
where: {
AND: [
{
OR: [{ eventTypeId: booking.eventTypeId }, { userId: { in: teamMemberIds } }],
},
{
OR: [
credentialId ? { credentialId: credentialId } : {},
delegationCredentialId ? { delegationCredentialId: delegationCredentialId } : {},
].filter((condition) => Object.keys(condition).length > 0),
},
],
},
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prisma OR: [] yields invalid filter when no credential filters are provided.

You always include an OR with a potentially empty array. Prisma rejects OR: [].

Apply:

-        teamDestinationCalendars = await prisma.destinationCalendar.findMany({
-          where: {
-            AND: [
-              {
-                OR: [{ eventTypeId: booking.eventTypeId }, { userId: { in: teamMemberIds } }],
-              },
-              {
-                OR: [
-                  credentialId ? { credentialId: credentialId } : {},
-                  delegationCredentialId ? { delegationCredentialId: delegationCredentialId } : {},
-                ].filter((condition) => Object.keys(condition).length > 0),
-              },
-            ],
-          },
-        });
+        const credOr = [
+          credentialId ? { credentialId } : null,
+          delegationCredentialId ? { delegationCredentialId } : null,
+        ].filter(Boolean) as Record<string, unknown>[];
+
+        const andFilters: any[] = [
+          { OR: [{ eventTypeId: booking.eventTypeId }, { userId: { in: teamMemberIds } }] },
+        ];
+        if (credOr.length) andFilters.push({ OR: credOr });
+
+        teamDestinationCalendars = await prisma.destinationCalendar.findMany({
+          where: { AND: andFilters },
+        });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (schedulingType === "COLLECTIVE" && teamMembers.length > 0) {
const teamMemberIds = teamMembers.map((member) => member.id);
teamDestinationCalendars = await prisma.destinationCalendar.findMany({
where: {
AND: [
{
OR: [{ eventTypeId: booking.eventTypeId }, { userId: { in: teamMemberIds } }],
},
{
OR: [
credentialId ? { credentialId: credentialId } : {},
delegationCredentialId ? { delegationCredentialId: delegationCredentialId } : {},
].filter((condition) => Object.keys(condition).length > 0),
},
],
},
});
}
if (schedulingType === "COLLECTIVE" && teamMembers.length > 0) {
const teamMemberIds = teamMembers.map((member) => member.id);
const credOr = [
credentialId ? { credentialId } : null,
delegationCredentialId ? { delegationCredentialId } : null,
].filter(Boolean) as Record<string, unknown>[];
const andFilters: any[] = [
{ OR: [{ eventTypeId: booking.eventTypeId }, { userId: { in: teamMemberIds } }] },
];
if (credOr.length) andFilters.push({ OR: credOr });
teamDestinationCalendars = await prisma.destinationCalendar.findMany({
where: { AND: andFilters },
});
}
🤖 Prompt for AI Agents
In packages/features/tasker/calendars/GoogleCalendarProcessor.ts around lines
172 to 190, the current Prisma query always includes an OR with a potentially
empty array (credential filters) which causes Prisma to reject OR: []; fix by
building the credential filter array first and only add the OR clause to the
where when that array is non-empty (or alternatively merge credential filters
into the AND only when present), and ensure you don't pass an empty object into
OR — construct the where object conditionally so OR is omitted if there are no
credential filters.

Comment on lines +231 to +245
const organizerDestinationCalendars = await prisma.destinationCalendar.findMany({
where: {
AND: [
{
OR: [{ eventTypeId: booking.eventTypeId }, { userId: userId }],
},
{
OR: [
credentialId ? { credentialId: credentialId } : {},
delegationCredentialId ? { delegationCredentialId: delegationCredentialId } : {},
].filter((condition) => Object.keys(condition).length > 0),
},
],
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same Prisma OR: [] issue for organizerDestinationCalendars.

Apply:

-    const organizerDestinationCalendars = await prisma.destinationCalendar.findMany({
-      where: {
-        AND: [
-          {
-            OR: [{ eventTypeId: booking.eventTypeId }, { userId: userId }],
-          },
-          {
-            OR: [
-              credentialId ? { credentialId: credentialId } : {},
-              delegationCredentialId ? { delegationCredentialId: delegationCredentialId } : {},
-            ].filter((condition) => Object.keys(condition).length > 0),
-          },
-        ],
-      },
-    });
+    {
+      const credOr = [
+        credentialId ? { credentialId } : null,
+        delegationCredentialId ? { delegationCredentialId } : null,
+      ].filter(Boolean) as Record<string, unknown>[];
+      const andFilters: any[] = [{ OR: [{ eventTypeId: booking.eventTypeId }, { userId }] }];
+      if (credOr.length) andFilters.push({ OR: credOr });
+      var organizerDestinationCalendars = await prisma.destinationCalendar.findMany({
+        where: { AND: andFilters },
+      });
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const organizerDestinationCalendars = await prisma.destinationCalendar.findMany({
where: {
AND: [
{
OR: [{ eventTypeId: booking.eventTypeId }, { userId: userId }],
},
{
OR: [
credentialId ? { credentialId: credentialId } : {},
delegationCredentialId ? { delegationCredentialId: delegationCredentialId } : {},
].filter((condition) => Object.keys(condition).length > 0),
},
],
},
});
{
const credOr = [
credentialId ? { credentialId } : null,
delegationCredentialId ? { delegationCredentialId } : null,
].filter(Boolean) as Record<string, unknown>[];
const andFilters: any[] = [
{ OR: [{ eventTypeId: booking.eventTypeId }, { userId }] },
];
if (credOr.length) {
andFilters.push({ OR: credOr });
}
var organizerDestinationCalendars = await prisma.destinationCalendar.findMany({
where: { AND: andFilters },
});
}
🤖 Prompt for AI Agents
In packages/features/tasker/calendars/GoogleCalendarProcessor.ts around lines
231 to 245, the Prisma query can produce an empty OR: [] for the
credential/delegation clause (and potentially for the top-level OR) which Prisma
rejects; instead build the where object conditionally: construct arrays for each
OR (e.g., credentialConditions and topLevelConditions), push candidate objects
only when their values exist, and only include an OR property in the final where
if the corresponding array.length > 0 (omit it otherwise) so the query never
contains an empty array.

Comment on lines +290 to +304
const bookerUrl = await getBookerBaseUrl(teamParentId ?? organizerOrganizationId ?? null);

const eventNameObject = {
attendeeName: booking.attendees[0]?.name || "Nameless",
eventType: type,
eventName: booking.title || type,
teamName: schedulingType === "COLLECTIVE" ? teamObject?.name : null,
host: organizerUser.name || "Nameless",
location: booking.location,
eventDuration: dayjs(booking.endTime).diff(booking.startTime, "minutes"),
bookingFields: {},
t: organizerTranslation,
};
const eventName = getEventName(eventNameObject);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

bookerUrl computed but never set on event.

getRichDescription may rely on event.bookerUrl. Set it before composing calendarDescription.

Apply:

     const eventName = getEventName(eventNameObject);
@@
     calendarEvent = {
       ...processLocation(calendarEvent),
     };
+
+    // Ensure manage links and descriptions can include booker URL
+    calendarEvent.bookerUrl = bookerUrl ?? undefined;
@@
     calendarEvent.calendarDescription = getRichDescription(calendarEvent);

Also applies to: 332-341

🤖 Prompt for AI Agents
In packages/features/tasker/calendars/GoogleCalendarProcessor.ts around lines
290 to 304, the computed bookerUrl is never assigned to the event object before
composing the calendar description; set the computed value onto the event (e.g.,
event.bookerUrl = bookerUrl) immediately after computing bookerUrl and before
calling getRichDescription/getEventName so that downstream description builders
can read it; apply the same assignment in the corresponding block around lines
332 to 341.

Comment on lines +375 to +384
const regularCredential = await prisma.credential.findUnique({
where: { id: this.credentialId },
include: {
user: {
select: {
email: true,
},
},
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prisma guideline: avoid include; use select.

Replace include with select to fetch only needed fields.

Apply:

-      const regularCredential = await prisma.credential.findUnique({
-        where: { id: this.credentialId },
-        include: {
-          user: {
-            select: {
-              email: true,
-            },
-          },
-        },
-      });
+      const regularCredential = await prisma.credential.findUnique({
+        where: { id: this.credentialId },
+        select: {
+          id: true,
+          type: true,
+          key: true,
+          userId: true,
+          appId: true,
+          invalid: true,
+          user: { select: { email: true } },
+        },
+      });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const regularCredential = await prisma.credential.findUnique({
where: { id: this.credentialId },
include: {
user: {
select: {
email: true,
},
},
},
});
const regularCredential = await prisma.credential.findUnique({
where: { id: this.credentialId },
select: {
id: true,
type: true,
key: true,
userId: true,
appId: true,
invalid: true,
user: {
select: {
email: true,
},
},
},
});
🤖 Prompt for AI Agents
In packages/features/tasker/calendars/GoogleCalendarProcessor.ts around lines
375 to 384, replace the prisma.findUnique call that uses include with a select
projection to fetch only the required fields (credential fields plus nested
user.email) to follow the Prisma guideline; update the query to use select with
a nested user: { select: { email: true } } and adjust any consuming code/type
expectations accordingly.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
turbo.json (1)

25-25: Remove duplicate entries from turbo.json’s globalEnv

  • Detected duplicates in the globalEnv array:
    • CALCOM_CREDENTIAL_SYNC_SECRET
    • INTERCOM_SECRET
    • NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED
    • VAPID_PRIVATE_KEY
  • Remove the redundant occurrences to slim the cache key and eliminate noise.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bdd2593 and be5b70e.

📒 Files selected for processing (1)
  • turbo.json (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: anglerfishlyy
PR: calcom/cal.com#0
File: :0-0
Timestamp: 2025-08-27T16:39:38.192Z
Learning: anglerfishlyy successfully implemented CAL-3076 email invitation feature for Cal.com team event-types in PR #23312. The feature allows inviting people via email directly from assignment flow, with automatic team invitation if email doesn't belong to existing team member. Implementation includes Host type modifications (userId?: number, email?: string, isPending?: boolean), CheckedTeamSelect component updates with CreatableSelect, TRPC schema validation with zod email validation, and integration with existing teamInvite system.
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (1)
turbo.json (1)

260-262: Scope TRIGGER_SECRET_KEY and TRIGGER_API_URL to only tasks that consume them.
These keys currently live in globalEnv (turbo.json lines 261–262), which causes any change to invalidate every task’s cache. Move them into the env array of only those tasks that actually reference process.env.TRIGGER_SECRET_KEY or process.env.TRIGGER_API_URL. I ran a repo-wide grep and found no code references outside turbo.json—please locate the real consumers and adjust accordingly.

Comment on lines +260 to +262
"BUILD_STANDALONE",
"TRIGGER_SECRET_KEY",
"TRIGGER_API_URL"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure env documentation and CI secrets are updated.

Add TRIGGER_SECRET_KEY and TRIGGER_API_URL to .env.example/.env.appStore.example, mask them in CI logs, and provision them in all deployment environments.

Suggested checks:


🏁 Script executed:

#!/bin/bash
# Verify examples include new vars
rg -n 'TRIGGER_SECRET_KEY|TRIGGER_API_URL' .env.example .env.appStore.example || echo "Missing in env examples"

# Spot references in CI configs (adjust paths if different)
fd -a -HI -t f -E node_modules -E .git -g '(*ci*|*.yml|*.yaml)' | xargs rg -n 'TRIGGER_SECRET_KEY|TRIGGER_API_URL' -n || echo "No CI references found (may be fine)"

Length of output: 334


Add new env vars to examples & CI

  • Insert TRIGGER_SECRET_KEY and TRIGGER_API_URL into .env.example and .env.appStore.example (currently missing).
  • Update your CI/YAML configs to mask these variables and reference them where needed.
  • Ensure both keys are provisioned in every deployment environment.
🤖 Prompt for AI Agents
In turbo.json around lines 260 to 262, the review requests adding the new
environment variables TRIGGER_SECRET_KEY and TRIGGER_API_URL into the example
env files and CI: update .env.example and .env.appStore.example to include these
two keys (with placeholder values or comments), update CI/YAML pipeline configs
to add and mask TRIGGER_SECRET_KEY and TRIGGER_API_URL as secret variables and
reference them in any steps that need triggering, and ensure deployment
manifests or environment provisioning scripts include these variables so every
environment (dev/staging/prod) has them set.

@github-actions
Copy link
Contributor

This PR is being marked as stale due to inactivity.

@github-actions github-actions bot added the Stale label Sep 24, 2025
@ThyMinimalDev
Copy link
Contributor Author

closing this PR, making a new one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

calendar-apps area: calendar, google calendar, outlook, lark, microsoft 365, apple calendar core area: core, team members only ✨ feature New feature or request foundation platform Anything related to our platform plan size/XXL Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants