Skip to content
Open
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
10 changes: 10 additions & 0 deletions .gemini/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"experimental": {
"extensionManagement": true
},
"mcp": {
"enabled": true,
"excluded": ["gemini-cloud-assist-mcp", "firebase", "julesServer", "securityServer", "osvScanner", "observability"]
}
}

3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ function_output.json
serverlog_stderr.txt
serverlog_stdout.txt
temp
test/conformance/package-lock.json
test/conformance/package-lock.json
firebase-debug.log
1 change: 1 addition & 0 deletions .guides/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
15 changes: 15 additions & 0 deletions .guides/docs/runFunctions.prompt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: run a function from a plain Node.js script
description: when to invoke a function from the server code instead of using the Functions Framework CLI
---
It is possible to serve a remote function directly from a plain script instead of the Functions Framework CLI.

The benefits include:

- Functions can be started with node directly: `node src/server.js`
- Furnctions can be used with node watcher without extra setup: `node watch src/server.js`
- Better Typescript support with ts-node/tsx: `tsx --watch src/server.ts`
- Easier debugging in IDEs because it is much easier to attach a debugger (like in VS Code or WebStorm) a plain Node.js script (node main.js) than to a command-line tool spawned via npx
- Custom initialization code because running code before server starts can be useful

When adding integration tests, choose this option so that you can easily stop the server instance within your test suite, rather than spawning a child process to run the CLI.
2 changes: 2 additions & 0 deletions .guides/usage.prompt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
This is a local development version of the package.

19 changes: 19 additions & 0 deletions .kiro/steering/product.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Product Overview

The Google Cloud Functions Framework for Node.js is an open-source FaaS (Function as a Service) framework built on Express.js for writing portable Node.js functions.

## Core Purpose
- Enables writing lightweight functions that run across multiple environments (Google Cloud Functions, Cloud Run, local development, Knative)
- Provides automatic request handling and event unmarshalling
- Supports multiple function signatures: HTTP, background events, and CloudEvents
- Eliminates the need to write HTTP server boilerplate

## Key Features
- Local development server for testing
- Automatic CloudEvents spec compliance
- Portable between serverless platforms
- Express.js-based request/response handling
- Support for Google Cloud Functions event payloads

## Target Users
Developers building serverless functions for Google Cloud Platform and other Knative-compatible environments.
57 changes: 57 additions & 0 deletions .kiro/steering/structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Project Structure

## Root Directory
- `package.json` - Main package configuration and dependencies
- `tsconfig.json` - TypeScript configuration extending gts
- `api-extractor.json` - API documentation extraction config
- `.eslintrc.json` - ESLint configuration extending gts
- `.prettierrc.js` - Prettier configuration via gts

## Source Code (`src/`)
- `index.ts` - Main public API exports
- `main.ts` - CLI entry point and server startup
- `functions.ts` - Core function registration and handling
- `function_registry.ts` - Function registration system
- `function_wrappers.ts` - Function signature adapters
- `server.ts` - Express server setup and configuration
- `invoker.ts` - Function invocation logic
- `loader.ts` - Dynamic function loading
- `types.ts` - TypeScript type definitions
- `options.ts` - Configuration and command-line options
- `logger.ts` - Logging utilities
- `testing.ts` - Testing utilities (exported separately)

### Middleware (`src/middleware/`)
- `background_event_to_cloud_event.ts` - Event format conversion
- `cloud_event_to_background_event.ts` - Reverse event conversion
- `timeout.ts` - Request timeout handling

### Other Core Files
- `cloud_events.ts` - CloudEvents specification handling
- `execution_context.ts` - Request execution context
- `async_local_storage.ts` - Async context management
- `pubsub_middleware.ts` - Pub/Sub specific middleware

## Testing (`test/`)
- Mirror structure of `src/` directory
- `integration/` - Integration tests for different function types
- `system-test/` - End-to-end system tests
- `conformance/` - Conformance test setup
- `data/` - Test fixtures and sample functions

## Build Output (`build/`)
- Generated JavaScript and type definitions
- Mirrors `src/` structure
- Main exports: `build/src/index.js` and `build/src/index.d.ts`

## Documentation (`docs/`)
- `generated/` - Auto-generated API documentation
- Various markdown guides for specific features
- `esm/` - ESM usage examples

## Conventions
- TypeScript source files in `src/`
- Corresponding test files in `test/` with same name
- Middleware components in dedicated subdirectory
- Integration tests grouped by function signature type
- All exports go through `src/index.ts` for clean public API
56 changes: 56 additions & 0 deletions .kiro/steering/tech.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Technology Stack

## Core Technologies
- **Language**: TypeScript/JavaScript (Node.js >=10.0.0)
- **Framework**: Express.js for HTTP handling
- **Build System**: TypeScript compiler with Google TypeScript Style (gts)
- **Testing**: Mocha test framework
- **Package Manager**: npm

## Key Dependencies
- `express` - HTTP server framework
- `cloudevents` - CloudEvents specification support
- `body-parser` - Request body parsing
- `minimist` - Command-line argument parsing

## Development Tools
- **Linting**: ESLint with Google TypeScript Style (gts)
- **Formatting**: Prettier (configured via gts)
- **Type Checking**: TypeScript 5.8.2
- **API Documentation**: Microsoft API Extractor

## Common Commands

### Development
```bash
npm run build # Clean, compile TypeScript
npm run compile # Compile TypeScript only
npm run watch # Compile with watch mode
npm run clean # Clean build directory
```

### Testing
```bash
npm test # Run unit tests
npm run conformance # Run conformance tests (requires Go 1.16+)
npm run pretest # Compile before testing
```

