云函数 examples
This commit is contained in:
		
							
								
								
									
										35
									
								
								examples/serverless-faas/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								examples/serverless-faas/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
Serverless 云函数支持示例
 | 
			
		||||
===
 | 
			
		||||
 | 
			
		||||
演示了示例项目中的留言板,同时支持阿里云函数计算(FC),腾讯云云函数(SCF),和原生 NodeJS 部署的支持。
 | 
			
		||||
 | 
			
		||||
主要方式是在后端项目中区分为 3 个不同的入口点:
 | 
			
		||||
 | 
			
		||||
- `index.ts`:NodeJS 原生部署入口点
 | 
			
		||||
- `aliyun-fc.ts`:阿里云函数计算 FC 入口点
 | 
			
		||||
    
 | 
			
		||||
- `txcloud-scf.ts`:腾讯云云函数 SCF 入口点
 | 
			
		||||
 | 
			
		||||
## 部署方式
 | 
			
		||||
 | 
			
		||||
### 阿里云
 | 
			
		||||
 | 
			
		||||
1. 创建 HTTP 函数,`npm run build` 然后将 `dist` 目录下的代码上传到阿里云根目录
 | 
			
		||||
1. 在 Web IDE 终端执行命令:
 | 
			
		||||
    ```shell
 | 
			
		||||
    npm i
 | 
			
		||||
    ```
 | 
			
		||||
1. 配置 **函数入口** 为 `aliyun-fc.handler`
 | 
			
		||||
1. 配置 **初始化函数** 为 `aliyun-fc.initializer`
 | 
			
		||||
1. 测试运行
 | 
			
		||||
 | 
			
		||||
### 腾讯云
 | 
			
		||||
 | 
			
		||||
1. 创建 Event 函数,`npm run build` 然后将 `dist` 目录上传到腾讯云,并更名为 `src` 目录
 | 
			
		||||
1. 在 Web IDE 终端执行命令:
 | 
			
		||||
    ```shell
 | 
			
		||||
    cd src
 | 
			
		||||
    npm i
 | 
			
		||||
    ```
 | 
			
		||||
1. 配置 **执行函数** 为 `txcloud-scf.handler`
 | 
			
		||||
1. 测试运行
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/serverless-faas/backend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/serverless-faas/backend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
node_modules
 | 
			
		||||
dist
 | 
			
		||||
.DS_STORE
 | 
			
		||||
							
								
								
									
										11
									
								
								examples/serverless-faas/backend/.mocharc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								examples/serverless-faas/backend/.mocharc.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
