feat: new cross-engine proxy support (#14)

* feat: new proxy package

* chore: update nx workspace

* chore: remove unused codegen lib

* chore: add swc-node as build dependencies

* chore: remove unused demo-app-e2e app

* chore: reorganize demo apps

* feat: working cross-engine proxy

* chore: update docs

* fix: readme file export

* chore: update nextjs lib

* chore: keep nextjs code
This commit is contained in:
Daniel Rochetti 2023-10-08 01:46:36 -07:00 committed by GitHub
parent f240e622d3
commit b79e51683b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 16496 additions and 11872 deletions

View File

@ -1,12 +1,19 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nrwl/nx"],
"plugins": ["@nx"],
"overrides": [
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error"
}
},
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
"@nrwl/nx/enforce-module-boundaries": [
"@nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
@ -23,12 +30,12 @@
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nrwl/nx/typescript"],
"extends": ["plugin:@nx/typescript"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nrwl/nx/javascript"],
"extends": ["plugin:@nx/javascript"],
"rules": {}
}
]

View File

@ -1,62 +1,76 @@
# The fal-serverless JS Client
# The fal.ai JS client
![@fal-ai/serverless-client npm package](https://img.shields.io/npm/v/@fal-ai/serverless-client?color=%237527D7&label=client&style=flat-square)
![@fal-ai/serverless-nextjs npm package](https://img.shields.io/npm/v/@fal-ai/serverless-nextjs?color=%237527D7&label=nextjs-proxy&style=flat-square)
![@fal-ai/serverless-proxy npm package](https://img.shields.io/npm/v/@fal-ai/serverless-proxy?color=%237527D7&label=proxy&style=flat-square)
![Build](https://img.shields.io/github/actions/workflow/status/fal-ai/serverless-js/build.yml?style=flat-square)
![License](https://img.shields.io/github/license/fal-ai/serverless-js?style=flat-square)
## About the project
## About the Project
The fal-serverless JS/TS Client is a powerful and easy-to-use JavaScript and TypeScript library that allows you to effortlessly integrate and run your fal serverless functions in your Web, Node.js and React Native applications.
The project is written in TypeScript, so developers get type-safety out-of-the-box.
The fal-serverless JavaScript/TypeScript Client is a robust and user-friendly library designed for seamless integration of fal serverless functions in Web, Node.js, and React Native applications. Developed in TypeScript, it provides developers with type safety right from the start.
## Getting Started
The serverless-js library is a client for the fal serverless Python functions. Check the [quickstart guide](https://fal.ai/docs) in order to create your functions.
The `serverless-js` library serves as a client for fal serverless Python functions. For guidance on creating your functions, refer to the [quickstart guide](https://fal.ai/docs).
### Library
### Client Library
The client library is designed as a lightweight layer on top of the platform standards, such as `fetch` and `WebSocket`, ensuring smooth integration with your existing codebase.
This client library is crafted as a lightweight layer atop platform standards like `fetch`. This ensures a hassle-free integration into your existing codebase. Moreover, it addresses platform disparities, guaranteeing flawless operation across various JavaScript runtimes.
It also handle platform differences, so it work seamlessly across different JS runtimes.
> **Note:**
> Ensure you've reviewed the [fal-serverless getting started guide](https://fal.ai/docs) to acquire your credentials and register your functions.
> **Note**
>
> Make sure you followed the [fal-serverless getting started](https://fal.ai/docs) so you get your credentials and register your functions.
1. First you need to configure your credentials:
1. Start by configuring your credentials:
```ts
import * as fal from '@fal-ai/serverless-js';
fal.config({
// can also be auto-configured using environment variables
credentials: "FAL_KEY_ID:FAL_KEY_SECRET",
// Can also be auto-configured using environment variables:
// Either a single FAL_KEY or a combination of FAL_KEY_ID and FAL_KEY_SECRET
credentials: 'FAL_KEY_ID:FAL_KEY_SECRET',
});
```
2. Get your function id and run it:
2. Retrieve your function id and execute it:
```ts
const result = await fal.run('my-function-id');
```
The result type depends on the result of your Python function, types are mapped to their equivalent types in JS.
The result's type is contingent upon your Python function's output. Types in Python are mapped to their corresponding types in JavaScript.
### The fal client proxy
Although the fal client is designed to work in any JS environment, including client-side, **it is not recommended** to store your credentials in your client source code. The common practice is to use your own server to serve as a proxy to serverless APIs. Luckily fal supports that out-of-the-box with plug-and-play proxy functions for the most common engines/framrworks.
For example, if you are using Next.js, you can:
1. Instal the proxy library `npm install --save @fal-ai/serverless-proxy`
2. Add the proxy as an API endpoint of your app, see an example here in [pages/api/\_fal/proxy.ts](https://github.com/fal-ai/serverless-js/blob/main/apps/demo-nextjs-app/pages/api/_fal/proxy.ts)
```ts
export { handler as default } from '@fal-ai/serverless-proxy/nextjs';
```
3. Configure the client to use the proxy:
```ts
import * as fal from '@fal-ai/serverless-js';
fal.config({
requestMiddleware: fal.withProxy({
targetUrl: '/api/_fal/proxy',
}),
});
```
4. Make sure your server has `FAL_KEY` as environment variable with a valid API Key. That's it! Now your client calls will route through your server proxy, so your credentials are protected.
See [libs/proxy](./libs/proxy/) for more details.
### The example Next.js app
You can find a minimal Next.js + fal application examples in [apps/demo-app/](https://github.com/fal-ai/serverless-js/tree/main/apps/demo-app).
You can find a minimal Next.js + fal application examples in [apps/demo-nextjs-app/](https://github.com/fal-ai/serverless-js/tree/main/apps/demo-nextjs-app).
1. Run `npm install` on the repository root.
2. Run `npx nx serve demo-app` to start the Next.js app.
#### The Next.js fal proxy
The Next.js + fal integration provides you with a proxy that allows you to run your functions directly from the browser without exposing your fal credentials.
1. Instal it with `npm install --save @fal-ai/serverless-nextjs`
2. Add the proxy as an API endpoint of your app, see an example here in [apps/demo-app/pages/api/_fal/proxy.ts](https://github.com/fal-ai/serverless-js/blob/main/apps/demo-app/pages/api/_fal/proxy.ts)
2. Create a `.env.local` file and add your API Key as `FAL_KEY` environment variable (or export it any other way your prefer).
3. Run `npx nx serve demo-nextjs-app` to start the Next.js app.
## Roadmap

View File

@ -1,17 +0,0 @@
{
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["src/plugins/index.js"],
"rules": {
"@typescript-eslint/no-var-requires": "off",
"no-undef": "off"
}
}
]
}

View File

@ -1,6 +0,0 @@
import { defineConfig } from 'cypress';
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
export default defineConfig({
e2e: nxE2EPreset(__dirname),
});

View File

@ -1,30 +0,0 @@
{
"name": "demo-app-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/demo-app-e2e/src",
"projectType": "application",
"targets": {
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/demo-app-e2e/cypress.config.ts",
"devServerTarget": "demo-app:serve:development",
"testingType": "e2e"
},
"configurations": {
"production": {
"devServerTarget": "demo-app:serve:production"
}
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/demo-app-e2e/**/*.{js,ts}"]
}
}
},
"tags": [],
"implicitDependencies": ["demo-app"]
}

View File

@ -1,13 +0,0 @@
import { getGreeting } from '../support/app.po';
describe('demo-app', () => {
beforeEach(() => cy.visit('/'));
it('should display welcome message', () => {
// Custom command example, see `../support/commands.ts` file
cy.login('my-email@something.com', 'myPassword');
// Function helper example, see `../support/app.po.ts` file
getGreeting().contains('Welcome demo-app');
});
});

View File

@ -1,4 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io"
}

View File

@ -1 +0,0 @@
export const getGreeting = () => cy.get('h1');

View File

@ -1,33 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
// eslint-disable-next-line @typescript-eslint/no-namespace
declare namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
login(email: string, password: string): void;
}
}
//
// -- This is a parent command --
Cypress.Commands.add('login', (email, password) => {
console.log('Custom command example: Login', email, password);
});
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })

View File

@ -1,17 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands';

View File

@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"sourceMap": false,
"outDir": "../../dist/out-tsc",
"allowJs": true,
"types": ["cypress", "node"]
},
"include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"]
}