### Code Quality
```bash
npm run check # Run gts linting
npm run fix # Auto-fix linting issues
npm run docs # Generate API documentation
```

### Local Development
```bash
npx @google-cloud/functions-framework --target=functionName
functions-framework --target=functionName # If globally installed
```

## Build Output
- Compiled JavaScript: `build/src/`
- Type definitions: `build/src/**/*.d.ts`
- Main entry: `build/src/index.js`
- CLI binary: `build/src/main.js`
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ npm install @google-cloud/functions-framework
};
```

Option 1:

1. Run the following command:

```sh
Expand All @@ -76,6 +78,33 @@ npm install @google-cloud/functions-framework

1. Open http://localhost:8080/ in your browser and see _Hello, World_.

Option 2:

1. Create a `main.js` file with the following contents:

```js
import { run } from '@google-cloud/functions-framework';
import { helloWorld } from './hello.js';

run(helloWorld);
```

1. Run `node main.js` to start the local development server to serve the function:

```
Serving function...
Function: function
Signature type: http
URL: http://localhost:8080/
```

1. Send requests to this function using `curl` from another terminal window:

```sh
curl localhost:8080
# Output: Hello, World
```

### Quickstart: Set up a new project

1. Create a `package.json` file using `npm init`:
Expand Down
65 changes: 65 additions & 0 deletions docs/generated/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -1589,6 +1589,71 @@
"endIndex": 2
}
]
},
{
"kind": "Function",
"canonicalReference": "@google-cloud/functions-framework!run_2:function(1)",
"docComment": "/**\n * Main entrypoint for the functions framework that loads the user's function and starts the HTTP server.\n *\n * @param code - A function to be executed.\n */\n",
"excerptTokens": [
{
"kind": "Content",
"text": "main: (code?: "
},
{
"kind": "Reference",
"text": "HttpFunction",
"canonicalReference": "@google-cloud/functions-framework!HttpFunction:interface"
},
{
"kind": "Content",
"text": " | "
},
{
"kind": "Reference",
"text": "EventFunction",
"canonicalReference": "@google-cloud/functions-framework!EventFunction:interface"
},
{
"kind": "Content",
"text": " | "
},
{
"kind": "Reference",
"text": "CloudEventFunction",
"canonicalReference": "@google-cloud/functions-framework!CloudEventFunction:interface"
},
{
"kind": "Content",
"text": ") => "
},
{
"kind": "Reference",
"text": "Promise",
"canonicalReference": "!Promise:interface"
},
{
"kind": "Content",
"text": "<void>"
}
],
"fileUrlPath": "src/main.ts",
"returnTypeTokenRange": {
"startIndex": 7,
"endIndex": 9
},
"releaseTag": "Public",
"overloadIndex": 1,
"parameters": [
{
"parameterName": "code",
"parameterTypeTokenRange": {
"startIndex": 1,
"endIndex": 6
},
"isOptional": true
}
],
"name": "run_2"
}
]
}
Expand Down
4 changes: 4 additions & 0 deletions docs/generated/api.md.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export { Request_2 as Request }

export { Response_2 as Response }

// @public
const run_2: (code?: HttpFunction | EventFunction | CloudEventFunction) => Promise<void>;
export { run_2 as run }

// (No @packageDocumentation comment for this package)

```
2 changes: 1 addition & 1 deletion src/function_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const registrationContainer = new Map<string, RegisteredFunction<any>>();
/**
* Helper method to store a registered function in the registration container
*/
const register = <T = unknown, U = unknown>(
const register = <T = unknown>(
functionName: string,
signatureType: SignatureType,
userFunction: HandlerFunction<T>,
Expand Down
5 changes: 5 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ export * from './functions';
* @public
*/
export {http, cloudEvent} from './function_registry';

/**
* @public
*/
export {main as run} from './main';
38 changes: 30 additions & 8 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,22 @@ import {getUserFunction} from './loader';
import {ErrorHandler} from './invoker';
import {getServer} from './server';
import {parseOptions, helpText, OptionsError} from './options';
import {
HttpFunction,
EventFunction,
CloudEventFunction,
HandlerFunction,
} from './functions';
import {loggingHandlerAddExecutionContext} from './logger';

/**
* Main entrypoint for the functions framework that loads the user's function
* and starts the HTTP server.
* @param code - A function to be executed.
*/
export const main = async () => {
export const main = async (
code?: HttpFunction | EventFunction | CloudEventFunction,
) => {
try {
const options = parseOptions();

Expand All @@ -40,11 +49,21 @@ export const main = async () => {
loggingHandlerAddExecutionContext();
}

const loadedFunction = await getUserFunction(
options.sourceLocation,
options.target,
options.signatureType,
);
let loadedFunction;
// If a function is provided directly, use it.
if (code) {
loadedFunction = {
userFunction: code,
signatureType: options.signatureType || 'http',
};
} else {
// Otherwise, load the function from file.
loadedFunction = await getUserFunction(
options.sourceLocation,
options.target,
options.signatureType,
);
}
if (!loadedFunction) {
console.error('Could not load the function, shutting down.');
// eslint-disable-next-line no-process-exit
Expand All @@ -55,7 +74,7 @@ export const main = async () => {
// It is possible to overwrite the configured signature type in code so we
// reset it here based on what we loaded.
options.signatureType = signatureType;
const server = getServer(userFunction!, options);
const server = getServer(userFunction as HandlerFunction, options);
const errorHandler = new ErrorHandler(server);
server
.listen(options.port, () => {
Expand All @@ -79,4 +98,7 @@ export const main = async () => {
};

// Call the main method to load the user code and start the http server.
void main();
// Only call main if the module is not being required.
if (require.main === module) {
void main();
}
Loading
Loading