Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .agents/skills/create-firebase-function/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: |
Create Firebase Cloud Functions using effect-firebase library with type-safe Effect patterns.
Use when: (1) Creating callable functions (onCallEffect), (2) Creating HTTP endpoints (onRequestEffect),
(3) Creating Firestore triggers (onDocumentCreated/Updated/Deleted/Written), (4) Creating Pub/Sub handlers (onMessagePublishedEffect),
(5) Setting up function runtime, (6) Adding schema validation to functions.
(5) Creating scheduled functions (onScheduleEffect), (6) Setting up function runtime, (7) Adding schema validation to functions.
---

# Effect Firebase Functions
Expand Down Expand Up @@ -240,6 +240,25 @@ export const handleNotification = onMessagePublishedEffect(
);
```

### Scheduled Function

```typescript
import { onScheduleEffect } from '@effect-firebase/admin';

export const dailyCleanup = onScheduleEffect(
{
runtime,
schedule: 'every 24 hours',
timeZone: 'Europe/Copenhagen', // Optional
},
(event) =>
Effect.gen(function* () {
// event.jobName and event.scheduleTime available
yield* Effect.log(`Cleanup triggered at ${event.scheduleTime}`);
})
);
```

## Best Practices

1. **Single runtime**: Share one runtime across all functions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
3. [onRequestEffect](#onrequesteffect)
4. [Firestore Triggers](#firestore-triggers)
5. [Pub/Sub](#pubsub)
6. [Context Types](#context-types)
7. [Error Handling](#error-handling)
6. [Scheduler](#scheduler)
7. [Context Types](#context-types)
8. [Error Handling](#error-handling)

---

Expand Down Expand Up @@ -347,6 +348,45 @@ interface PubSubOptions {

---

## Scheduler

### onScheduleEffect

```typescript
function onScheduleEffect<R, E>(
options: {
runtime: Runtime<R>;
schedule: string;
} & ScheduleOptions,
handler: (event: ScheduledEvent) => Effect.Effect<void, E, R>
): ScheduleFunction;
```

### ScheduleOptions (from firebase-functions)

```typescript
interface ScheduleOptions {
schedule: string; // Unix Crontab or AppEngine syntax
timeZone?: string;
retryCount?: number;
maxRetrySeconds?: number;
minBackoffSeconds?: number;
maxBackoffSeconds?: number;
maxDoublings?: number;
}
```

### ScheduledEvent

```typescript
interface ScheduledEvent {
jobName?: string; // Cloud Scheduler job name (undefined when invoked manually)
scheduleTime: string; // RFC3339 UTC schedule time
}
```

---

## Context Types

### CallableContext
Expand Down
11 changes: 11 additions & 0 deletions packages/admin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ export const processEmail = onTaskDispatchedEffect(
);
```

### Scheduled (`onSchedule`)

```typescript
import { onScheduleEffect } from '@effect-firebase/admin';

export const cleanup = onScheduleEffect(
{ runtime, schedule: 'every 24 hours' },
(event) => Effect.log(`Running cleanup job: ${event.jobName}`),
);
```

## Cloud Logging

`Admin.layer` automatically replaces the default Effect logger with one that writes structured logs to Cloud Logging:
Expand Down
1 change: 1 addition & 0 deletions packages/admin/src/lib/functions/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './on-document-deleted.js';
export * from './on-document-written.js';
export * from './on-message-published.js';
export * from './on-task-dispatched.js';
export * from './on-schedule.js';
38 changes: 38 additions & 0 deletions packages/admin/src/lib/functions/on-schedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Effect } from 'effect';
import {
onSchedule,
ScheduledEvent,
ScheduleFunction,
ScheduleOptions,
} from 'firebase-functions/v2/scheduler';
import { logger } from 'firebase-functions';
import { run, Runtime } from './run.js';

interface ScheduleEffectOptions<R> extends ScheduleOptions {
runtime: Runtime<R>;
}

/**
* Create a Firebase Functions scheduled trigger that runs an effect on a schedule.
*
* @param options - The options for the scheduled trigger including the schedule.
* @param handler - The handler function that runs the effect.
* @returns The Firebase Functions scheduled trigger.
*/
export function onScheduleEffect<R, E>(
options: ScheduleEffectOptions<R>,
handler: (event: ScheduledEvent) => Effect.Effect<void, E, R>,
): ScheduleFunction {
return onSchedule(options, async (event) => {
const effect = handler(event).pipe(Effect.withSpan('onScheduleEffect'));

await run(options.runtime, effect as Effect.Effect<void, never, R>).catch(
(error) => {
logger.error('Defect in onSchedule', {
inner: error,
stack: error instanceof Error ? error.stack : undefined,
});
},
);
});
}
Loading