View File

@ -1,11 +0,0 @@
/* eslint-disable */
export default {
displayName: 'demo-app',
preset: '../../jest.preset.js',
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/apps/demo-app',
};

View File

@ -1,3 +0,0 @@
// @snippet:start("client.proxy.nextjs")
export { config, handler as default } from '@fal-ai/serverless-nextjs';
// @snippet:end

View File

@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'demo-express-app',
preset: '../../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/demo-express-app',
};

View File

@ -0,0 +1,64 @@
{
"name": "demo-express-app",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/demo-express-app/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"target": "node",
"compiler": "tsc",
"outputPath": "dist/apps/demo-express-app",
"main": "apps/demo-express-app/src/main.ts",
"tsConfig": "apps/demo-express-app/tsconfig.app.json",
"assets": ["apps/demo-express-app/src/assets"],
"isolatedConfig": true,
"webpackConfig": "apps/demo-express-app/webpack.config.js"
},
"configurations": {
"development": {},
"production": {}
}
},
"serve": {
"executor": "@nx/js:node",
"defaultConfiguration": "development",
"options": {
"buildTarget": "demo-express-app:build"
},
"configurations": {
"development": {
"buildTarget": "demo-express-app:build:development"
},
"production": {
"buildTarget": "demo-express-app:build:production"
}
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/demo-express-app/**/*.ts"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/demo-express-app/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
},
"tags": []
}

View File

@ -0,0 +1,32 @@
/**
* This is not a production server yet!
* This is only a minimal backend to get started.
*/
import * as falProxy from '@fal-ai/serverless-proxy/express';
import cors from 'cors';
import { configDotenv } from 'dotenv';
import express from 'express';
import * as path from 'path';
configDotenv({ path: './env.local' });
const app = express();
// Middlewares
app.use('/assets', express.static(path.join(__dirname, 'assets')));
app.use(express.json());
// fal.ai client proxy
app.all(falProxy.route, cors(), falProxy.handler);
// Your API endpoints
app.get('/api', (req, res) => {
res.send({ message: 'Welcome to demo-express-app!' });
});
const port = process.env.PORT || 3333;
const server = app.listen(port, () => {
console.log(`Listening at http://localhost:${port}/api`);
});
server.on('error', console.error);

View File

@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["node", "express"]
},
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
"include": ["src/**/*.ts"]
}

View File

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
],
"compilerOptions": {
"esModuleInterop": true
}
}

View File

@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node"]
},
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

View File

@ -0,0 +1,8 @@
const { composePlugins, withNx } = require('@nx/webpack');
// Nx plugins for webpack.
module.exports = composePlugins(withNx(), (config) => {
// Update the webpack config as needed here.
// e.g. `config.plugins.push(new MyPlugin())`
return config;
});

View File