module.exports = {
 | 
			
		||||
    require: [
 | 
			
		||||
        'ts-node/register',
 | 
			
		||||
    ],
 | 
			
		||||
    timeout: 999999,
 | 
			
		||||
    exit: true,
 | 
			
		||||
    spec: [
 | 
			
		||||
        './test/**/*.test.ts'
 | 
			
		||||
    ],
 | 
			
		||||
    'preserve-symlinks': true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								examples/serverless-faas/backend/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								examples/serverless-faas/backend/.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
{
 | 
			
		||||
    "configurations": [
 | 
			
		||||
        {
 | 
			
		||||
            "type": "node",
 | 
			
		||||
            "request": "launch",
 | 
			
		||||
            "name": "mocha current file",
 | 
			
		||||
            "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
 | 
			
		||||
            "args": [
 | 
			
		||||
                "${file}"
 | 
			
		||||
            ],
 | 
			
		||||
            "internalConsoleOptions": "openOnSessionStart",
 | 
			
		||||
            "cwd": "${workspaceFolder}"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "type": "node",
 | 
			
		||||
            "request": "launch",
 | 
			
		||||
            "name": "ts-node current file",
 | 
			
		||||
            "protocol": "inspector",
 | 
			
		||||
            "args": [
 | 
			
		||||
                "${relativeFile}"
 | 
			
		||||
            ],
 | 
			
		||||
            "cwd": "${workspaceRoot}",
 | 
			
		||||
            "runtimeArgs": [
 | 
			
		||||
                "-r",
 | 
			
		||||
                "ts-node/register"
 | 
			
		||||
            ],
 | 
			
		||||
            "internalConsoleOptions": "openOnSessionStart"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								examples/serverless-faas/backend/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/serverless-faas/backend/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
{
 | 
			
		||||
    "typescript.tsdk": "node_modules\\typescript\\lib"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								examples/serverless-faas/backend/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								examples/serverless-faas/backend/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
FROM node
 | 
			
		||||
 | 
			
		||||
# 使用淘宝 NPM 镜像(国内机器构建推荐启用)
 | 
			
		||||
# RUN npm config set registry https://registry.npm.taobao.org/
 | 
			
		||||
 | 
			
		||||
# npm install
 | 
			
		||||
ADD package*.json /src/
 | 
			
		||||
WORKDIR /src
 | 
			
		||||
RUN npm i
 | 
			
		||||
 | 
			
		||||
# build
 | 
			
		||||
ADD . /src
 | 
			
		||||
RUN npm run build
 | 
			
		||||
 | 
			
		||||
# clean
 | 
			
		||||
RUN npm prune --production
 | 
			
		||||
 | 
			
		||||
# move
 | 
			
		||||
RUN rm -rf /app \
 | 
			
		||||
    && mv dist /app \
 | 
			
		||||
    && mv node_modules /app/ \
 | 
			
		||||
    && rm -rf /src
 | 
			
		||||
 | 
			
		||||
# ENV
 | 
			
		||||
ENV NODE_ENV production
 | 
			
		||||
 | 
			
		||||
EXPOSE 3000
 | 
			
		||||
 | 
			
		||||
WORKDIR /app
 | 
			
		||||
CMD node index.js
 | 
			
		||||
							
								
								
									
										31
									
								
								examples/serverless-faas/backend/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								examples/serverless-faas/backend/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
# TSRPC Server
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
### Local dev server
 | 
			
		||||
 | 
			
		||||
Dev server would restart automatically when code changed.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
npm run dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Build
 | 
			
		||||
```
 | 
			
		||||
npm run build
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Generate API document
 | 
			
		||||
 | 
			
		||||
Generate API document in swagger/openapi and markdown format.
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
npm run doc
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Run unit Test
 | 
			
		||||
Execute `npm run dev` first, then execute:
 | 
			
		||||
```
 | 
			
		||||
npm run test
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
							
								
								
									
										24
									
								
								examples/serverless-faas/backend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								examples/serverless-faas/backend/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "serverless-faas-backend",
 | 
			
		||||
  "version": "0.1.0",
 | 
			
		||||
  "main": "index.js",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "tsrpc-cli dev",
 | 
			
		||||
    "build": "tsrpc-cli build",
 | 
			
		||||
    "doc": "tsrpc-cli doc",
 | 
			
		||||
    "test": "mocha test/**/*.test.ts"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@types/mocha": "^8.2.3",
 | 
			
		||||
    "@types/node": "^15.14.9",
 | 
			
		||||
    "mocha": "^9.1.3",
 | 
			
		||||
    "onchange": "^7.1.0",
 | 
			
		||||
    "ts-node": "^10.4.0",
 | 
			
		||||
    "tsrpc-cli": "^2.3.0",
 | 
			
		||||
    "typescript": "^4.5.4"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "tsrpc": "^3.1.4"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								examples/serverless-faas/backend/src/aliyun-fc.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								examples/serverless-faas/backend/src/aliyun-fc.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
// 阿里云 - 函数计算:FC
 | 
			
		||||
// https://cloud.tencent.com/product/scf
 | 
			
		||||
 | 
			
		||||
import { init, server } from "./models/server";
 | 
			
		||||
 | 
			
		||||
// 阿里云函数计算 - HTTP函数
 | 
			
		||||
export async function handler(req: any, res: any, context: any) {
 | 
			
		||||
    // JSON 请求
 | 
			
		||||
    if (req.headers['content-type']?.includes('application/json')) {
 | 
			
		||||
        let apiName = req.path.slice(1);    // 去除开头的 "/"
 | 
			
		||||
        let ret = await server.inputJSON(apiName, JSON.parse(req.body));
 | 
			
		||||
        res.setStatusCode(ret.isSucc ? 200 : 500);
 | 
			
		||||
        res.send(JSON.stringify(ret));
 | 
			
		||||
    }
 | 
			
		||||
    // 二进制请求
 | 
			
		||||
    else {
 | 
			
		||||
        let output = await server.inputBuffer(req.body);
 | 
			
		||||
        res.send(Buffer.from(output));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 阿里云函数计算 - 初始化
 | 
			
		||||
export async function initializer(context: unknown, callback: Function) {
 | 
			
		||||
    await init();
 | 
			
		||||
    callback();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								examples/serverless-faas/backend/src/api/ApiAddData.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								examples/serverless-faas/backend/src/api/ApiAddData.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
import { ApiCall } from "tsrpc";
 | 
			
		||||
import { ReqAddData, ResAddData } from "../shared/protocols/PtlAddData";
 | 
			
		||||
import { AllData } from "./ApiGetData";
 | 
			
		||||
 | 
			
		||||
// This is a demo code file
 | 
			
		||||
// Feel free to delete it
 | 
			
		||||
 | 
			
		||||
export async function ApiAddData(call: ApiCall<ReqAddData, ResAddData>) {
 | 
			
		||||
    // Error
 | 
			
		||||
    if (call.req.content === '') {
 | 
			
		||||
        call.error('Content is empty');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let time = new Date();
 | 
			
		||||
    AllData.unshift({
 | 
			
		||||
        content: call.req.content,
 | 
			
		||||
        time: time
 | 
			
		||||
    })
 | 
			
		||||
    console.log('AllData', AllData)
 | 
			
		||||
 | 
			
		||||
    // Success
 | 
			
		||||
    call.succ({
 | 
			
		||||
        time: time
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								examples/serverless-faas/backend/src/api/ApiGetData.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/serverless-faas/backend/src/api/ApiGetData.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
import { ApiCall } from "tsrpc";
 | 
			
		||||
import { ReqGetData, ResGetData } from "../shared/protocols/PtlGetData";
 | 
			
		||||
 | 
			
		||||
// This is a demo code file
 | 
			
		||||
// Feel free to delete it
 | 
			
		||||
 | 
			
		||||
export async function ApiGetData(call: ApiCall<ReqGetData, ResGetData>) {
 | 
			
		||||
    call.succ({
 | 
			
		||||
        data: AllData
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const AllData: { content: string, time: Date }[] = [];
 | 
			
		||||
							
								
								
									
										8
									
								
								examples/serverless-faas/backend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								examples/serverless-faas/backend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
import { init, server } from "./models/server";
 | 
			
		||||
 | 
			
		||||
// 普通 NodeJS 环境入口点
 | 
			
		||||
async function main() {
 | 
			
		||||
    await init();
 | 
			
		||||
    await server.start();
 | 
			
		||||
};
 | 
			
		||||
main();
 | 
			
		||||
							
								
								
									
										19
									
								
								examples/serverless-faas/backend/src/models/server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								examples/serverless-faas/backend/src/models/server.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,19 @@
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { HttpServer } from "tsrpc";
 | 
			
		||||
import { serviceProto } from "../shared/protocols/serviceProto";
 | 
			
		||||
 | 
			
		||||
// Create the Server
 | 
			
		||||
export const server = new HttpServer(serviceProto, {
 | 
			
		||||
    port: 3000,
 | 
			
		||||
    // Remove this to use binary mode (remove from the client too)
 | 
			
		||||
    json: true
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Initialize before server start
 | 
			
		||||
export async function init(delay?: boolean) {
 | 
			
		||||
    // Auto implement APIs
 | 
			
		||||
    await server.autoImplementApi(path.resolve(__dirname, '../api/'), delay);
 | 
			
		||||
 | 
			
		||||
    // TODO
 | 
			
		||||
    // Prepare something... (e.g. connect the db)
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,16 @@
 | 
			
		||||
// This is a demo code file
 | 
			
		||||
// Feel free to delete it
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 增加数据
 | 
			
		||||
 * 此处的注释将会自动附带到生成的 API 文档中
 | 
			
		||||
 */
 | 
			
		||||
export interface ReqAddData {
 | 
			
		||||
    /** 要增加的消息内容 */
 | 
			
		||||
    content: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ResAddData {
 | 
			
		||||
    /** 服务端内容创建时间 */
 | 
			
		||||
    time: Date
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
// This is a demo code file
 | 
			
		||||
// Feel free to delete it
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 获取数据
 | 
			
		||||
 */
 | 
			
		||||
export interface ReqGetData {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ResGetData {
 | 
			
		||||
    /** 返回所有数据列表 */
 | 
			
		||||
    data: {
 | 
			
		||||
        /** 消息内容 */
 | 
			
		||||
        content: string,
 | 
			
		||||
        /** 创建时间 */
 | 
			
		||||
        time: Date
 | 
			
		||||
    }[]
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,11 @@
 | 
			
		||||
export interface BaseRequest {
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface BaseResponse {
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface BaseConf {
 | 
			
		||||
    
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,98 @@
 | 
			
		||||
import { ServiceProto } from 'tsrpc-proto';
 | 
			
		||||
import { ReqAddData, ResAddData } from './PtlAddData';
 | 
			
		||||
import { ReqGetData, ResGetData } from './PtlGetData';
 | 
			
		||||
 | 
			
		||||
// This is a demo service proto file (auto generated)
 | 
			
		||||
// Feel free to delete it
 | 
			
		||||
 | 
			
		||||
export interface ServiceType {
 | 
			
		||||
    api: {
 | 
			
		||||
        "AddData": {
 | 
			
		||||
            req: ReqAddData,
 | 
			
		||||
            res: ResAddData
 | 
			
		||||
        },
 | 
			
		||||
        "GetData": {
 | 
			
		||||
            req: ReqGetData,
 | 
			
		||||
            res: ResGetData
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    msg: {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const serviceProto: ServiceProto<ServiceType> = {
 | 
			
		||||
    "version": 1,
 | 
			
		||||
    "services": [
 | 
			
		||||
        {
 | 
			
		||||
            "id": 0,
 | 
			
		||||
            "name": "AddData",
 | 
			
		||||
            "type": "api"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "id": 1,
 | 
			
		||||
            "name": "GetData",
 | 
			
		||||
            "type": "api"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "types": {
 | 
			
		||||
        "PtlAddData/ReqAddData": {
 | 
			
		||||
            "type": "Interface",
 | 
			
		||||
            "properties": [
 | 
			
		||||
                {
 | 
			
		||||
                    "id": 0,
 | 
			
		||||
                    "name": "content",
 | 
			
		||||
                    "type": {
 | 
			
		||||
                        "type": "String"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "PtlAddData/ResAddData": {
 | 
			
		||||
            "type": "Interface",
 | 
			
		||||
            "properties": [
 | 
			
		||||
                {
 | 
			
		||||
                    "id": 0,
 | 
			
		||||
                    "name": "time",
 | 
			
		||||
                    "type": {
 | 
			
		||||
                        "type": "Date"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        },
 | 
			
		||||
        "PtlGetData/ReqGetData": {
 | 
			
		||||
            "type": "Interface"
 | 
			
		||||
        },
 | 
			
		||||
        "PtlGetData/ResGetData": {
 | 
			
		||||
            "type": "Interface",
 | 
			
		||||
            "properties": [
 | 
			
		||||
                {
 | 
			
		||||
                    "id": 0,
 | 
			
		||||
                    "name": "data",
 | 
			
		||||
                    "type": {
 | 
			
		||||
                        "type": "Array",
 | 
			
		||||
                        "elementType": {
 | 
			
		||||
                            "type": "Interface",
 | 
			
		||||
                            "properties": [
 | 
			
		||||
                                {
 | 
			
		||||
                                    "id": 0,
 | 
			
		||||
                                    "name": "content",
 | 
			
		||||
                                    "type": {
 | 
			
		||||
                                        "type": "String"
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                {
 | 
			
		||||
                                    "id": 1,
 | 
			
		||||
                                    "name": "time",
 | 
			
		||||
                                    "type": {
 | 
			
		||||
                                        "type": "Date"
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            ]
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										26
									
								
								examples/serverless-faas/backend/src/txcloud-scf.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								examples/serverless-faas/backend/src/txcloud-scf.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
// 腾讯云 - 云函数:SCF
 | 
			
		||||
// https://cloud.tencent.com/product/scf
 | 
			
		||||
 | 
			
		||||
import { init, server } from "./models/server";
 | 
			
		||||
 | 
			
		||||
export async function handler(event: any, context: any) {
 | 
			
		||||
    // init
 | 
			
		||||
    await ensureInit();
 | 
			
		||||
 | 
			
		||||
    let apiName = event.path.slice(event.requestContext.path.length + 1);    // 从 URL 中提取 ApiName
 | 
			
		||||
    let ret = await server.inputJSON(apiName, JSON.parse(event.body));
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        "statusCode": ret.isSucc ? 200 : 500,
 | 
			
		||||
        "headers": { "Content-Type": "application/json" },
 | 
			
		||||
        "body": JSON.stringify(ret)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let promiseInit: Promise<void> | undefined;
 | 
			
		||||
async function ensureInit() {
 | 
			
		||||
    if (!promiseInit) {
 | 
			
		||||
        promiseInit = init(true);
 | 
			
		||||
    }
 | 
			
		||||
    return promiseInit;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								examples/serverless-faas/backend/test/api/data.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								examples/serverless-faas/backend/test/api/data.test.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import assert from 'assert';
 | 
			
		||||
import { HttpClient, TsrpcError } from 'tsrpc';
 | 
			
		||||
import { serviceProto } from '../../src/shared/protocols/serviceProto';
 | 
			
		||||
 | 
			
		||||
// 1. EXECUTE `npm run dev` TO START A LOCAL DEV SERVER
 | 
			
		||||
// 2. EXECUTE `npm test` TO START UNIT TEST
 | 
			
		||||
 | 
			
		||||
describe('ApiGetData', function () {
 | 
			
		||||
    let client = new HttpClient(serviceProto, {
 | 
			
		||||
        server: 'http://127.0.0.1:3000',
 | 
			
		||||
        logger: console
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('AddData & GetData', async function () {
 | 
			
		||||
        // Get data before add
 | 
			
		||||
        let ret1 = await client.callApi('GetData', {});
 | 
			
		||||
        assert.strictEqual(ret1.isSucc, true);
 | 
			
		||||
 | 
			
		||||
        // AddData
 | 
			
		||||
        let ret2 = await client.callApi('AddData', { content: 'AABBCC' });
 | 
			
		||||
        assert.strictEqual(ret2.isSucc, true);
 | 
			
		||||
 | 
			
		||||
        // Get data again, the new data should appear
 | 
			
		||||
        let ret3 = await client.callApi('GetData', {});
 | 
			
		||||
        assert.strictEqual(ret3.isSucc, true);
 | 
			
		||||
        assert.strictEqual(ret3.res!.data.length, ret1.res!.data.length + 1);
 | 
			
		||||
        assert.strictEqual(ret3.res!.data[0].content, 'AABBCC');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('AddData: Check content is empty', async function () {
 | 
			
		||||
        let ret = await client.callApi('AddData', {
 | 
			
		||||
            content: ''
 | 
			
		||||
        });
 | 
			
		||||
        assert.deepStrictEqual(ret, {
 | 
			
		||||
            isSucc: false,
 | 
			
		||||
            err: new TsrpcError('Content is empty')
 | 
			
		||||
        });
 | 
			
		||||
    })
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										15
									
								
								examples/serverless-faas/backend/test/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/serverless-faas/backend/test/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "lib": [
 | 
			
		||||
      "es2018"
 | 
			
		||||
    ],
 | 
			
		||||
    "module": "commonjs",
 | 
			
		||||
    "target": "es2018",
 | 
			
		||||
    "outDir": "dist",
 | 
			
		||||
    "strict": true,
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "skipLibCheck": true,
 | 
			
		||||
    "forceConsistentCasingInFileNames": true,
 | 
			
		||||
    "moduleResolution": "node"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								examples/serverless-faas/backend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								examples/serverless-faas/backend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "lib": [
 | 
			
		||||
      "es2018"
 | 
			
		||||
    ],
 | 
			
		||||
    "module": "commonjs",
 | 
			
		||||
    "target": "es2018",
 | 
			
		||||
    "outDir": "dist",
 | 
			
		||||
    "strict": true,
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "skipLibCheck": true,
 | 
			
		||||
    "forceConsistentCasingInFileNames": true,
 | 
			
		||||
    "moduleResolution": "node"
 | 
			
		||||
  },
 | 
			
		||||
  "include": [
 | 
			
		||||
    "src"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								examples/serverless-faas/backend/tsrpc.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								examples/serverless-faas/backend/tsrpc.config.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import { CodeTemplate, TsrpcConfig } from 'tsrpc-cli';
 | 
			
		||||
 | 
			
		||||
const tsrpcConf: TsrpcConfig = {
 | 
			
		||||
    // Generate ServiceProto
 | 
			
		||||
    proto: [
 | 
			
		||||
        {
 | 
			
		||||
            ptlDir: 'src/shared/protocols', // Protocol dir
 | 
			
		||||
            output: 'src/shared/protocols/serviceProto.ts', // Path for generated ServiceProto
 | 
			
		||||
            apiDir: 'src/api',   // API dir
 | 
			
		||||
            docDir: 'docs',     // API documents dir
 | 
			
		||||
            ptlTemplate: CodeTemplate.getExtendedPtl(),
 | 
			
		||||
            // msgTemplate: CodeTemplate.getExtendedMsg(),
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    // Sync shared code
 | 
			
		||||
    sync: [
 | 
			
		||||
        {
 | 
			
		||||
            from: 'src/shared',
 | 
			
		||||
            to: '../frontend/src/shared',
 | 
			
		||||
            type: 'symlink'     // Change this to 'copy' if your environment not support symlink
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    // Dev server
 | 
			
		||||
    dev: {
 | 
			
		||||
        autoProto: true,        // Auto regenerate proto
 | 
			
		||||
        autoSync: true,         // Auto sync when file changed
 | 
			
		||||
        autoApi: true,          // Auto create API when ServiceProto updated
 | 
			
		||||
        watch: 'src',           // Restart dev server when these files changed
 | 
			
		||||
        entry: 'src/index.ts',  // Dev server command: node -r ts-node/register {entry}
 | 
			
		||||
    },
 | 
			
		||||
    // Build config
 | 
			
		||||
    build: {
 | 
			
		||||
        autoProto: true,        // Auto generate proto before build
 | 
			
		||||
        autoSync: true,         // Auto sync before build
 | 
			
		||||
        autoApi: true,          // Auto generate API before build
 | 
			
		||||
        outDir: 'dist',         // Clean this dir before build
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
export default tsrpcConf;
 | 
			
		||||
							
								
								
									
										1315
									
								
								examples/serverless-faas/backend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1315
									
								
								examples/serverless-faas/backend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										3
									
								
								examples/serverless-faas/frontend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								examples/serverless-faas/frontend/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
.DS_STORE
 | 
			
		||||
node_modules
 | 
			
		||||
dist
 | 
			
		||||
							
								
								
									
										24
									
								
								examples/serverless-faas/frontend/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								examples/serverless-faas/frontend/package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "serverless-faas-frontend",
 | 
			
		||||
  "version": "0.1.0",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "dev": "webpack serve --mode=development --open",
 | 
			
		||||
    "build": "webpack --mode=production"
 | 
			
		||||
  },
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "copy-webpack-plugin": "^9.1.0",
 | 
			
		||||
    "html-webpack-plugin": "^5.5.0",
 | 
			
		||||
    "ts-loader": "^9.2.6",
 | 
			
		||||
    "typescript": "^4.5.4",
 | 
			
		||||
    "webpack": "^5.65.0",
 | 
			
		||||
    "webpack-cli": "^4.9.1",
 | 
			
		||||
    "webpack-dev-server": "^3.11.3"
 | 
			
		||||
  },
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "tsrpc-browser": "^3.1.4"
 | 
			
		||||
  },
 | 
			
		||||
  "browserslist": [
 | 
			
		||||
    "defaults"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								examples/serverless-faas/frontend/public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/serverless-faas/frontend/public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 4.2 KiB  | 
							
								
								
									
										83
									
								
								examples/serverless-faas/frontend/public/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								examples/serverless-faas/frontend/public/index.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
* {
 | 
			
		||||
    margin: 0;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body>* {
 | 
			
		||||
    margin: 20px auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.send,
 | 
			
		||||
.list {
 | 
			
		||||
    width: 370px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.send>* {
 | 
			
		||||
    display: block;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    margin: 10px auto;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    font-size: 16px;
 | 
			
		||||
    border: none;
 | 
			
		||||
    outline: none;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.send>textarea {
 | 
			
		||||
    height: 80px;
 | 
			
		||||
    background: #f7f7f7;
 | 
			
		||||
    border: #eeeeee 1px solid;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.send>textarea:focus {
 | 
			
		||||
    background: #fff;
 | 
			
		||||
    border-color: #ccc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.send>button {
 | 
			
		||||
    background: #215fa4;
 | 
			
		||||
    color: white;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.send>button:hover {
 | 
			
		||||
    background: #4b80bb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list {
 | 
			
		||||
    list-style: none;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    background: #f2f2f2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list>li {
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    background: #fff;
 | 
			
		||||
    line-height: 1.5em;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list>li>.content {
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    white-space: pre-wrap;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list>li>.time {
 | 
			
		||||
    font-size: 12px;
 | 
			
		||||
    color: #4b80bb;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.list>li:last-child {
 | 
			
		||||
    border-bottom: none;
 | 
			
		||||
    margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								examples/serverless-faas/frontend/public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								examples/serverless-faas/frontend/public/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8" />
 | 
			
		||||
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
 | 
			
		||||
    <meta name="renderer" content="webkit" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
			
		||||
    <title>TSRPC Browser</title>
 | 
			
		||||
    <link rel="stylesheet" href="index.css" />
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <h1>TSRPC Guestbook</h1>
 | 
			
		||||
 | 
			
		||||
    <div class="send">
 | 
			
		||||
        <textarea placeholder="Say something..."></textarea>
 | 
			
		||||
        <button>Send</button>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <ul class="list">
 | 
			
		||||
        <!-- <li>
 | 
			
		||||
            <div class="content">Hello, World</div>
 | 
			
		||||
            <div class="time">11:23:23</div>
 | 
			
		||||
        </li> -->
 | 
			
		||||
    </ul>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										10
									
								
								examples/serverless-faas/frontend/src/client.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								examples/serverless-faas/frontend/src/client.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
import { HttpClient } from "tsrpc-browser";
 | 
			
		||||
import { serviceProto } from "./shared/protocols/serviceProto";
 | 
			
		||||
 | 
			
		||||
// Create Client
 | 
			
		||||
export const client = new HttpClient(serviceProto, {
 | 
			
		||||
    server: "https://service-23pfz6cm-1253954497.gz.apigw.tencentcs.com/release/helloworld-1634897828/",
 | 
			
		||||
    // Remove this to use binary mode (remove from the server too)
 | 
			
		||||
    json: true,
 | 
			
		||||
    logger: console,
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										9
									
								
								examples/serverless-faas/frontend/src/env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								examples/serverless-faas/frontend/src/env.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
// TSRPC would decode ObjectId as string in frontend.
 | 
			
		||||
declare module 'mongodb' {
 | 
			
		||||
    export type ObjectId = string;
 | 
			
		||||
    export type ObjectID = string;
 | 
			
		||||
}
 | 
			
		||||
declare module 'bson' {
 | 
			
		||||
    export type ObjectId = string;
 | 
			
		||||
    export type ObjectID = string;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										50
									
								
								examples/serverless-faas/frontend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								examples/serverless-faas/frontend/src/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
import { client } from './client';
 | 
			
		||||
 | 
			
		||||
// This is a demo code file
 | 
			
		||||
// Feel free to modify or clear it
 | 
			
		||||
 | 
			
		||||
// Reload message list
 | 
			
		||||
async function loadList() {
 | 
			
		||||
    let ret = await client.callApi('GetData', {});
 | 
			
		||||
 | 
			
		||||
    // Error
 | 
			
		||||
    if (!ret.isSucc) {
 | 
			
		||||
        alert(ret.err.message);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Success
 | 
			
		||||
    const list = document.querySelector('.list')!;
 | 
			
		||||
    list.innerHTML = '';
 | 
			
		||||
    ret.res.data.forEach(v => {
 | 
			
		||||
        let li = document.createElement('li');
 | 
			
		||||
        li.innerHTML = `<div class="content"></div><div class="time"></div>`;
 | 
			
		||||
        (li.querySelector('.content') as HTMLDivElement).innerText = v.content;
 | 
			
		||||
        (li.querySelector('.time') as HTMLDivElement).innerText = v.time.toLocaleTimeString();
 | 
			
		||||
        list.appendChild(li);
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Send Message
 | 
			
		||||
async function send() {
 | 
			
		||||
    const textarea = document.querySelector('.send>textarea') as HTMLTextAreaElement;
 | 
			
		||||
    let ret = await client.callApi('AddData', {
 | 
			
		||||
        content: textarea.value
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Error
 | 
			
		||||
    if (!ret.isSucc) {
 | 
			
		||||
        alert(ret.err.message);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Success
 | 
			
		||||
    textarea.value = '';
 | 
			
		||||
    loadList();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bind Events
 | 
			
		||||
(document.querySelector('.send>button') as HTMLButtonElement).onclick = send;
 | 
			
		||||
 | 
			
		||||
// Load list after page load
 | 
			
		||||
loadList();
 | 
			
		||||
							
								
								
									
										1
									
								
								examples/serverless-faas/frontend/src/shared
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								examples/serverless-faas/frontend/src/shared
									
									
									
									
									
										Symbolic link
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
E:/Projects/tsrpc-examples/examples/serverless-faas/backend/src/shared
 | 
			
		||||
							
								
								
									
										23
									
								
								examples/serverless-faas/frontend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								examples/serverless-faas/frontend/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
{
 | 
			
		||||
  "compilerOptions": {
 | 
			
		||||
    "lib": [
 | 
			
		||||
      "dom",
 | 
			
		||||
      "es2015"
 | 
			
		||||
    ],
 | 
			
		||||
    "module": "esnext",
 | 
			
		||||
    "target": "es5",
 | 
			
		||||
    "allowSyntheticDefaultImports": true,
 | 
			
		||||
    "esModuleInterop": true,
 | 
			
		||||
    "forceConsistentCasingInFileNames": true,
 | 
			
		||||
    "moduleResolution": "node",
 | 
			
		||||
    "outDir": "dist",
 | 
			
		||||
    "skipLibCheck": true,
 | 
			
		||||
    "strict": true,
 | 
			
		||||
    "jsx": "react-jsx",
 | 
			
		||||
    "sourceMap": true,
 | 
			
		||||
    "isolatedModules": true
 | 
			
		||||
  },
 | 
			
		||||
  "include": [
 | 
			
		||||
    "src"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								examples/serverless-faas/frontend/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								examples/serverless-faas/frontend/webpack.config.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
const webpack = require('webpack');
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
 | 
			
		||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
 | 
			
		||||
 | 
			
		||||
const isProduction = process.argv.indexOf('--mode=production') > -1;
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    entry: './src/index.ts',
 | 
			
		||||
    output: {
 | 
			
		||||
        filename: 'bundle.[contenthash].js',
 | 
			
		||||
        path: path.resolve(__dirname, 'dist'),
 | 
			
		||||
        clean: true
 | 
			
		||||
    },
 | 
			
		||||
    resolve: {
 | 
			
		||||
        extensions: ['.ts', '.tsx', '.js', '.mjs', '.cjs']
 | 
			
		||||
    },
 | 
			
		||||
    module: {
 | 
			
		||||
        rules: [
 | 
			
		||||
            {
 | 
			
		||||
                test: /\.tsx?$/,
 | 
			
		||||
                use: [{
 | 
			
		||||
                    loader: 'ts-loader',
 | 
			
		||||
                    options: {
 | 
			
		||||
                        // Compile to ES5 in production mode for better compatibility
 | 
			
		||||
                        // Compile to ES2018 in development for better debugging (like async/await)
 | 
			
		||||
                        compilerOptions: !isProduction ? {
 | 
			
		||||
                            "target": "es2018",
 | 
			
		||||
                        } : undefined
 | 
			
		||||
                    }
 | 
			
		||||
                }],
 | 
			
		||||
                exclude: /node_modules/
 | 
			
		||||
            },
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    plugins: [
 | 
			
		||||
        // Copy "public" to "dist"
 | 
			
		||||
        new CopyWebpackPlugin({
 | 
			
		||||
            patterns: [{
 | 
			
		||||
                from: 'public',
 | 
			
		||||
                to: '.',
 | 
			
		||||
                toType: 'dir',
 | 
			
		||||
                globOptions: {
 | 
			
		||||
                    gitignore: true,
 | 
			
		||||
                    ignore: [path.resolve(__dirname, 'public/index.html').replace(/\\/g, '/')]
 | 
			
		||||
                },
 | 
			
		||||
                noErrorOnMissing: true
 | 
			
		||||
            }]
 | 
			
		||||
        }),
 | 
			
		||||
        // Auto add <script> to "index.html"
 | 
			
		||||
        new HtmlWebpackPlugin({
 | 
			
		||||
            template: 'public/index.html'
 | 
			
		||||
        }),
 | 
			
		||||
    ],
 | 
			
		||||
    devtool: isProduction ? false : 'inline-source-map'
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3624
									
								
								examples/serverless-faas/frontend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3624
									
								
								examples/serverless-faas/frontend/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user