@faasjs/dev 8.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/index.cjs +356 -0
- package/dist/index.d.ts +131 -0
- package/dist/index.mjs +323 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019-present, Zhu Feng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @faasjs/dev
|
|
2
|
+
|
|
3
|
+
FaasJS development toolkit for local development and testing.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/faasjs/faasjs/blob/main/packages/dev/LICENSE)
|
|
6
|
+
[](https://www.npmjs.com/package/@faasjs/dev)
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install @faasjs/dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- Vite integration for in-process FaasJS API during local development.
|
|
17
|
+
- PGlite helpers for lightweight database setup in tests.
|
|
18
|
+
- Test helpers to invoke and assert FaasJS functions.
|
|
19
|
+
|
|
20
|
+
## Usage: Vite integration
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { viteFaasJsServer } from '@faasjs/dev'
|
|
24
|
+
|
|
25
|
+
export default defineConfig({
|
|
26
|
+
plugins: [viteFaasJsServer()],
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Usage: PGlite helpers
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
createPgliteKnex,
|
|
35
|
+
mountFaasKnex,
|
|
36
|
+
unmountFaasKnex,
|
|
37
|
+
} from '@faasjs/dev'
|
|
38
|
+
|
|
39
|
+
const db = createPgliteKnex()
|
|
40
|
+
mountFaasKnex(db)
|
|
41
|
+
|
|
42
|
+
// run tests...
|
|
43
|
+
|
|
44
|
+
await db.destroy()
|
|
45
|
+
unmountFaasKnex()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage: Test helpers
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { test } from '@faasjs/dev'
|
|
52
|
+
import Func from '../demo.func.ts'
|
|
53
|
+
|
|
54
|
+
const func = test(Func)
|
|
55
|
+
const response = await func.JSONhandler({ name: 'FaasJS' })
|
|
56
|
+
|
|
57
|
+
expect(response.statusCode).toBe(200)
|
|
58
|
+
expect(response.data).toEqual({ message: 'Hello, FaasJS' })
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
- Vite: [viteFaasJsServer](functions/viteFaasJsServer.md), [ViteFaasJsServerOptions](type-aliases/ViteFaasJsServerOptions.md)
|
|
64
|
+
- PGlite: [createPgliteKnex](functions/createPgliteKnex.md), [mountFaasKnex](functions/mountFaasKnex.md), [runPgliteSql](functions/runPgliteSql.md), [runPgliteSqlFile](functions/runPgliteSqlFile.md), [unmountFaasKnex](functions/unmountFaasKnex.md), [MountFaasKnexOptions](type-aliases/MountFaasKnexOptions.md)
|
|
65
|
+
- Test: [test](functions/test.md), [FuncWarper](classes/FuncWarper.md), [streamToString](functions/streamToString.md)
|
|
66
|
+
|
|
67
|
+
## Functions
|
|
68
|
+
|
|
69
|
+
- [createPgliteKnex](functions/createPgliteKnex.md)
|
|
70
|
+
- [defineFunc](functions/defineFunc.md)
|
|
71
|
+
- [mountFaasKnex](functions/mountFaasKnex.md)
|
|
72
|
+
- [nameFunc](functions/nameFunc.md)
|
|
73
|
+
- [parseFuncFilenameFromStack](functions/parseFuncFilenameFromStack.md)
|
|
74
|
+
- [runPgliteSql](functions/runPgliteSql.md)
|
|
75
|
+
- [runPgliteSqlFile](functions/runPgliteSqlFile.md)
|
|
76
|
+
- [streamToString](functions/streamToString.md)
|
|
77
|
+
- [test](functions/test.md)
|
|
78
|
+
- [unmountFaasKnex](functions/unmountFaasKnex.md)
|
|
79
|
+
- [useFunc](functions/useFunc.md)
|
|
80
|
+
- [usePlugin](functions/usePlugin.md)
|
|
81
|
+
- [viteFaasJsServer](functions/viteFaasJsServer.md)
|
|
82
|
+
|
|
83
|
+
## Classes
|
|
84
|
+
|
|
85
|
+
- [Func](classes/Func.md)
|
|
86
|
+
- [FuncWarper](classes/FuncWarper.md)
|
|
87
|
+
|
|
88
|
+
## Type Aliases
|
|
89
|
+
|
|
90
|
+
- [Config](type-aliases/Config.md)
|
|
91
|
+
- [ExportedHandler](type-aliases/ExportedHandler.md)
|
|
92
|
+
- [FuncConfig](type-aliases/FuncConfig.md)
|
|
93
|
+
- [FuncEventType](type-aliases/FuncEventType.md)
|
|
94
|
+
- [FuncReturnType](type-aliases/FuncReturnType.md)
|
|
95
|
+
- [Handler](type-aliases/Handler.md)
|
|
96
|
+
- [InvokeData](type-aliases/InvokeData.md)
|
|
97
|
+
- [LifeCycleKey](type-aliases/LifeCycleKey.md)
|
|
98
|
+
- [MountData](type-aliases/MountData.md)
|
|
99
|
+
- [MountFaasKnexOptions](type-aliases/MountFaasKnexOptions.md)
|
|
100
|
+
- [Next](type-aliases/Next.md)
|
|
101
|
+
- [Plugin](type-aliases/Plugin.md)
|
|
102
|
+
- [UseifyPlugin](type-aliases/UseifyPlugin.md)
|
|
103
|
+
- [ViteFaasJsServerOptions](type-aliases/ViteFaasJsServerOptions.md)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs');
|
|
4
|
+
var knex = require('knex');
|
|
5
|
+
var PgliteDialect = require('knex-pglite');
|
|
6
|
+
var zlib = require('zlib');
|
|
7
|
+
var deep_merge = require('@faasjs/deep_merge');
|
|
8
|
+
var http = require('@faasjs/http');
|
|
9
|
+
var load = require('@faasjs/load');
|
|
10
|
+
var logger = require('@faasjs/logger');
|
|
11
|
+
var func_star = require('@faasjs/func');
|
|
12
|
+
var path = require('path');
|
|
13
|
+
var server = require('@faasjs/server');
|
|
14
|
+
|
|
15
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
16
|
+
|
|
17
|
+
function _interopNamespace(e) {
|
|
18
|
+
if (e && e.__esModule) return e;
|
|
19
|
+
var n = Object.create(null);
|
|
20
|
+
if (e) {
|
|
21
|
+
Object.keys(e).forEach(function (k) {
|
|
22
|
+
if (k !== 'default') {
|
|
23
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
24
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () { return e[k]; }
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
n.default = e;
|
|
32
|
+
return Object.freeze(n);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
var PgliteDialect__default = /*#__PURE__*/_interopDefault(PgliteDialect);
|
|
36
|
+
var func_star__namespace = /*#__PURE__*/_interopNamespace(func_star);
|
|
37
|
+
|
|
38
|
+
var __defProp = Object.defineProperty;
|
|
39
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
40
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
41
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
42
|
+
var __export = (target, all) => {
|
|
43
|
+
for (var name in all)
|
|
44
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
45
|
+
};
|
|
46
|
+
var __copyProps = (to, from, except, desc) => {
|
|
47
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
48
|
+
for (let key of __getOwnPropNames(from))
|
|
49
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
50
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
51
|
+
}
|
|
52
|
+
return to;
|
|
53
|
+
};
|
|
54
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget);
|
|
55
|
+
|
|
56
|
+
// src/index.ts
|
|
57
|
+
var index_exports = {};
|
|
58
|
+
__export(index_exports, {
|
|
59
|
+
FuncWarper: () => FuncWarper,
|
|
60
|
+
createPgliteKnex: () => createPgliteKnex,
|
|
61
|
+
mountFaasKnex: () => mountFaasKnex,
|
|
62
|
+
runPgliteSql: () => runPgliteSql,
|
|
63
|
+
runPgliteSqlFile: () => runPgliteSqlFile,
|
|
64
|
+
streamToString: () => streamToString,
|
|
65
|
+
test: () => test,
|
|
66
|
+
unmountFaasKnex: () => unmountFaasKnex,
|
|
67
|
+
viteFaasJsServer: () => viteFaasJsServer
|
|
68
|
+
});
|
|
69
|
+
function createPgliteKnex(config = {}, connection = {}) {
|
|
70
|
+
return knex.knex({
|
|
71
|
+
...config,
|
|
72
|
+
client: PgliteDialect__default.default,
|
|
73
|
+
connection
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function mountFaasKnex(db, options = {}) {
|
|
77
|
+
const globalWithFaasKnex = globalThis;
|
|
78
|
+
const name = options.name || "knex";
|
|
79
|
+
if (!globalWithFaasKnex.FaasJS_Knex) globalWithFaasKnex.FaasJS_Knex = {};
|
|
80
|
+
globalWithFaasKnex.FaasJS_Knex[name] = {
|
|
81
|
+
adapter: db,
|
|
82
|
+
query: db,
|
|
83
|
+
config: options.config || {}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function unmountFaasKnex(name = "knex") {
|
|
87
|
+
const globalWithFaasKnex = globalThis;
|
|
88
|
+
if (!globalWithFaasKnex.FaasJS_Knex) return;
|
|
89
|
+
delete globalWithFaasKnex.FaasJS_Knex[name];
|
|
90
|
+
}
|
|
91
|
+
async function runPgliteSql(db, sql) {
|
|
92
|
+
if (!sql.trim()) return;
|
|
93
|
+
await db.raw(sql);
|
|
94
|
+
}
|
|
95
|
+
async function runPgliteSqlFile(db, filePath, options = {}) {
|
|
96
|
+
const stripUuidOsspExtension = options.stripUuidOsspExtension !== false;
|
|
97
|
+
let sql = fs.readFileSync(filePath, "utf8");
|
|
98
|
+
if (stripUuidOsspExtension)
|
|
99
|
+
sql = sql.replace(/CREATE EXTENSION IF NOT EXISTS "uuid-ossp";\s*/gi, "");
|
|
100
|
+
await runPgliteSql(db, sql);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/test.ts
|
|
104
|
+
var test_exports = {};
|
|
105
|
+
__export(test_exports, {
|
|
106
|
+
FuncWarper: () => FuncWarper,
|
|
107
|
+
streamToString: () => streamToString,
|
|
108
|
+
test: () => test
|
|
109
|
+
});
|
|
110
|
+
__reExport(test_exports, func_star__namespace);
|
|
111
|
+
async function streamToString(stream) {
|
|
112
|
+
if (!(stream instanceof ReadableStream))
|
|
113
|
+
throw new TypeError("stream must be a ReadableStream instance");
|
|
114
|
+
const reader = stream.getReader();
|
|
115
|
+
const chunks = [];
|
|
116
|
+
try {
|
|
117
|
+
while (true) {
|
|
118
|
+
const { done, value } = await reader.read();
|
|
119
|
+
if (done) break;
|
|
120
|
+
if (value) chunks.push(value);
|
|
121
|
+
}
|
|
122
|
+
} finally {
|
|
123
|
+
reader.releaseLock();
|
|
124
|
+
}
|
|
125
|
+
const decoder = new TextDecoder();
|
|
126
|
+
return decoder.decode(Buffer.concat(chunks.map((c) => Buffer.from(c))));
|
|
127
|
+
}
|
|
128
|
+
var FuncWarper = class {
|
|
129
|
+
file;
|
|
130
|
+
staging;
|
|
131
|
+
logger;
|
|
132
|
+
func;
|
|
133
|
+
config;
|
|
134
|
+
plugins;
|
|
135
|
+
_handler;
|
|
136
|
+
/**
|
|
137
|
+
* @param file {string} Full file path
|
|
138
|
+
* @param func {Func} A FaasJs function
|
|
139
|
+
* ```ts
|
|
140
|
+
* import { FuncWarper } from '@faasjs/dev'
|
|
141
|
+
*
|
|
142
|
+
* new FuncWarper(__dirname + '/../demo.func.ts')
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
constructor(initBy) {
|
|
146
|
+
this.staging = process.env.FaasEnv;
|
|
147
|
+
this.logger = new logger.Logger("TestCase");
|
|
148
|
+
this.func = initBy.default ? initBy.default : initBy;
|
|
149
|
+
if (this.func.filename)
|
|
150
|
+
this.func.config = deep_merge.deepMerge(
|
|
151
|
+
load.loadConfig(process.cwd(), initBy.filename, this.staging, this.logger),
|
|
152
|
+
initBy.config
|
|
153
|
+
);
|
|
154
|
+
this.plugins = this.func.plugins || [];
|
|
155
|
+
for (const plugin of this.plugins) {
|
|
156
|
+
if (["handler", "config", "plugins", "logger", "mount"].includes(
|
|
157
|
+
plugin.type
|
|
158
|
+
))
|
|
159
|
+
continue;
|
|
160
|
+
this[plugin.type] = plugin;
|
|
161
|
+
}
|
|
162
|
+
this._handler = this.func.export().handler;
|
|
163
|
+
}
|
|
164
|
+
async mount(handler) {
|
|
165
|
+
if (!this.func.mounted) await this.func.mount();
|
|
166
|
+
if (handler) await handler(this);
|
|
167
|
+
}
|
|
168
|
+
async handler(event = /* @__PURE__ */ Object.create(null), context = /* @__PURE__ */ Object.create(null)) {
|
|
169
|
+
await this.mount();
|
|
170
|
+
const response = await this._handler(event, context);
|
|
171
|
+
this.logger.debug("response: %j", response);
|
|
172
|
+
return response;
|
|
173
|
+
}
|
|
174
|
+
async JSONhandler(body, options = /* @__PURE__ */ Object.create(null)) {
|
|
175
|
+
await this.mount();
|
|
176
|
+
const headers = options.headers || /* @__PURE__ */ Object.create(null);
|
|
177
|
+
if (this.http && this.http instanceof http.Http) {
|
|
178
|
+
if (options.cookie)
|
|
179
|
+
for (const key in options.cookie)
|
|
180
|
+
this.http.cookie.write(key, options.cookie[key]);
|
|
181
|
+
if (options.session) {
|
|
182
|
+
for (const key in options.session)
|
|
183
|
+
this.http.session.write(key, options.session[key]);
|
|
184
|
+
this.http.session.update();
|
|
185
|
+
}
|
|
186
|
+
const cookie = this.http.cookie.headers()["Set-Cookie"]?.map((c) => c.split(";")[0]).join(";");
|
|
187
|
+
if (cookie)
|
|
188
|
+
if (headers.cookie) headers.cookie += `;${cookie}`;
|
|
189
|
+
else headers.cookie = cookie;
|
|
190
|
+
}
|
|
191
|
+
const response = await this._handler({
|
|
192
|
+
httpMethod: "POST",
|
|
193
|
+
headers: Object.assign({ "content-type": "application/json" }, headers),
|
|
194
|
+
body: typeof body === "string" ? body : JSON.stringify(body)
|
|
195
|
+
});
|
|
196
|
+
if (response?.body instanceof ReadableStream) {
|
|
197
|
+
let stream = response.body;
|
|
198
|
+
const encoding = response.headers?.["Content-Encoding"] || response.headers?.["content-encoding"];
|
|
199
|
+
if (encoding) {
|
|
200
|
+
const chunks = [];
|
|
201
|
+
const reader = stream.getReader();
|
|
202
|
+
try {
|
|
203
|
+
while (true) {
|
|
204
|
+
const { done, value } = await reader.read();
|
|
205
|
+
if (done) break;
|
|
206
|
+
if (value) chunks.push(value);
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
this.logger.error("Failed to read ReadableStream: %s", error);
|
|
210
|
+
response.body = JSON.stringify({
|
|
211
|
+
error: { message: error.message }
|
|
212
|
+
});
|
|
213
|
+
response.error = { message: error.message };
|
|
214
|
+
response.statusCode = 500;
|
|
215
|
+
reader.releaseLock();
|
|
216
|
+
return response;
|
|
217
|
+
}
|
|
218
|
+
reader.releaseLock();
|
|
219
|
+
const compressedBuffer = Buffer.concat(chunks);
|
|
220
|
+
try {
|
|
221
|
+
let decompressed;
|
|
222
|
+
if (encoding === "br") {
|
|
223
|
+
decompressed = zlib.brotliDecompressSync(compressedBuffer);
|
|
224
|
+
} else if (encoding === "gzip") {
|
|
225
|
+
decompressed = zlib.gunzipSync(compressedBuffer);
|
|
226
|
+
} else if (encoding === "deflate") {
|
|
227
|
+
decompressed = zlib.inflateSync(compressedBuffer);
|
|
228
|
+
} else {
|
|
229
|
+
throw new Error(`Unsupported encoding: ${encoding}`);
|
|
230
|
+
}
|
|
231
|
+
stream = new ReadableStream({
|
|
232
|
+
start(controller) {
|
|
233
|
+
controller.enqueue(new Uint8Array(decompressed));
|
|
234
|
+
controller.close();
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
} catch (error) {
|
|
238
|
+
this.logger.error("Failed to decompress: %s", error);
|
|
239
|
+
response.body = JSON.stringify({
|
|
240
|
+
error: { message: error.message }
|
|
241
|
+
});
|
|
242
|
+
response.error = { message: error.message };
|
|
243
|
+
response.statusCode = 500;
|
|
244
|
+
return response;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
response.body = await streamToString(stream);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
this.logger.error("Failed to decode ReadableStream: %s", error);
|
|
251
|
+
response.body = JSON.stringify({
|
|
252
|
+
error: { message: error.message }
|
|
253
|
+
});
|
|
254
|
+
response.error = { message: error.message };
|
|
255
|
+
response.statusCode = 500;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (response?.headers && response.body && response.headers["content-type"]?.includes("json")) {
|
|
259
|
+
const parsedBody = JSON.parse(response.body);
|
|
260
|
+
response.data = parsedBody.data;
|
|
261
|
+
response.error = parsedBody.error;
|
|
262
|
+
}
|
|
263
|
+
if (this.http) {
|
|
264
|
+
response.cookie = this.http.cookie.content;
|
|
265
|
+
response.session = this.http.session.content;
|
|
266
|
+
}
|
|
267
|
+
this.logger.debug("response: %j", response);
|
|
268
|
+
return response;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
function test(initBy) {
|
|
272
|
+
const warper = new FuncWarper(initBy);
|
|
273
|
+
warper.mount = warper.mount.bind(warper);
|
|
274
|
+
warper.handler = warper.handler.bind(warper);
|
|
275
|
+
warper.JSONhandler = warper.JSONhandler.bind(warper);
|
|
276
|
+
return warper;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/index.ts
|
|
280
|
+
__reExport(index_exports, test_exports);
|
|
281
|
+
function normalizeBase(base) {
|
|
282
|
+
const normalized = base.startsWith("/") ? base : `/${base}`;
|
|
283
|
+
if (normalized === "/") return "/";
|
|
284
|
+
return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
|
|
285
|
+
}
|
|
286
|
+
function stripBase(url, base) {
|
|
287
|
+
if (base === "/") return url;
|
|
288
|
+
const queryIndex = url.indexOf("?");
|
|
289
|
+
const pathname = queryIndex >= 0 ? url.slice(0, queryIndex) : url;
|
|
290
|
+
const search = queryIndex >= 0 ? url.slice(queryIndex) : "";
|
|
291
|
+
if (pathname === base) return `/${search}`;
|
|
292
|
+
if (pathname.startsWith(`${base}/`))
|
|
293
|
+
return `${pathname.slice(base.length)}${search}`;
|
|
294
|
+
return url;
|
|
295
|
+
}
|
|
296
|
+
function viteFaasJsServer(options = {}) {
|
|
297
|
+
let config;
|
|
298
|
+
let server$1 = null;
|
|
299
|
+
const logger$1 = new logger.Logger("FaasJs:Vite");
|
|
300
|
+
return {
|
|
301
|
+
name: "vite:faasjs",
|
|
302
|
+
enforce: "pre",
|
|
303
|
+
configResolved(resolvedConfig) {
|
|
304
|
+
const root = options.root || resolvedConfig.root;
|
|
305
|
+
const base = normalizeBase(options.base || resolvedConfig.base);
|
|
306
|
+
config = {
|
|
307
|
+
root,
|
|
308
|
+
base
|
|
309
|
+
};
|
|
310
|
+
},
|
|
311
|
+
configureServer: async ({ middlewares }) => {
|
|
312
|
+
if (process.env.VITEST) {
|
|
313
|
+
logger$1.debug("Skipping faas server in vitest environment");
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (!config) throw new Error("viteFaasJsServer: config is not resolved");
|
|
317
|
+
server$1 = new server.Server(path.join(config.root, "src"));
|
|
318
|
+
middlewares.use(async (req, res, next) => {
|
|
319
|
+
if (!req.url || req.method !== "POST" || !server$1) return next();
|
|
320
|
+
const originalUrl = req.url;
|
|
321
|
+
const strippedUrl = stripBase(req.url, config.base);
|
|
322
|
+
req.url = strippedUrl;
|
|
323
|
+
try {
|
|
324
|
+
logger$1.debug(`Request ${req.url}`);
|
|
325
|
+
await server$1.handle(req, res, {
|
|
326
|
+
requestedAt: Date.now()
|
|
327
|
+
});
|
|
328
|
+
} catch (error) {
|
|
329
|
+
logger$1.error(error);
|
|
330
|
+
if (!res.headersSent && !res.writableEnded) {
|
|
331
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
332
|
+
res.write(
|
|
333
|
+
JSON.stringify({
|
|
334
|
+
error: { message: "Internal Server Error" }
|
|
335
|
+
})
|
|
336
|
+
);
|
|
337
|
+
res.end();
|
|
338
|
+
}
|
|
339
|
+
} finally {
|
|
340
|
+
req.url = originalUrl;
|
|
341
|
+
}
|
|
342
|
+
if (!res.writableEnded) next();
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
exports.FuncWarper = FuncWarper;
|
|
349
|
+
exports.createPgliteKnex = createPgliteKnex;
|
|
350
|
+
exports.mountFaasKnex = mountFaasKnex;
|
|
351
|
+
exports.runPgliteSql = runPgliteSql;
|
|
352
|
+
exports.runPgliteSqlFile = runPgliteSqlFile;
|
|
353
|
+
exports.streamToString = streamToString;
|
|
354
|
+
exports.test = test;
|
|
355
|
+
exports.unmountFaasKnex = unmountFaasKnex;
|
|
356
|
+
exports.viteFaasJsServer = viteFaasJsServer;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { Knex } from 'knex';
|
|
2
|
+
import { Func, Config, Plugin } from '@faasjs/func';
|
|
3
|
+
export * from '@faasjs/func';
|
|
4
|
+
import { Logger } from '@faasjs/logger';
|
|
5
|
+
import { Plugin as Plugin$1 } from 'vite';
|
|
6
|
+
|
|
7
|
+
type MountFaasKnexOptions = {
|
|
8
|
+
/** key of `globalThis.FaasJS_Knex`, default is `knex` */
|
|
9
|
+
name?: string;
|
|
10
|
+
/** optional config metadata passed through to `@faasjs/knex` */
|
|
11
|
+
config?: Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Create a knex instance backed by `knex-pglite`.
|
|
15
|
+
*/
|
|
16
|
+
declare function createPgliteKnex(config?: Partial<Knex.Config>, connection?: Record<string, unknown>): Knex;
|
|
17
|
+
/**
|
|
18
|
+
* Mount a knex adapter to `globalThis.FaasJS_Knex` for `@faasjs/knex`.
|
|
19
|
+
*/
|
|
20
|
+
declare function mountFaasKnex(db: Knex, options?: MountFaasKnexOptions): void;
|
|
21
|
+
/**
|
|
22
|
+
* Remove mounted knex adapter from `globalThis.FaasJS_Knex`.
|
|
23
|
+
*/
|
|
24
|
+
declare function unmountFaasKnex(name?: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Run a SQL string on a PGlite-backed knex instance.
|
|
27
|
+
*/
|
|
28
|
+
declare function runPgliteSql(db: Knex, sql: string): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Run SQL from file on a PGlite-backed knex instance.
|
|
31
|
+
*/
|
|
32
|
+
declare function runPgliteSqlFile(db: Knex, filePath: string, options?: {
|
|
33
|
+
stripUuidOsspExtension?: boolean;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convert ReadableStream to string.
|
|
38
|
+
* @param stream {ReadableStream<Uint8Array>} The stream to convert
|
|
39
|
+
* @returns {Promise<string>} The string content of stream
|
|
40
|
+
* @throws {TypeError} If stream is not a ReadableStream instance
|
|
41
|
+
*/
|
|
42
|
+
declare function streamToString(stream: ReadableStream<Uint8Array>): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Test wrapper for a function.
|
|
45
|
+
*
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { FuncWarper } from '@faasjs/dev'
|
|
48
|
+
* import Func from '../demo.func.ts'
|
|
49
|
+
*
|
|
50
|
+
* const func = new FuncWarper(Func)
|
|
51
|
+
*
|
|
52
|
+
* expect(await func.handler()).toEqual('Hello, world')
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
declare class FuncWarper {
|
|
56
|
+
[key: string]: any;
|
|
57
|
+
readonly file: string;
|
|
58
|
+
readonly staging: string;
|
|
59
|
+
readonly logger: Logger;
|
|
60
|
+
readonly func: Func;
|
|
61
|
+
readonly config: Config;
|
|
62
|
+
readonly plugins: Plugin[];
|
|
63
|
+
private readonly _handler;
|
|
64
|
+
/**
|
|
65
|
+
* @param file {string} Full file path
|
|
66
|
+
* @param func {Func} A FaasJs function
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { FuncWarper } from '@faasjs/dev'
|
|
69
|
+
*
|
|
70
|
+
* new FuncWarper(__dirname + '/../demo.func.ts')
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
constructor(initBy: Func);
|
|
74
|
+
mount(handler?: (func: FuncWarper) => Promise<void> | void): Promise<void>;
|
|
75
|
+
handler<TResult = any>(event?: any, context?: any): Promise<TResult>;
|
|
76
|
+
JSONhandler<TData = any>(body?: {
|
|
77
|
+
[key: string]: any;
|
|
78
|
+
}, options?: {
|
|
79
|
+
headers?: {
|
|
80
|
+
[key: string]: any;
|
|
81
|
+
};
|
|
82
|
+
cookie?: {
|
|
83
|
+
[key: string]: any;
|
|
84
|
+
};
|
|
85
|
+
session?: {
|
|
86
|
+
[key: string]: any;
|
|
87
|
+
};
|
|
88
|
+
}): Promise<{
|
|
89
|
+
statusCode: number;
|
|
90
|
+
headers: {
|
|
91
|
+
[key: string]: string;
|
|
92
|
+
};
|
|
93
|
+
cookie?: Record<string, any>;
|
|
94
|
+
session?: Record<string, any>;
|
|
95
|
+
body: any;
|
|
96
|
+
data?: TData;
|
|
97
|
+
error?: {
|
|
98
|
+
message: string;
|
|
99
|
+
};
|
|
100
|
+
}>;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A simple way to wrap a FaasJS function.
|
|
104
|
+
* @param initBy {Func} Full file path or a FaasJs function
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* import { test } from '@faasjs/dev'
|
|
108
|
+
* import Func from '../demo.func.ts'
|
|
109
|
+
*
|
|
110
|
+
* const func = test(Func)
|
|
111
|
+
*
|
|
112
|
+
* expect(await func.handler()).toEqual('Hello, world')
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
declare function test(initBy: Func): FuncWarper;
|
|
116
|
+
|
|
117
|
+
type ViteFaasJsServerOptions = {
|
|
118
|
+
/** faas project root path, default is vite's root */
|
|
119
|
+
root: string;
|
|
120
|
+
/** faas server base path, default is vite's base */
|
|
121
|
+
base: string;
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Create a Vite plugin that proxies POST requests to an in-process FaasJS server.
|
|
125
|
+
*
|
|
126
|
+
* It resolves project root/base from Vite config and strips `base` from request URL
|
|
127
|
+
* before forwarding to `@faasjs/server`.
|
|
128
|
+
*/
|
|
129
|
+
declare function viteFaasJsServer(options?: Partial<ViteFaasJsServerOptions> & Record<string, unknown>): Plugin$1;
|
|
130
|
+
|
|
131
|
+
export { FuncWarper, type MountFaasKnexOptions, type ViteFaasJsServerOptions, createPgliteKnex, mountFaasKnex, runPgliteSql, runPgliteSqlFile, streamToString, test, unmountFaasKnex, viteFaasJsServer };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { knex } from 'knex';
|
|
3
|
+
import PgliteDialect from 'knex-pglite';
|
|
4
|
+
import { brotliDecompressSync, gunzipSync, inflateSync } from 'zlib';
|
|
5
|
+
import { deepMerge } from '@faasjs/deep_merge';
|
|
6
|
+
import { Http } from '@faasjs/http';
|
|
7
|
+
import { loadConfig } from '@faasjs/load';
|
|
8
|
+
import { Logger } from '@faasjs/logger';
|
|
9
|
+
import * as func_star from '@faasjs/func';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { Server } from '@faasjs/server';
|
|
12
|
+
|
|
13
|
+
var __defProp = Object.defineProperty;
|
|
14
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
15
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
16
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
|
+
FuncWarper: () => FuncWarper,
|
|
35
|
+
createPgliteKnex: () => createPgliteKnex,
|
|
36
|
+
mountFaasKnex: () => mountFaasKnex,
|
|
37
|
+
runPgliteSql: () => runPgliteSql,
|
|
38
|
+
runPgliteSqlFile: () => runPgliteSqlFile,
|
|
39
|
+
streamToString: () => streamToString,
|
|
40
|
+
test: () => test,
|
|
41
|
+
unmountFaasKnex: () => unmountFaasKnex,
|
|
42
|
+
viteFaasJsServer: () => viteFaasJsServer
|
|
43
|
+
});
|
|
44
|
+
function createPgliteKnex(config = {}, connection = {}) {
|
|
45
|
+
return knex({
|
|
46
|
+
...config,
|
|
47
|
+
client: PgliteDialect,
|
|
48
|
+
connection
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function mountFaasKnex(db, options = {}) {
|
|
52
|
+
const globalWithFaasKnex = globalThis;
|
|
53
|
+
const name = options.name || "knex";
|
|
54
|
+
if (!globalWithFaasKnex.FaasJS_Knex) globalWithFaasKnex.FaasJS_Knex = {};
|
|
55
|
+
globalWithFaasKnex.FaasJS_Knex[name] = {
|
|
56
|
+
adapter: db,
|
|
57
|
+
query: db,
|
|
58
|
+
config: options.config || {}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function unmountFaasKnex(name = "knex") {
|
|
62
|
+
const globalWithFaasKnex = globalThis;
|
|
63
|
+
if (!globalWithFaasKnex.FaasJS_Knex) return;
|
|
64
|
+
delete globalWithFaasKnex.FaasJS_Knex[name];
|
|
65
|
+
}
|
|
66
|
+
async function runPgliteSql(db, sql) {
|
|
67
|
+
if (!sql.trim()) return;
|
|
68
|
+
await db.raw(sql);
|
|
69
|
+
}
|
|
70
|
+
async function runPgliteSqlFile(db, filePath, options = {}) {
|
|
71
|
+
const stripUuidOsspExtension = options.stripUuidOsspExtension !== false;
|
|
72
|
+
let sql = readFileSync(filePath, "utf8");
|
|
73
|
+
if (stripUuidOsspExtension)
|
|
74
|
+
sql = sql.replace(/CREATE EXTENSION IF NOT EXISTS "uuid-ossp";\s*/gi, "");
|
|
75
|
+
await runPgliteSql(db, sql);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/test.ts
|
|
79
|
+
var test_exports = {};
|
|
80
|
+
__export(test_exports, {
|
|
81
|
+
FuncWarper: () => FuncWarper,
|
|
82
|
+
streamToString: () => streamToString,
|
|
83
|
+
test: () => test
|
|
84
|
+
});
|
|
85
|
+
__reExport(test_exports, func_star);
|
|
86
|
+
async function streamToString(stream) {
|
|
87
|
+
if (!(stream instanceof ReadableStream))
|
|
88
|
+
throw new TypeError("stream must be a ReadableStream instance");
|
|
89
|
+
const reader = stream.getReader();
|
|
90
|
+
const chunks = [];
|
|
91
|
+
try {
|
|
92
|
+
while (true) {
|
|
93
|
+
const { done, value } = await reader.read();
|
|
94
|
+
if (done) break;
|
|
95
|
+
if (value) chunks.push(value);
|
|
96
|
+
}
|
|
97
|
+
} finally {
|
|
98
|
+
reader.releaseLock();
|
|
99
|
+
}
|
|
100
|
+
const decoder = new TextDecoder();
|
|
101
|
+
return decoder.decode(Buffer.concat(chunks.map((c) => Buffer.from(c))));
|
|
102
|
+
}
|
|
103
|
+
var FuncWarper = class {
|
|
104
|
+
file;
|
|
105
|
+
staging;
|
|
106
|
+
logger;
|
|
107
|
+
func;
|
|
108
|
+
config;
|
|
109
|
+
plugins;
|
|
110
|
+
_handler;
|
|
111
|
+
/**
|
|
112
|
+
* @param file {string} Full file path
|
|
113
|
+
* @param func {Func} A FaasJs function
|
|
114
|
+
* ```ts
|
|
115
|
+
* import { FuncWarper } from '@faasjs/dev'
|
|
116
|
+
*
|
|
117
|
+
* new FuncWarper(__dirname + '/../demo.func.ts')
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
constructor(initBy) {
|
|
121
|
+
this.staging = process.env.FaasEnv;
|
|
122
|
+
this.logger = new Logger("TestCase");
|
|
123
|
+
this.func = initBy.default ? initBy.default : initBy;
|
|
124
|
+
if (this.func.filename)
|
|
125
|
+
this.func.config = deepMerge(
|
|
126
|
+
loadConfig(process.cwd(), initBy.filename, this.staging, this.logger),
|
|
127
|
+
initBy.config
|
|
128
|
+
);
|
|
129
|
+
this.plugins = this.func.plugins || [];
|
|
130
|
+
for (const plugin of this.plugins) {
|
|
131
|
+
if (["handler", "config", "plugins", "logger", "mount"].includes(
|
|
132
|
+
plugin.type
|
|
133
|
+
))
|
|
134
|
+
continue;
|
|
135
|
+
this[plugin.type] = plugin;
|
|
136
|
+
}
|
|
137
|
+
this._handler = this.func.export().handler;
|
|
138
|
+
}
|
|
139
|
+
async mount(handler) {
|
|
140
|
+
if (!this.func.mounted) await this.func.mount();
|
|
141
|
+
if (handler) await handler(this);
|
|
142
|
+
}
|
|
143
|
+
async handler(event = /* @__PURE__ */ Object.create(null), context = /* @__PURE__ */ Object.create(null)) {
|
|
144
|
+
await this.mount();
|
|
145
|
+
const response = await this._handler(event, context);
|
|
146
|
+
this.logger.debug("response: %j", response);
|
|
147
|
+
return response;
|
|
148
|
+
}
|
|
149
|
+
async JSONhandler(body, options = /* @__PURE__ */ Object.create(null)) {
|
|
150
|
+
await this.mount();
|
|
151
|
+
const headers = options.headers || /* @__PURE__ */ Object.create(null);
|
|
152
|
+
if (this.http && this.http instanceof Http) {
|
|
153
|
+
if (options.cookie)
|
|
154
|
+
for (const key in options.cookie)
|
|
155
|
+
this.http.cookie.write(key, options.cookie[key]);
|
|
156
|
+
if (options.session) {
|
|
157
|
+
for (const key in options.session)
|
|
158
|
+
this.http.session.write(key, options.session[key]);
|
|
159
|
+
this.http.session.update();
|
|
160
|
+
}
|
|
161
|
+
const cookie = this.http.cookie.headers()["Set-Cookie"]?.map((c) => c.split(";")[0]).join(";");
|
|
162
|
+
if (cookie)
|
|
163
|
+
if (headers.cookie) headers.cookie += `;${cookie}`;
|
|
164
|
+
else headers.cookie = cookie;
|
|
165
|
+
}
|
|
166
|
+
const response = await this._handler({
|
|
167
|
+
httpMethod: "POST",
|
|
168
|
+
headers: Object.assign({ "content-type": "application/json" }, headers),
|
|
169
|
+
body: typeof body === "string" ? body : JSON.stringify(body)
|
|
170
|
+
});
|
|
171
|
+
if (response?.body instanceof ReadableStream) {
|
|
172
|
+
let stream = response.body;
|
|
173
|
+
const encoding = response.headers?.["Content-Encoding"] || response.headers?.["content-encoding"];
|
|
174
|
+
if (encoding) {
|
|
175
|
+
const chunks = [];
|
|
176
|
+
const reader = stream.getReader();
|
|
177
|
+
try {
|
|
178
|
+
while (true) {
|
|
179
|
+
const { done, value } = await reader.read();
|
|
180
|
+
if (done) break;
|
|
181
|
+
if (value) chunks.push(value);
|
|
182
|
+
}
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.logger.error("Failed to read ReadableStream: %s", error);
|
|
185
|
+
response.body = JSON.stringify({
|
|
186
|
+
error: { message: error.message }
|
|
187
|
+
});
|
|
188
|
+
response.error = { message: error.message };
|
|
189
|
+
response.statusCode = 500;
|
|
190
|
+
reader.releaseLock();
|
|
191
|
+
return response;
|
|
192
|
+
}
|
|
193
|
+
reader.releaseLock();
|
|
194
|
+
const compressedBuffer = Buffer.concat(chunks);
|
|
195
|
+
try {
|
|
196
|
+
let decompressed;
|
|
197
|
+
if (encoding === "br") {
|
|
198
|
+
decompressed = brotliDecompressSync(compressedBuffer);
|
|
199
|
+
} else if (encoding === "gzip") {
|
|
200
|
+
decompressed = gunzipSync(compressedBuffer);
|
|
201
|
+
} else if (encoding === "deflate") {
|
|
202
|
+
decompressed = inflateSync(compressedBuffer);
|
|
203
|
+
} else {
|
|
204
|
+
throw new Error(`Unsupported encoding: ${encoding}`);
|
|
205
|
+
}
|
|
206
|
+
stream = new ReadableStream({
|
|
207
|
+
start(controller) {
|
|
208
|
+
controller.enqueue(new Uint8Array(decompressed));
|
|
209
|
+
controller.close();
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
} catch (error) {
|
|
213
|
+
this.logger.error("Failed to decompress: %s", error);
|
|
214
|
+
response.body = JSON.stringify({
|
|
215
|
+
error: { message: error.message }
|
|
216
|
+
});
|
|
217
|
+
response.error = { message: error.message };
|
|
218
|
+
response.statusCode = 500;
|
|
219
|
+
return response;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
try {
|
|
223
|
+
response.body = await streamToString(stream);
|
|
224
|
+
} catch (error) {
|
|
225
|
+
this.logger.error("Failed to decode ReadableStream: %s", error);
|
|
226
|
+
response.body = JSON.stringify({
|
|
227
|
+
error: { message: error.message }
|
|
228
|
+
});
|
|
229
|
+
response.error = { message: error.message };
|
|
230
|
+
response.statusCode = 500;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (response?.headers && response.body && response.headers["content-type"]?.includes("json")) {
|
|
234
|
+
const parsedBody = JSON.parse(response.body);
|
|
235
|
+
response.data = parsedBody.data;
|
|
236
|
+
response.error = parsedBody.error;
|
|
237
|
+
}
|
|
238
|
+
if (this.http) {
|
|
239
|
+
response.cookie = this.http.cookie.content;
|
|
240
|
+
response.session = this.http.session.content;
|
|
241
|
+
}
|
|
242
|
+
this.logger.debug("response: %j", response);
|
|
243
|
+
return response;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
function test(initBy) {
|
|
247
|
+
const warper = new FuncWarper(initBy);
|
|
248
|
+
warper.mount = warper.mount.bind(warper);
|
|
249
|
+
warper.handler = warper.handler.bind(warper);
|
|
250
|
+
warper.JSONhandler = warper.JSONhandler.bind(warper);
|
|
251
|
+
return warper;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// src/index.ts
|
|
255
|
+
__reExport(index_exports, test_exports);
|
|
256
|
+
function normalizeBase(base) {
|
|
257
|
+
const normalized = base.startsWith("/") ? base : `/${base}`;
|
|
258
|
+
if (normalized === "/") return "/";
|
|
259
|
+
return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
|
|
260
|
+
}
|
|
261
|
+
function stripBase(url, base) {
|
|
262
|
+
if (base === "/") return url;
|
|
263
|
+
const queryIndex = url.indexOf("?");
|
|
264
|
+
const pathname = queryIndex >= 0 ? url.slice(0, queryIndex) : url;
|
|
265
|
+
const search = queryIndex >= 0 ? url.slice(queryIndex) : "";
|
|
266
|
+
if (pathname === base) return `/${search}`;
|
|
267
|
+
if (pathname.startsWith(`${base}/`))
|
|
268
|
+
return `${pathname.slice(base.length)}${search}`;
|
|
269
|
+
return url;
|
|
270
|
+
}
|
|
271
|
+
function viteFaasJsServer(options = {}) {
|
|
272
|
+
let config;
|
|
273
|
+
let server = null;
|
|
274
|
+
const logger = new Logger("FaasJs:Vite");
|
|
275
|
+
return {
|
|
276
|
+
name: "vite:faasjs",
|
|
277
|
+
enforce: "pre",
|
|
278
|
+
configResolved(resolvedConfig) {
|
|
279
|
+
const root = options.root || resolvedConfig.root;
|
|
280
|
+
const base = normalizeBase(options.base || resolvedConfig.base);
|
|
281
|
+
config = {
|
|
282
|
+
root,
|
|
283
|
+
base
|
|
284
|
+
};
|
|
285
|
+
},
|
|
286
|
+
configureServer: async ({ middlewares }) => {
|
|
287
|
+
if (process.env.VITEST) {
|
|
288
|
+
logger.debug("Skipping faas server in vitest environment");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (!config) throw new Error("viteFaasJsServer: config is not resolved");
|
|
292
|
+
server = new Server(join(config.root, "src"));
|
|
293
|
+
middlewares.use(async (req, res, next) => {
|
|
294
|
+
if (!req.url || req.method !== "POST" || !server) return next();
|
|
295
|
+
const originalUrl = req.url;
|
|
296
|
+
const strippedUrl = stripBase(req.url, config.base);
|
|
297
|
+
req.url = strippedUrl;
|
|
298
|
+
try {
|
|
299
|
+
logger.debug(`Request ${req.url}`);
|
|
300
|
+
await server.handle(req, res, {
|
|
301
|
+
requestedAt: Date.now()
|
|
302
|
+
});
|
|
303
|
+
} catch (error) {
|
|
304
|
+
logger.error(error);
|
|
305
|
+
if (!res.headersSent && !res.writableEnded) {
|
|
306
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
307
|
+
res.write(
|
|
308
|
+
JSON.stringify({
|
|
309
|
+
error: { message: "Internal Server Error" }
|
|
310
|
+
})
|
|
311
|
+
);
|
|
312
|
+
res.end();
|
|
313
|
+
}
|
|
314
|
+
} finally {
|
|
315
|
+
req.url = originalUrl;
|
|
316
|
+
}
|
|
317
|
+
if (!res.writableEnded) next();
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export { FuncWarper, createPgliteKnex, mountFaasKnex, runPgliteSql, runPgliteSqlFile, streamToString, test, unmountFaasKnex, viteFaasJsServer };
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@faasjs/dev",
|
|
3
|
+
"version": "v8.0.0-beta.5",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.cjs",
|
|
7
|
+
"module": "dist/index.mjs",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://faasjs.com/doc/dev",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/faasjs/faasjs.git",
|
|
20
|
+
"directory": "packages/dev"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/faasjs/faasjs/issues"
|
|
24
|
+
},
|
|
25
|
+
"funding": "https://github.com/sponsors/faasjs",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup-node src/index.ts --config ../../tsup.config.ts"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@faasjs/deep_merge": ">=v8.0.0-beta.5",
|
|
34
|
+
"@faasjs/func": ">=v8.0.0-beta.5",
|
|
35
|
+
"@faasjs/http": ">=v8.0.0-beta.5",
|
|
36
|
+
"@faasjs/server": ">=v8.0.0-beta.5",
|
|
37
|
+
"@faasjs/knex": ">=v8.0.0-beta.5",
|
|
38
|
+
"@faasjs/load": ">=v8.0.0-beta.5",
|
|
39
|
+
"@faasjs/logger": ">=v8.0.0-beta.5",
|
|
40
|
+
"knex": "*",
|
|
41
|
+
"vite": "*",
|
|
42
|
+
"knex-pglite": "*"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@faasjs/deep_merge": ">=v8.0.0-beta.5",
|
|
46
|
+
"@faasjs/func": ">=v8.0.0-beta.5",
|
|
47
|
+
"@faasjs/http": ">=v8.0.0-beta.5",
|
|
48
|
+
"@faasjs/server": ">=v8.0.0-beta.5",
|
|
49
|
+
"@faasjs/knex": ">=v8.0.0-beta.5",
|
|
50
|
+
"@faasjs/load": ">=v8.0.0-beta.5",
|
|
51
|
+
"@faasjs/logger": ">=v8.0.0-beta.5",
|
|
52
|
+
"knex": "*",
|
|
53
|
+
"vite": "*",
|
|
54
|
+
"knex-pglite": "*"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=24.0.0",
|
|
58
|
+
"npm": ">=11.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|