@ -1,6 +1,6 @@
{
"extends": [
"plugin:@nrwl/nx/react-typescript",
"plugin:@nx/react-typescript",
"next",
"next/core-web-vitals",
"../../.eslintrc.json"

View File

@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'demo-nextjs-app',
preset: '../../jest.preset.js',
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/apps/demo-nextjs-app',
};

View File

@ -1,10 +1,10 @@
//@ts-check
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { withNx } = require('@nrwl/next/plugins/with-nx');
const { withNx } = require('@nx/next/plugins/with-nx');
/**
* @type {import('@nrwl/next/plugins/with-nx').WithNxOptions}
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
**/
const nextConfig = {
nx: {

View File

@ -0,0 +1,3 @@
// @snippet:start("client.proxy.nextjs")
export { handler as default } from '@fal-ai/serverless-proxy/nextjs';
// @snippet:end

View File

@ -1,10 +1,12 @@
import * as fal from '@fal-ai/serverless-client';
import { withNextProxy } from '@fal-ai/serverless-nextjs';
import { useMemo, useState } from 'react';
// @snippet:start(client.config)
fal.config({
requestMiddleware: withNextProxy(),
requestMiddleware: fal.withProxy({
targetUrl: '/api/_fal/proxy', // the built-int nextjs proxy
// targetUrl: 'http://localhost:3333/api/_fal/proxy', // or your own external proxy
}),
});
// @snippet:end

View File

@ -1,61 +1,60 @@
{
"name": "demo-app",
"name": "demo-nextjs-app",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/demo-app",
"sourceRoot": "apps/demo-nextjs-app",
"projectType": "application",
"targets": {
"build": {
"executor": "@nrwl/next:build",
"executor": "@nx/next:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"root": "apps/demo-app",
"outputPath": "dist/apps/demo-app"
"outputPath": "dist/apps/demo-nextjs-app"
},
"configurations": {
"development": {
"outputPath": "apps/demo-app"
"outputPath": "apps/demo-nextjs-app"
},
"production": {}
}
},
"serve": {
"executor": "@nrwl/next:server",
"executor": "@nx/next:server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "demo-app:build",
"buildTarget": "demo-nextjs-app:build",
"dev": true
},
"configurations": {
"development": {
"buildTarget": "demo-app:build:development",
"buildTarget": "demo-nextjs-app:build:development",
"dev": true
},
"production": {
"buildTarget": "demo-app:build:production",
"buildTarget": "demo-nextjs-app:build:production",
"dev": false
}
}
},
"export": {
"executor": "@nrwl/next:export",
"executor": "@nx/next:export",
"options": {
"buildTarget": "demo-app:build:production"
"buildTarget": "demo-nextjs-app:build:production"
}
},
"test": {
"executor": "@nrwl/jest:jest",
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "apps/demo-app/jest.config.ts",
"jestConfig": "apps/demo-nextjs-app/jest.config.ts",
"passWithNoTests": true
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/demo-app/**/*.{ts,tsx,js,jsx}"]
"lintFilePatterns": ["apps/demo-nextjs-app/**/*.{ts,tsx,js,jsx}"]
}
}
},

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,4 +1,4 @@
const { createGlobPatternsForDependencies } = require('@nrwl/react/tailwind');
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
const { join } = require('path');
/** @type {import('tailwindcss').Config} */

View File

@ -1,4 +1,4 @@
import { getJestProjects } from '@nrwl/jest';
import { getJestProjects } from '@nx/jest';
export default {
projects: getJestProjects(),

View File

@ -1,3 +1,15 @@
const nxPreset = require('@nrwl/jest/preset').default;
const nxPreset = require('@nx/jest/preset').default;
module.exports = { ...nxPreset };
module.exports = {
...nxPreset,
/* TODO: Update to latest Jest snapshotFormat
* By default Nx has kept the older style of Jest Snapshot formats
* to prevent breaking of any existing tests with snapshots.
* It's recommend you update to the latest format.
* You can do this by removing snapshotFormat property
* and running tests with --update-snapshot flag.
* Example: "nx affected --targets=test --update-snapshot"
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
*/
snapshotFormat: { escapeString: true, printBasicPrototype: true },
};

View File

@ -1,3 +1,3 @@
{
"presets": [["@nrwl/js/babel", { "useBuiltIns": "usage" }]]
"presets": [["@nx/js/babel", { "useBuiltIns": "usage" }]]
}

View File

@ -1,11 +1,52 @@
# client
# fal.ai JavaScript/TypeScript client library
This library was generated with [Nx](https://nx.dev).
![@fal-ai/serverless-client npm package](https://img.shields.io/npm/v/@fal-ai/serverless-client?color=%237527D7&label=%40fal-ai%2Fserverless-client&style=flat-square)
## Running unit tests
## Introduction
Run `nx test client` to execute the unit tests via [Jest](https://jestjs.io).
The `fal.ai` JavaScript Client Library provides a seamless way to interact with `fal` serverless functions from your JavaScript or TypeScript applications. With built-in support for various platforms, it ensures consistent behavior across web, Node.js, and React Native environments.
## Running lint
## Getting started
Run `nx lint client` to execute the lint via [ESLint](https://eslint.org/).
Before diving into the client-specific features, ensure you've set up your credentials:
```ts
import * as fal from '@fal-ai/serverless-js';
fal.config({
// Can also be auto-configured using environment variables:
// Either a single FAL_KEY or a combination of FAL_KEY_ID and FAL_KEY_SECRET
credentials: 'FAL_KEY_ID:FAL_KEY_SECRET',
});
```
**Note:** Ensure you've reviewed the [fal.ai getting started guide](https://fal.ai/docs) to acquire your credentials and register your functions. Also, make sure your credentials are always protected. See the [../proxy](../proxy) package for a secure way to use the client in client-side applications.
## Running functions with `fal.run`
The `fal.run` method is the simplest way to execute a function. It returns a promise that resolves to the function's result:
```ts
const result = await fal.run('my-function-id', {
input: { foo: 'bar' },
});
```
## Long-running functions with `fal.subscribe`
The `fal.subscribe` method offers a powerful way to rely on the [queue system](https://www.fal.ai/docs/function-endpoints/queue) to execute long-running functions. It returns the result once it's done like any other async function, so your don't have to deal with queue status updates yourself. However, it does support queue events, in case you want to listen and react to them:
```ts
const result = await fal.subscribe('my-function-id', {
input: { foo: 'bar' },
onQueueUpdate(update) {
if (update.status === 'IN_QUEUE') {
console.log(`Your position in the queue is ${update.position}`);
}
},
});
```
## More features
The client library offers a plethora of features designed to simplify your serverless journey with `fal.ai`. Dive into the [official documentation](https://fal.ai/docs) for a comprehensive guide.

View File

@ -2,14 +2,15 @@
export default {
displayName: 'client',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
globals: {},
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
'^.+\\.[tj]sx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
},
],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/client',

View File

@ -1,7 +1,7 @@
{
"name": "@fal-ai/serverless-client",
"description": "The fal serverless JS/TS client",
"version": "0.2.1",
"version": "0.3.1",
"license": "MIT",
"repository": {
"type": "git",

View File

@ -5,25 +5,25 @@
"projectType": "library",
"targets": {
"build": {
"executor": "@nrwl/js:tsc",
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/client",
"tsConfig": "libs/client/tsconfig.lib.json",
"packageJson": "libs/client/package.json",
"main": "libs/client/src/index.ts",
"assets": ["LICENSE", "CODE_OF_CONDUCT.md", "README.md"]
"assets": ["LICENSE", "CODE_OF_CONDUCT.md", "libs/client/README.md"]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/client/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/client/jest.config.ts",

View File

@ -102,8 +102,7 @@ export async function run<Input, Output>(
const response = await fetch(url, {
method,
headers: requestHeaders,
mode: 'same-origin',
credentials: 'same-origin',
mode: 'cors',
body:
method !== 'get' && options.input
? JSON.stringify(options.input)

View File

@ -1,7 +1,7 @@
export { config, getConfig } from './config';
export { queue, run, subscribe } from './function';
export { withMiddleware } from './middleware';
export { ApiError, ValidationError } from './response';
export { withMiddleware, withProxy } from './middleware';
export type { RequestMiddleware } from './middleware';
export { ApiError, ValidationError } from './response';
export type { ResponseHandler } from './response';
export type { QueueStatus } from './types';

View File

@ -32,3 +32,25 @@ export function withMiddleware(
Promise.resolve(config)
);
}
export type RequestProxyConfig = {
targetUrl: string;
};
export const TARGET_URL_HEADER = 'x-fal-target-url';
export function withProxy(config: RequestProxyConfig): RequestMiddleware {
// when running on the server, we don't need to proxy the request
if (typeof window === 'undefined') {
return (requestConfig) => Promise.resolve(requestConfig);
}
return (requestConfig) =>
Promise.resolve({
...requestConfig,
url: config.targetUrl,
headers: {
...(requestConfig.headers || {}),
[TARGET_URL_HEADER]: requestConfig.url,
},
});
}

View File

@ -1,3 +0,0 @@
{
"presets": [["@nrwl/js/babel", { "useBuiltIns": "usage" }]]
}

View File

@ -1,11 +0,0 @@
# codegen
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test codegen` to execute the unit tests via [Jest](https://jestjs.io).
## Running lint
Run `nx lint codegen` to execute the lint via [ESLint](https://eslint.org/).

View File

@ -1,23 +0,0 @@
#!/usr/bin/env node
const oclif = require("@oclif/core");
const path = require("path");
// Get project config
const project = path.join(__dirname, "..", "tsconfig.lib.json");
process.env.TS_NODE_PROJECT = project;
// In dev mode -> use ts-node and dev plugins
process.env.NODE_ENV = "development";
// Register ts-node and path mapping
require("ts-node").register({ project, esm: true });
require("tsconfig-paths").register();
// In dev mode, always show stack traces
oclif.settings.debug = true;
// Start the CLI
oclif.run()
.then(oclif.flush)
.catch(oclif.Errors.handle);

View File

@ -1,8 +0,0 @@
#!/usr/bin/env node
const oclif = require("@oclif/core");
oclif
.run()
.then(require("@oclif/core/flush"))
.catch(require("@oclif/core/handle"));

View File

@ -1,16 +0,0 @@
/* eslint-disable */
export default {
displayName: 'codegen',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/codegen',
};

View File

@ -1,27 +0,0 @@
{
"name": "@fal-ai/serverless-codegen",
"version": "0.0.1",
"bin": {
"ksjs": "./bin/run"
},
"files": [
"/bin",
"/dist",
"/npm-shrinkwrap.json",
"/oclif.manifest.json"
],
"oclif": {
"bin": "ksjs",
"dirname": "ksjs",
"commands": "./src/commands",
"plugins": [
"@oclif/plugin-help"
],
"topicSeparator": ":",
"topics": {
"generate": {
"description": "Command used to generate source files based on fal serverless metadata."
}
}
}
}

View File

@ -1,95 +0,0 @@
import { Command, Flags } from '@oclif/core';
import { camelCase, paramCase } from 'change-case';
import { execSync } from 'child_process';
import { watch as watchFiles } from 'chokidar';
import * as glob from 'fast-glob';
import { ensureDir, readJSONSync, writeFileSync } from 'fs-extra';
import * as path from 'path';
import { generateFunction } from '../../generators/generateFunction';
import { IsolateFunctionMetadata } from '../../types';
export default class GenerateFunctionCommand extends Command {
static description =
'Generate fal serverless functions from a path containing Python files';
// static examples = ['$ ksjs generate:functions'];
static flags = {
include: Flags.string({
char: 'i',
description: 'A path pattern (accepts pattern matching/glob)',
required: true,
}),
out: Flags.string({
char: 'o',
description: 'The output directory',
required: true,
}),
python: Flags.string({
char: 'p',
description: 'Path to a python environment',
required: false,
default: null,
}),
watch: Flags.boolean({
char: 'w',
description: 'Watch for changes and regenerate code',
required: false,
default: false,
}),
};
async run(): Promise<void> {
const { flags } = await this.parse(GenerateFunctionCommand);
const { include, out, python, watch } = flags;
const commands: string[] = [];
if (python !== null) {
commands.push('source ' + python + '/bin/activate');
}
const tmp = 'tmp/.fal/serverless/generated';
await ensureDir(tmp);
commands.push(
`koldstart generate metadata --include="${include}" --out="${tmp}"`
);
const executeCommand = () => {
execSync(commands.join(' && '), {
shell: 'bash',
});
};
const generateFiles = async () => {
// NOTE: this can be improved, a lot!
console.log('Generating files...');
const metadataFiles = await glob(`${tmp}/**/*.json`);
for (const metadataFile of metadataFiles) {
const metadata = readJSONSync(metadataFile) as IsolateFunctionMetadata;
const sourceCode = generateFunction(metadata);
const folder = paramCase(
path.parse(path.relative(tmp, metadataFile)).dir
);
const filename = `${camelCase(metadata.name)}.ts`;
const outputPath = path.join(out, folder);
await ensureDir(outputPath);
writeFileSync(path.join(outputPath, filename), sourceCode);
}
};
executeCommand();
if (watch) {
const watcher = watchFiles(include);
watcher.on('all', async () => {
// TODO update only changed files
executeCommand();
await generateFiles();
});
} else {
await generateFiles();
}
return;
}
}

View File

@ -1,106 +0,0 @@
import { camelCase, pascalCase } from 'change-case';
import * as prettier from 'prettier';
import {
FunctionDeclarationStructure,
ImportDeclarationStructure,
InterfaceDeclarationStructure,
ParameterDeclarationStructure,
Project,
PropertySignatureStructure,
SourceFileStructure,
StatementStructures,
StructureKind,
} from 'ts-morph';
import { IsolateFunctionMetadata, IsolateFunctionParameter } from '../types';
const CORE_TYPES = {
str: 'string',
} as const;
function generateProperty(
param: IsolateFunctionParameter
): PropertySignatureStructure {
return {
kind: StructureKind.PropertySignature,
name: param.name,
isReadonly: true,
type: CORE_TYPES[param.type],
hasQuestionToken: !param.is_required,
};
}
export function generateFunction(metadata: IsolateFunctionMetadata): string {
const identifier = camelCase(metadata.name);
const typename = pascalCase(metadata.name);
const members: StatementStructures[] = [];
members.push({
kind: StructureKind.ImportDeclaration,
namedImports: ['run'],
moduleSpecifier: '@fal-ai/serverless-client',
} as ImportDeclarationStructure);
members.push({
kind: StructureKind.ImportDeclaration,
namedImports: ['credentials'],
moduleSpecifier: '../../credentials',
} as ImportDeclarationStructure);
const inputTypename = `${typename}Input`;
const parameters: ParameterDeclarationStructure[] = [];
if (metadata.parameters && metadata.parameters.length > 0) {
const inputType = {
kind: StructureKind.Interface,
name: inputTypename,
properties: metadata.parameters.map(generateProperty),
} as InterfaceDeclarationStructure;
members.push(inputType);
parameters.push({
kind: StructureKind.Parameter,
name: 'input',
type: inputTypename,
});
}
const onData: ParameterDeclarationStructure = {
kind: StructureKind.Parameter,
name: 'onData',
type: '(data: string) => void',
};
parameters.push(onData);
const isolatedFunction: FunctionDeclarationStructure = {
kind: StructureKind.Function,
isExported: true,
name: identifier,
parameters: parameters,
// returnType: metadata.return_type
// ? CORE_TYPES[metadata.return_type]
// : undefined,
statements: (writer) => {
writer.write('return run(').block(() => {
writer.writeLine(`host: '${metadata.config.host}'`);
writer.writeLine('credentials');
writer.writeLine(`environmentKind: '${metadata.config.env_kind}'`);
writer.writeLine(
`requirements: ${JSON.stringify(metadata.config.requirements)}`
);
writer.writeLine(`definition: '${metadata.definition}'`);
});
writer.writeLine(', onData);');
},
};
members.push(isolatedFunction);
const project = new Project();
const source: SourceFileStructure = {
kind: StructureKind.SourceFile,
statements: members,
};
const filepath = `src/${identifier}.ts`;
const file = project.createSourceFile(filepath, source);
return prettier.format(file.print(), {
filepath,
});
}

View File

@ -1 +0,0 @@
export { run } from '@oclif/core';

View File

@ -1,19 +0,0 @@
export type IsolateFunctionConfig = {
env_kind: string;
requirements: string[];
host: string;
};
export type IsolateFunctionParameter = {
name: string;
is_required: boolean;
type: string;
};
export type IsolateFunctionMetadata = {
name: string;
parameters: IsolateFunctionParameter[];
return_type: string;
definition: string;
config: IsolateFunctionConfig;
};

View File

@ -1,3 +1,3 @@
{
"presets": [["@nrwl/js/babel", { "useBuiltIns": "usage" }]]
"presets": [["@nx/js/babel", { "useBuiltIns": "usage" }]]
}

View File

@ -1,11 +1,3 @@
# nextjs
# @fal-ai/serverless-nextjs
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test nextjs` to execute the unit tests via [Jest](https://jestjs.io).
## Running lint
Run `nx lint nextjs` to execute the lint via [ESLint](https://eslint.org/).
This package is not longer maintained. Check out the [@fal-ai/serverless-proxy](../proxy/)package, which supports Next.js and other Express-based frameworks.

View File

@ -2,14 +2,15 @@
export default {
displayName: 'nextjs',
preset: '../../jest.preset.js',
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.spec.json',
},
},
globals: {},
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': 'ts-jest',
'^.+\\.[tj]sx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
},
],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/nextjs',

View File

@ -1,7 +1,7 @@
{
"name": "@fal-ai/serverless-nextjs",
"description": "The fal-serverless Next.js integration",
"version": "0.2.3",
"version": "0.2.4",
"license": "MIT",
"repository": {
"type": "git",

View File

@ -5,25 +5,25 @@
"projectType": "library",
"targets": {
"build": {
"executor": "@nrwl/js:tsc",
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/nextjs",
"tsConfig": "libs/nextjs/tsconfig.lib.json",
"packageJson": "libs/nextjs/package.json",
"main": "libs/nextjs/src/index.ts",
"assets": ["libs/nextjs/*.md"]
"assets": ["LICENSE", "CODE_OF_CONDUCT.md", "libs/nextjs/README.md"]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/nextjs/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/nextjs/jest.config.ts",

3
libs/proxy/.babelrc Normal file
View File

@ -0,0 +1,3 @@
{
"presets": [["@nx/js/babel", { "useBuiltIns": "usage" }]]
}

18
libs/proxy/.eslintrc.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

64
libs/proxy/README.md Normal file
View File

@ -0,0 +1,64 @@
# fal.ai proxy library
![@fal-ai/serverless-proxy npm package](https://img.shields.io/npm/v/@fal-ai/serverless-proxy?color=%237527D7&label=%40fal-ai%2Fserverless-proxy&style=flat-square)
## Introduction
The `@fal-ai/serverless-proxy` library enables you to route client requests through your own server, therefore safeguarding sensitive credentials. With built-in support for popular frameworks like Next.js and Express, setting up the proxy becomes a breeze.
### Install the proxy library:
```
npm install --save @fal-ai/serverless-proxy
```
## Next.js integration
1. Create an API route in your Next.js app, as a convention we suggest using `pages/api/_fal/proxy.js` (or `.ts` if you're using TypeScript):
2. Re-export the proxy handler from the library as the default export:
```ts
export { handler as default } from '@fal-ai/serverless-proxy/nextjs';
```
3. Ensure you've set the `FAL_KEY` as an environment variable in your server, containing a valid API Key.
## Express integration
For Express applications:
1. Make sure your app supports JSON payloads, either by using `express.json()` (recommended) or `body-parser`:
```ts
app.use(express.json());
```
2. Add the proxy route and its handler. Note that if your client lives outside of the express app (i.e. the express app is solely used as an external API for other clients), your will need to allow CORS on the proxy route:
```ts
import * as falProxy from '@fal-ai/serverless-proxy/express';
app.all(
falProxy.route, // '/api/_fal/proxy' or you can use your own
cors(), // if external clients will use the proxy
falProxy.handler
);
```
3. Ensure you've set the `FAL_KEY` as an environment variable in your server, containing a valid API Key.
## Client configuration
Once you've set up the proxy, you can configure the client to use it:
```ts
import * as fal from '@fal-ai/serverless-js';
fal.config({
requestMiddleware: fal.withProxy({
targetUrl: '/api/_fal/proxy', // or https://my.app.com/api/_fal/proxy
}),
});
```
Now all your client calls will route through your server proxy, so your credentials are protected.
## More information
For a deeper dive into the proxy library and its capabilities, explore the [official documentation](https://fal.ai/docs).

17
libs/proxy/jest.config.ts Normal file
View File

@ -0,0 +1,17 @@
/* eslint-disable */
export default {
displayName: 'proxy',
preset: '../../jest.preset.js',
globals: {},
testEnvironment: 'node',
transform: {
'^.+\\.[tj]sx?$': [
'ts-jest',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
},
],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/proxy',
};

44
libs/proxy/package.json Normal file
View File

@ -0,0 +1,44 @@
{
"name": "@fal-ai/serverless-proxy",
"version": "0.3.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/fal-ai/serverless-js.git",
"directory": "libs/proxy"
},
"keywords": [
"fal",
"serverless",
"client",
"next",
"nextjs",
"express",
"proxy"
],
"exports": {
".": "./src/index.js",
"./express": "./src/express.js",
"./nextjs": "./src/nextjs.js"
},
"peerDependencies": {
"express": "^4.0.0",
"next": "^13.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"express": {
"optional": true
},
"next": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
}

View File

@ -1,32 +1,32 @@
{
"name": "codegen",
"name": "proxy",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/codegen/src",
"sourceRoot": "libs/proxy/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nrwl/js:tsc",
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/codegen",
"tsConfig": "libs/codegen/tsconfig.lib.json",
"packageJson": "libs/codegen/package.json",
"main": "libs/codegen/src/index.ts",
"assets": ["libs/codegen/*.md"]
"outputPath": "dist/libs/proxy",
"tsConfig": "libs/proxy/tsconfig.lib.json",
"packageJson": "libs/proxy/package.json",
"main": "libs/proxy/src/index.ts",
"assets": ["LICENSE", "CODE_OF_CONDUCT.md", "libs/proxy/README.md"]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/codegen/**/*.ts"]
"lintFilePatterns": ["libs/proxy/**/*.ts"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/codegen/jest.config.ts",
"jestConfig": "libs/proxy/jest.config.ts",
"passWithNoTests": true
},
"configurations": {

31
libs/proxy/src/express.ts Normal file
View File

@ -0,0 +1,31 @@
import type { RequestHandler } from 'express';
import { DEFAULT_PROXY_ROUTE, handleRequest } from './index';
/**
* The default Express route for the fal.ai client proxy.
*/
export const route = DEFAULT_PROXY_ROUTE;
/**
* The Express route handler for the fal.ai client proxy.
*
* @param request The Express request object.
* @param response The Express response object.
* @param next The Express next function.
*/
export const handler: RequestHandler = async (request, response, next) => {
await handleRequest({
id: 'express',
method: request.method,
respondWith: (status, data) =>
typeof data === 'string'
? response.status(status).send(data)
: response.status(status).json(data),
getHeaders: () => request.headers,
getHeader: (name) => request.headers[name],
removeHeader: (name) => response.removeHeader(name),
sendHeader: (name, value) => response.setHeader(name, value),
getBody: () => JSON.stringify(request.body),
});
next();
};

125
libs/proxy/src/index.ts Normal file
View File

@ -0,0 +1,125 @@
export const TARGET_URL_HEADER = 'x-fal-target-url';
export const DEFAULT_PROXY_ROUTE = '/api/_fal/proxy';
const FAL_KEY = process.env.FAL_KEY || process.env.NEXT_PUBLIC_FAL_KEY;
const FAL_KEY_ID = process.env.FAL_KEY_ID || process.env.NEXT_PUBLIC_FAL_KEY_ID;
const FAL_KEY_SECRET =
process.env.FAL_KEY_SECRET || process.env.NEXT_PUBLIC_FAL_KEY_SECRET;
export interface ProxyBehavior {
id: string;
method: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
respondWith(status: number, data: string | any): void;
getHeaders(): Record<string, string | string[] | undefined>;
getHeader(name: string): string | string[] | undefined;
removeHeader(name: string): void;
sendHeader(name: string, value: string): void;
getBody(): string | undefined;
}
/**
* Utility to get a header value as `string` from a Headers object.
*
* @private
* @param request the header value.
* @returns the header value as `string` or `undefined` if the header is not set.
*/
function singleHeaderValue(
value: string | string[] | undefined
): string | undefined {
if (value === undefined) {
return undefined;
}
if (Array.isArray(value)) {
return value[0];
}
return value;
}
/**
* Clean up headers that should not be forwarded to the proxy.
* @param behavior The proxy implementation.
*/
function cleanUpHeaders(behavior: ProxyBehavior) {
behavior.removeHeader('origin');
behavior.removeHeader('referer');
behavior.removeHeader(TARGET_URL_HEADER);
}
function getFalKey(): string | undefined {
if (FAL_KEY) {
return FAL_KEY;
}
if (FAL_KEY_ID && FAL_KEY_SECRET) {
return `${FAL_KEY_ID}:${FAL_KEY_SECRET}`;
}
return undefined;
}
/**
* A Next request handler that proxies the request to the fal-serverless
* endpoint. This is useful so client-side calls to the fal-serverless endpoint
* can be made without CORS issues and the correct credentials can be added
* effortlessly.
*
* @param request the Next request object.
* @param response the Next response object.
* @returns Promise<any> the promise that will be resolved once the request is done.
*/
export const handleRequest = async (behavior: ProxyBehavior) => {
const targetUrl = singleHeaderValue(behavior.getHeader(TARGET_URL_HEADER));
if (!targetUrl) {
behavior.respondWith(400, `Missing the ${TARGET_URL_HEADER} header`);
return;
}
if (targetUrl.indexOf('fal.ai') === -1) {
behavior.respondWith(412, `Invalid ${TARGET_URL_HEADER} header`);
return;
}
cleanUpHeaders(behavior);
const falKey = getFalKey();
if (!falKey) {
behavior.respondWith(401, 'Missing fal.ai credentials');
return;
}
// pass over headers prefixed with x-fal-*
const headers: Record<string, string | string[] | undefined> = {};
Object.keys(behavior.getHeaders()).forEach((key) => {
if (key.toLowerCase().startsWith('x-fal-')) {
headers[key.toLowerCase()] = behavior.getHeader(key);
}
});
const res = await fetch(targetUrl, {
method: behavior.method,
headers: {
...headers,
authorization:
singleHeaderValue(behavior.getHeader('authorization')) ??
`Key ${falKey}`,
accept: 'application/json',
'content-type': 'application/json',
'x-fal-client-proxy': `@fal-ai/serverless-proxy/${behavior.id}`,
},
body:
behavior.method?.toUpperCase() === 'GET' ? undefined : behavior.getBody(),
});
// copy headers from res to response
res.headers.forEach((value, key) => {
behavior.sendHeader(key, value);
});
if (res.headers.get('content-type') === 'application/json') {
const data = await res.json();
behavior.respondWith(res.status, data);
return;
}
const data = await res.text();
behavior.respondWith(res.status, data);
};

30
libs/proxy/src/nextjs.ts Normal file
View File

@ -0,0 +1,30 @@
import type { NextApiHandler } from 'next/types';
import { DEFAULT_PROXY_ROUTE, handleRequest } from './index';
/**
* The default Next API route for the fal.ai client proxy.
*/
export const PROXY_ROUTE = DEFAULT_PROXY_ROUTE;
/**
* The Next API route handler for the fal.ai client proxy.
*
* @param request the Next API request object.
* @param response the Next API response object.
* @returns a promise that resolves when the request is handled.
*/
export const handler: NextApiHandler = async (request, response) => {
return handleRequest({
id: 'nextjs',
method: request.method,
respondWith: (status, data) =>
typeof data === 'string'
? response.status(status).send(data)
: response.status(status).json(data),
getHeaders: () => request.headers,
getHeader: (name) => request.headers[name],
removeHeader: (name) => response.removeHeader(name),
sendHeader: (name, value) => response.setHeader(name, value),
getBody: () => JSON.stringify(request.body),
});
};

View File

@ -1,60 +1,331 @@
{
"migrations": [
{
"version": "15.7.0-beta.0",
"description": "Split global configuration files into individual project.json files. This migration has been added automatically to the beginning of your migration set to retroactively make them work with the new version of Nx.",
"cli": "nx",
"implementation": "./src/migrations/update-15-7-0/split-configuration-into-project-json-files",
"package": "@nrwl/workspace",
"name": "15-7-0-split-configuration-into-project-json-files"
"version": "15.8.2-beta.0",
"description": "Updates the nx wrapper.",
"implementation": "./src/migrations/update-15-8-2/update-nxw",
"package": "nx",
"name": "15.8.2-update-nx-wrapper"
},
{
"cli": "nx",
"version": "15.5.0-beta.0",
"description": "Update to Cypress v12. Cypress 12 contains a handful of breaking changes that might causes tests to start failing that nx cannot directly fix. Read more Cypress 12 changes: https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-12-0.This migration will only run if you are already using Cypress v11.",
"factory": "./src/migrations/update-15-5-0/update-to-cypress-12",
"package": "@nrwl/cypress",
"name": "update-to-cypress-12"
},
{
"version": "15.7.0-beta.0",
"description": "Split global configuration files (e.g., workspace.json) into individual project.json files.",
"cli": "nx",
"implementation": "./src/migrations/update-15-7-0/split-configuration-into-project-json-files",
"package": "@nrwl/workspace",
"name": "15-7-0-split-configuration-into-project-json-files"
"version": "16.0.0-beta.0",
"description": "Remove @nrwl/cli.",
"implementation": "./src/migrations/update-16-0-0/remove-nrwl-cli",
"package": "nx",
"name": "16.0.0-remove-nrwl-cli"
},
{
"cli": "nx",
"version": "15.3.0-beta.0",
"description": "Update projects using @nrwl/web:rollup to @nrwl/rollup:rollup for build.",
"factory": "./src/migrations/update-15-3-0/update-rollup-executor",
"package": "@nrwl/react",
"name": "update-rollup-executor"
"version": "16.0.0-beta.9",
"description": "Replace `dependsOn.projects` and `inputs` definitions with new configuration format.",
"implementation": "./src/migrations/update-16-0-0/update-depends-on-to-tokens",
"package": "nx",
"name": "16.0.0-tokens-for-depends-on"
},
{
"cli": "nx",
"version": "15.3.0-beta.0",
"description": "Install new dependencies for React projects using Webpack or Rollup.",
"factory": "./src/migrations/update-15-3-0/install-webpack-rollup-dependencies",
"package": "@nrwl/react",
"name": "install-webpack-rollup-dependencies"
"version": "16.0.0-beta.0",
"description": "Replace @nrwl/nx-cloud with nx-cloud",
"implementation": "./src/migrations/update-16-0-0/update-nx-cloud-runner",
"package": "nx",
"name": "16.0.0-update-nx-cloud-runner"
},
{
"cli": "nx",
"version": "15.6.3-beta.0",
"description": "Creates or updates webpack.config.js file with the new options for webpack.",
"factory": "./src/migrations/update-15-6-3/webpack-config-setup",
"package": "@nrwl/react",
"name": "react-webpack-config-setup"
"version": "16.2.0-beta.0",
"description": "Remove outputPath from run commands",
"implementation": "./src/migrations/update-16-2-0/remove-run-commands-output-path",
"package": "nx",
"name": "16.2.0-remove-output-path-from-run-commands"
},
{
"cli": "nx",
"version": "15.5.4-beta.0",
"description": "Update `@nrwl/web/babel` preset to `@nrwl/js/babel` for projects that have a .babelrc file.",
"factory": "./src/migrations/update-15-5-4/update-babel-preset",
"package": "@nrwl/web",
"name": "update-babel-preset"
"version": "16.6.0-beta.6",
"description": "Prefix outputs with {workspaceRoot}/{projectRoot} if needed",
"implementation": "./src/migrations/update-15-0-0/prefix-outputs",
"package": "nx",
"name": "16.6.0-prefix-outputs"
},
{
"cli": "nx",
"version": "16.8.0-beta.3",
"description": "Escape $ in env variables",
"implementation": "./src/migrations/update-16-8-0/escape-dollar-sign-env-variables",
"package": "nx",
"name": "16.8.0-escape-dollar-sign-env"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/workspace with @nx/workspace",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/workspace",
"name": "update-16-0-0-add-nx-packages"
},
{
"version": "16.0.0-beta.4",
"description": "Generates a plugin called 'workspace-plugin' containing your workspace generators.",
"cli": "nx",
"implementation": "./src/migrations/update-16-0-0/move-workspace-generators-to-local-plugin",
"package": "@nx/workspace",
"name": "16-0-0-move-workspace-generators-into-local-plugin"
},
{
"version": "16.0.0-beta.9",
"description": "Fix .babelrc presets if it contains an invalid entry for @nx/web/babel.",
"cli": "nx",
"implementation": "./src/migrations/update-16-0-0/fix-invalid-babelrc",
"package": "@nx/workspace",
"name": "16-0-0-fix-invalid-babelrc"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/linter with @nx/linter",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/linter",
"name": "update-16-0-0-add-nx-packages"
},
{
"version": "16.8.0",
"description": "update-16-8-0-add-ignored-files",
"implementation": "./src/migrations/update-16-8-0-add-ignored-files/update-16-8-0-add-ignored-files",
"package": "@nx/linter",
"name": "update-16-8-0-add-ignored-files"
},
{
"cli": "nx",
"version": "15.8.0-beta.0",
"description": "Rename .lib.swcrc to .swcrc for better SWC support throughout the workspace",
"factory": "./src/migrations/update-15-8-0/rename-swcrc-config",
"package": "@nx/js",
"name": "rename-swcrc-config"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/js with @nx/js",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/js",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "16.6.0-beta.0",
"description": "Explicitly set 'updateBuildableProjectDepsInPackageJson' to 'true' in targets that rely on that value as the default.",
"factory": "./src/migrations/update-16-6-0/explicitly-set-projects-to-update-buildable-deps",
"package": "@nx/js",
"name": "explicitly-set-projects-to-update-buildable-deps"
},
{
"cli": "nx",
"version": "16.8.2-beta.0",
"description": "Remove invalid options (strict, noInterop) for ES6 type modules.",
"factory": "./src/migrations/update-16-8-2/update-swcrc",
"package": "@nx/js",
"name": "16-8-2-update-swcrc"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/cypress with @nx/cypress",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/cypress",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "16.2.0-beta.0",
"description": "Normalize tsconfig.cy.json files to be located at '<projectRoot>/cypress/tsconfig.json'",
"implementation": "./src/migrations/update-16-2-0/update-cy-tsconfig",
"package": "@nx/cypress",
"name": "update-16-2-0-normalize-tsconfigs"
},
{
"cli": "nx",
"version": "16.4.0-beta.10",
"description": "Remove tsconfig.e2e.json and add settings to project tsconfig.json. tsConfigs executor option is now deprecated. The project level tsconfig.json file should be used instead.",
"implementation": "./src/migrations/update-16-4-0/tsconfig-sourcemaps",
"package": "@nx/cypress",
"name": "update-16-3-0-remove-old-tsconfigs"
},
{
"cli": "nx",
"version": "16.8.0-beta.4",
"description": "Update to Cypress v13. Most noteable change is video recording is off by default. This migration will only update if the workspace is already on Cypress v12. https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-130",
"implementation": "./src/migrations/update-16-8-0/cypress-13",
"package": "@nx/cypress",
"name": "update-16-8-0-cypress-13"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/eslint-plugin with @nx/eslint-plugin",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/eslint-plugin",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "15.9.1",
"description": "Add @nx/linter, @nx/cypress, @nx/jest, @nrwl/rollup if they are used",
"factory": "./src/migrations/update-15-9-1/add-dropped-dependencies",
"package": "@nx/web",
"name": "add-dropped-dependencies"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/web with @nx/web",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/web",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "16.0.0-beta.4",
"description": "Replace @nx/web executors with @nx/webpack and @nx/rollup",
"implementation": "./src/migrations/update-16-0-0-update-executors/update-16-0-0-update-executors",
"package": "@nx/web",
"name": "update-16-0-0-update-executors"
},
{
"version": "15.8.0-beta.0",
"cli": "nx",
"description": "Update jest configs to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)",
"factory": "./src/migrations/update-15-8-0/update-configs-jest-29",
"package": "@nx/jest",
"name": "update-configs-jest-29"
},
{
"version": "15.8.0-beta.0",
"cli": "nx",
"description": "Update jest test files to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)",
"factory": "./src/migrations/update-15-8-0/update-tests-jest-29",
"package": "@nx/jest",
"name": "update-tests-jest-29"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/jest with @nx/jest",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/jest",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "16.5.0-beta.2",
"description": "Add test-setup.ts to ignored files in production input",
"implementation": "./src/migrations/update-16-5-0/add-test-setup-to-inputs-ignore",
"package": "@nx/jest",
"name": "add-test-setup-to-inputs-ignore"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/node with @nx/node",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/node",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "16.0.0-beta.5",
"description": "Replace @nx/node:webpack with @nx/node:webpack",
"implementation": "./src/migrations/update-16-0-0/update-webpack-executor",
"package": "@nx/node",
"name": "update-16-0-0-update-executor"
},
{
"cli": "nx",
"version": "16.3.1-beta.0",
"description": "Replace @nx/node:webpack and @nx/node:webpack with @nx/webpack:webpack for all project targets",
"implementation": "./src/migrations/update-16-3-1/update-webpack-executor",
"package": "@nx/node",
"name": "update-16-3-1-update-executor"
},
{
"cli": "nx",
"version": "16.4.0-beta.8",
"description": "Replace @nx/node:node with @nx/js:node for all project targets",
"implementation": "./src/migrations/update-16-4-0/replace-node-executor",
"package": "@nx/node",
"name": "update-16-4-0-replace-node-executor"
},
{
"cli": "nx",
"version": "15.8.8-beta.0",
"description": "Add less and stylus packages if used.",
"factory": "./src/migrations/update-15-8-8/add-style-packages",
"package": "@nx/next",
"name": "add-style-packages"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/next with @nx/next",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/next",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "16.3.0-beta.9",
"description": "Remove root build option from project configurations since it is not needed.",
"implementation": "./src/migrations/update-16-3-0/remove-root-build-option",
"package": "@nx/next",
"name": "update-16-3-0-remove-root-build-option"
},
{
"cli": "nx",
"version": "16.4.0-beta.3",
"description": "Update package.json moving @nx/next from dependency to devDependency",
"implementation": "./src/migrations/update-16-4-0/update-nx-next-dependency",
"package": "@nx/next",
"name": "update-16-4-0-update-next-dependency"
},
{
"cli": "nx",
"version": "16.0.0-beta.1",
"description": "Replace @nx/react with @nx/react",
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages",
"package": "@nx/react",
"name": "update-16-0-0-add-nx-packages"
},
{
"cli": "nx",
"version": "16.2.0-beta.0",
"description": "Remove react-test-renderer from package.json",
"implementation": "./src/migrations/update-16-2-0-remove-package/update-16-2-0-remove-package",
"package": "@nx/react",
"name": "update-16-2-0-remove-package"
},
{
"cli": "nx",
"version": "16.3.0-beta.2",
"description": "Remove @types/react-router-dom from package.json",
"implementation": "./src/migrations/update-16-3-0/remove-types-react-router-dom-package",
"package": "@nx/react",
"name": "remove-types-react-router-dom"
},
{
"cli": "nx",
"version": "16.7.0-beta.2",
"description": "Add @babel/core to package.json if @babel/preset-react is present",
"implementation": "./src/migrations/update-16-7-0/add-babel-core",
"package": "@nx/react",
"name": "add-babel-core"
},
{
"cli": "nx",
"version": "16.7.0-beta.2",
"description": "Add @nx/react types to tsconfig types array",
"implementation": "./src/migrations/update-16-7-0-add-typings/update-16-7-0-add-typings",
"package": "@nx/react",
"name": "update-16-7-0-add-typings"
}
]
}

23
nx.json
View File

@ -1,15 +1,15 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"npmScope": "@fal-ai/serverless",
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/nx-cloud",
"runner": "nx-cloud",
"options": {
"cacheableOperations": ["build", "lint", "test", "e2e"],
"accessToken": "NmRkNDcxMzQtZWMxNi00OTk1LTkzOTItOGI0OGZkOTQyMDM0fHJlYWQtd3JpdGU="
"accessToken": ""
}
}
},
"neverConnectToCloud": true,
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
@ -32,22 +32,23 @@
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
"!{projectRoot}/tsconfig.spec.json",
"!{projectRoot}/jest.config.[jt]s",
"!{projectRoot}/.eslintrc.json"
"!{projectRoot}/.eslintrc.json",
"!{projectRoot}/src/test-setup.[jt]s"
],
"sharedGlobals": ["{workspaceRoot}/babel.config.json"]
},
"generators": {
"@nrwl/react": {
"application": {
"babel": true
}
},
"@nrwl/next": {
"@nx/next": {
"application": {
"style": "css",
"linter": "eslint"
}
},
"@nx/react": {
"application": {
"babel": true
}
}
},
"defaultProject": "demo-app"
"defaultProject": "demo-nextjs-app"
}

26639
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,19 +10,22 @@
},
"private": true,
"dependencies": {
"@nrwl/next": "15.7.2",
"@oclif/core": "^2.3.0",
"@oclif/plugin-help": "^5.2.5",
"axios": "^1.0.0",
"change-case": "^4.1.2",
"chokidar": "^3.5.3",
"core-js": "^3.6.5",
"cors": "^2.8.5",
"cross-fetch": "^3.1.5",
"dotenv": "^16.3.1",
"encoding": "^0.1.13",
"express": "^4.18.2",
"fast-glob": "^3.2.12",
"http-proxy": "^1.18.1",
"http-proxy-middleware": "^2.0.6",
"js-base64": "^3.7.5",
"next": "13.1.1",
"next": "13.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"regenerator-runtime": "0.13.7",
@ -32,50 +35,57 @@
"devDependencies": {
"@commitlint/cli": "^17.0.0",
"@commitlint/config-conventional": "^17.0.0",
"@nrwl/cli": "15.7.2",
"@nrwl/cypress": "15.7.2",
"@nrwl/eslint-plugin-nx": "15.7.2",
"@nrwl/jest": "15.7.2",
"@nrwl/js": "15.7.2",
"@nrwl/linter": "15.7.2",
"@nrwl/node": "15.7.2",
"@nrwl/nx-cloud": "15.1.0",
"@nrwl/react": "15.7.2",
"@nrwl/web": "15.7.2",
"@nrwl/workspace": "15.7.2",
"@testing-library/react": "13.4.0",
"@nrwl/express": "16.10.0",
"@nx/cypress": "16.10.0",
"@nx/eslint-plugin": "16.10.0",
"@nx/express": "16.10.0",
"@nx/jest": "16.10.0",
"@nx/js": "16.10.0",
"@nx/linter": "16.10.0",
"@nx/next": "16.10.0",
"@nx/node": "16.10.0",
"@nx/react": "16.10.0",
"@nx/web": "16.10.0",
"@nx/webpack": "16.10.0",
"@nx/workspace": "16.10.0",
"@swc-node/core": "^1.10.6",
"@swc-node/register": "^1.6.8",
"@testing-library/react": "14.0.0",
"@theunderscorer/nx-semantic-release": "^2.2.1",
"@types/jest": "28.1.1",
"@types/node": "^18.17.14",
"@types/react": "18.0.20",
"@types/react-dom": "18.0.6",
"@typescript-eslint/eslint-plugin": "^5.55.0",
"@typescript-eslint/parser": "^5.55.0",
"@types/cors": "^2.8.14",
"@types/express": "4.17.13",
"@types/jest": "29.4.4",
"@types/node": "18.14.2",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.6",
"@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "5.62.0",
"autoprefixer": "10.4.13",
"babel-jest": "28.1.1",
"babel-jest": "29.4.3",
"cypress": "^11.0.0",
"eslint": "^8.36.0",
"eslint": "8.46.0",
"eslint-config-next": "^13.1.1",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-cypress": "2.15.1",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"fs-extra": "^11.1.0",
"husky": "^8.0.0",
"jest": "28.1.1",
"jest-environment-jsdom": "28.1.1",
"nx": "15.7.2",
"jest": "29.4.3",
"jest-environment-jsdom": "29.4.3",
"jest-environment-node": "^29.4.1",
"nx": "16.10.0",
"nx-cloud": "16.4.0",
"organize-imports-cli": "^0.10.0",
"postcss": "8.4.19",
"postcss": "8.4.21",
"prettier": "^2.6.2",
"react-test-renderer": "18.2.0",
"tailwindcss": "^3.3.3",
"ts-jest": "28.0.5",
"tailwindcss": "3.2.7",
"ts-jest": "29.1.1",
"ts-node": "^10.9.1",
"ts-protoc-gen": "^0.15.0",
"tsconfig-paths": "^4.1.2",
"typescript": "^4.9.5"
"tsconfig-paths": "^4.2.0",
"typescript": "5.1.6"
}
}

View File

@ -16,9 +16,11 @@
"baseUrl": ".",
"paths": {
"@fal-ai/serverless-client": ["libs/client/src/index.ts"],
"@fal-ai/serverless-codegen": ["libs/codegen/src/index.ts"],
"@fal-ai/serverless-nextjs": ["libs/nextjs/src/index.ts"]
"@fal-ai/serverless-nextjs": ["libs/nextjs/src/index.ts"],
"@fal-ai/serverless-proxy": ["libs/proxy/src/index.ts"],
"@fal-ai/serverless-proxy/express": ["libs/proxy/src/express.ts"],
"@fal-ai/serverless-proxy/nextjs": ["libs/proxy/src/nextjs.ts"]
}
},
"exclude": ["node_modules", "tmp"]
"exclude": ["node_modules/**", "tmp/**", "dist/**"]
}