@faasjs/func 8.0.0-beta.5 → 8.0.0-beta.7

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/README.md CHANGED
@@ -19,7 +19,6 @@ npm install @faasjs/func
19
19
 
20
20
  ## Functions
21
21
 
22
- - [defineFunc](functions/defineFunc.md)
23
22
  - [nameFunc](functions/nameFunc.md)
24
23
  - [parseFuncFilenameFromStack](functions/parseFuncFilenameFromStack.md)
25
24
  - [useFunc](functions/useFunc.md)
@@ -29,6 +28,10 @@ npm install @faasjs/func
29
28
 
30
29
  - [Func](classes/Func.md)
31
30
 
31
+ ## Interfaces
32
+
33
+ - [FaasPluginEventMap](interfaces/FaasPluginEventMap.md)
34
+
32
35
  ## Type Aliases
33
36
 
34
37
  - [Config](type-aliases/Config.md)
@@ -37,9 +40,14 @@ npm install @faasjs/func
37
40
  - [FuncEventType](type-aliases/FuncEventType.md)
38
41
  - [FuncReturnType](type-aliases/FuncReturnType.md)
39
42
  - [Handler](type-aliases/Handler.md)
43
+ - [InferPluginEvent](type-aliases/InferPluginEvent.md)
40
44
  - [InvokeData](type-aliases/InvokeData.md)
41
45
  - [LifeCycleKey](type-aliases/LifeCycleKey.md)
42
46
  - [MountData](type-aliases/MountData.md)
43
47
  - [Next](type-aliases/Next.md)
48
+ - [NormalizePluginType](type-aliases/NormalizePluginType.md)
44
49
  - [Plugin](type-aliases/Plugin.md)
50
+ - [ResolvePluginEvent](type-aliases/ResolvePluginEvent.md)
51
+ - [Simplify](type-aliases/Simplify.md)
52
+ - [UnionToIntersection](type-aliases/UnionToIntersection.md)
45
53
  - [UseifyPlugin](type-aliases/UseifyPlugin.md)
package/dist/index.cjs CHANGED
@@ -1,328 +1,271 @@
1
- 'use strict';
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let node_crypto = require("node:crypto");
3
+ let node_url = require("node:url");
4
+ let _faasjs_logger = require("@faasjs/logger");
2
5
 
3
- var crypto = require('crypto');
4
- var url = require('url');
5
- var logger = require('@faasjs/logger');
6
-
7
- // src/index.ts
8
-
9
- // src/plugins/run_handler/index.ts
10
- var Name = "handler";
6
+ //#region src/plugins/run_handler/index.ts
7
+ const Name = "handler";
11
8
  var RunHandler = class {
12
- type = Name;
13
- name = Name;
14
- async onInvoke(data, next) {
15
- if (data.handler)
16
- if (!data.runHandler) {
17
- try {
18
- data.response = await new Promise((resolve, reject) => {
19
- data.callback = (error, result) => {
20
- if (error) reject(error);
21
- else resolve(result);
22
- };
23
- Promise.resolve(data.handler?.(data)).then(resolve).catch(reject);
24
- });
25
- } catch (error) {
26
- data.logger.error(error);
27
- data.response = error;
28
- }
29
- data.runHandler = true;
30
- } else data.logger.warn("handler has been run");
31
- await next();
32
- }
9
+ type = Name;
10
+ name = Name;
11
+ async onInvoke(data, next) {
12
+ if (data.handler) if (!data.runHandler) {
13
+ try {
14
+ data.response = await new Promise((resolve, reject) => {
15
+ data.callback = (error, result) => {
16
+ if (error) reject(error);
17
+ else resolve(result);
18
+ };
19
+ Promise.resolve(data.handler?.(data)).then(resolve).catch(reject);
20
+ });
21
+ } catch (error) {
22
+ data.logger.error(error);
23
+ data.response = error;
24
+ }
25
+ data.runHandler = true;
26
+ } else data.logger.warn("handler has been run");
27
+ await next();
28
+ }
33
29
  };
34
30
 
35
- // src/utils.ts
31
+ //#endregion
32
+ //#region src/utils.ts
33
+ /**
34
+ * Assigns a name to a given function handler, which will be displayed in logs and error messages.
35
+ *
36
+ * @template T - The type of the function handler.
37
+ * @param {string} name - The name to assign to the function handler.
38
+ * @param {T} handler - The function handler to which the name will be assigned.
39
+ * @returns {T} - The original function handler with the assigned name.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { nameFunc } from '@faasjs/func'
44
+ *
45
+ * const handler = nameFunc('myHandler', () => {
46
+ * return 'Hello World'
47
+ * })
48
+ *
49
+ * console.log(handler.name) // => 'myHandler'
50
+ * ```
51
+ */
36
52
  function nameFunc(name, handler) {
37
- Object.defineProperty(handler, "name", { value: name });
38
- return handler;
53
+ Object.defineProperty(handler, "name", { value: name });
54
+ return handler;
39
55
  }
40
56
 
41
- // src/index.ts
57
+ //#endregion
58
+ //#region src/index.ts
59
+ /**
60
+ * FaasJS's function module.
61
+ *
62
+ * [![License: MIT](https://img.shields.io/npm/l/@faasjs/func.svg)](https://github.com/faasjs/faasjs/blob/main/packages/func/LICENSE)
63
+ * [![NPM Version](https://img.shields.io/npm/v/@faasjs/func.svg)](https://www.npmjs.com/package/@faasjs/func)
64
+ *
65
+ * ## Install
66
+ *
67
+ * ```sh
68
+ * npm install @faasjs/func
69
+ * ```
70
+ *
71
+ * ## Usage
72
+ *
73
+ * @see {@link useFunc}
74
+ *
75
+ * @packageDocumentation
76
+ */
42
77
  function parseFuncFilenameFromStack(stack) {
43
- if (!stack) return;
44
- const frame = stack.split("\n").map((line) => line.trim()).find((line) => line.includes(".func.ts"));
45
- if (!frame) return;
46
- const content = frame.replace(/^at\s+/, "");
47
- const location = content.endsWith(")") && content.includes("(") ? content.slice(content.lastIndexOf("(") + 1, -1) : content;
48
- const match = location.match(/^(.+\.func\.ts):\d+:\d+$/);
49
- if (!match) return;
50
- const filename = match[1];
51
- if (filename.startsWith("file://")) {
52
- try {
53
- return url.fileURLToPath(filename);
54
- } catch (_) {
55
- return filename;
56
- }
57
- }
58
- return filename;
59
- }
60
- function formatPluginModuleName(type) {
61
- if (type.startsWith("npm:")) return type.slice(4);
62
- if (type.startsWith("@") || type.startsWith(".") || type.startsWith("/") || type.includes(":"))
63
- return type;
64
- return `@faasjs/${type}`;
65
- }
66
- function formatPluginClassName(type) {
67
- return type.replace(/^@[^/]+\//, "").split(/[^A-Za-z0-9]+/).filter(Boolean).map((item) => item.slice(0, 1).toUpperCase() + item.slice(1)).join("");
78
+ if (!stack) return;
79
+ const frame = stack.split("\n").map((line) => line.trim()).find((line) => line.includes(".func.ts"));
80
+ if (!frame) return;
81
+ const content = frame.replace(/^at\s+/, "");
82
+ const match = (content.endsWith(")") && content.includes("(") ? content.slice(content.lastIndexOf("(") + 1, -1) : content).match(/^(.+\.func\.ts):\d+:\d+$/);
83
+ if (!match) return;
84
+ const filename = match[1];
85
+ if (filename.startsWith("file://")) try {
86
+ return (0, node_url.fileURLToPath)(filename);
87
+ } catch (_) {
88
+ return filename;
89
+ }
90
+ return filename;
68
91
  }
69
92
  var Func = class {
70
- plugins;
71
- handler;
72
- config;
73
- mounted = false;
74
- filename;
75
- autoLoadPluginsFromConfig;
76
- loadedConfigPlugins = false;
77
- cachedFunctions = /* @__PURE__ */ Object.create(null);
78
- /**
79
- * Create a cloud function
80
- * @param config {object} config
81
- * @param config.plugins {Plugin[]} plugins list
82
- * @param config.handler {Handler} business logic
83
- */
84
- constructor(config) {
85
- this.handler = config.handler;
86
- this.plugins = config.plugins || [];
87
- this.plugins.push(new RunHandler());
88
- this.autoLoadPluginsFromConfig = !!config.autoLoadPluginsFromConfig;
89
- this.config = {
90
- plugins: /* @__PURE__ */ Object.create(null)
91
- };
92
- try {
93
- this.filename = parseFuncFilenameFromStack(new Error().stack);
94
- } catch (_) {
95
- }
96
- }
97
- insertPluginBeforeRunHandler(plugin) {
98
- const index = this.plugins.findIndex(
99
- (p) => p.type === "handler" && p.name === "handler"
100
- );
101
- if (index === -1) this.plugins.push(plugin);
102
- else this.plugins.splice(index, 0, plugin);
103
- this.cachedFunctions = /* @__PURE__ */ Object.create(null);
104
- }
105
- async resolvePluginConstructor(moduleName, className, pluginName) {
106
- let mod;
107
- try {
108
- mod = await import(moduleName);
109
- } catch (error) {
110
- throw Error(
111
- `[defineFunc] Failed to load plugin "${pluginName}" from "${moduleName}": ${error.message}`
112
- );
113
- }
114
- const constructors = [];
115
- if (className && mod[className]) constructors.push(mod[className]);
116
- if (typeof mod.default === "function") constructors.push(mod.default);
117
- if (mod.default && typeof mod.default === "object" && className && mod.default[className])
118
- constructors.push(mod.default[className]);
119
- for (const key in mod) {
120
- if (key === className || key === "default") continue;
121
- constructors.push(mod[key]);
122
- }
123
- if (mod.default && typeof mod.default === "object")
124
- for (const key in mod.default) {
125
- if (key === className) continue;
126
- constructors.push(mod.default[key]);
127
- }
128
- const PluginConstructor = constructors.find(
129
- (pluginConstructor) => typeof pluginConstructor === "function" && pluginConstructor.prototype && (typeof pluginConstructor.prototype.onMount === "function" || typeof pluginConstructor.prototype.onInvoke === "function")
130
- );
131
- if (!PluginConstructor)
132
- throw Error(
133
- `[defineFunc] Failed to resolve plugin class "${className}" from "${moduleName}" for plugin "${pluginName}".`
134
- );
135
- return PluginConstructor;
136
- }
137
- async loadPluginsFromConfig(config) {
138
- const pluginConfigs = config.plugins || /* @__PURE__ */ Object.create(null);
139
- for (const key in pluginConfigs) {
140
- const rawConfig = pluginConfigs[key];
141
- const configValue = rawConfig && typeof rawConfig === "object" ? Object.assign(/* @__PURE__ */ Object.create(null), rawConfig) : /* @__PURE__ */ Object.create(null);
142
- const pluginName = typeof configValue.name === "string" && configValue.name.length ? configValue.name : key;
143
- if (this.plugins.find((plugin2) => plugin2.name === pluginName)) continue;
144
- const pluginType = typeof configValue.type === "string" && configValue.type || typeof rawConfig === "string" && rawConfig || key;
145
- const moduleName = formatPluginModuleName(pluginType);
146
- const className = formatPluginClassName(pluginType);
147
- const PluginConstructor = await this.resolvePluginConstructor(
148
- moduleName,
149
- className,
150
- pluginName
151
- );
152
- let plugin;
153
- try {
154
- plugin = new PluginConstructor({
155
- ...configValue,
156
- name: pluginName,
157
- type: pluginType
158
- });
159
- } catch (error) {
160
- throw Error(
161
- `[defineFunc] Failed to initialize plugin "${pluginName}" from "${moduleName}": ${error.message}`
162
- );
163
- }
164
- if (!plugin || typeof plugin !== "object")
165
- throw Error(
166
- `[defineFunc] Invalid plugin instance for "${pluginName}" from "${moduleName}".`
167
- );
168
- this.insertPluginBeforeRunHandler(plugin);
169
- }
170
- this.loadedConfigPlugins = true;
171
- }
172
- compose(key) {
173
- let list = [];
174
- if (this.cachedFunctions[key]) list = this.cachedFunctions[key];
175
- else {
176
- for (const plugin of this.plugins) {
177
- const handler = plugin[key];
178
- if (typeof handler === "function")
179
- list.push({
180
- key: plugin.name,
181
- handler: handler.bind(plugin)
182
- });
183
- }
184
- this.cachedFunctions[key] = list;
185
- }
186
- return async (data, next) => {
187
- let index = -1;
188
- if (!data.logger) data.logger = new logger.Logger();
189
- const dispatch = async (i) => {
190
- if (i <= index)
191
- return Promise.reject(Error("next() called multiple times"));
192
- index = i;
193
- let fn = list[i];
194
- if (i === list.length) fn = next;
195
- if (!fn) return Promise.resolve();
196
- if (typeof fn.key === "undefined") fn.key = `uname#${i}`;
197
- if (!data.context) data.context = /* @__PURE__ */ Object.create(null);
198
- if (!data.context.request_at)
199
- data.context.request_at = crypto.randomBytes(16).toString("hex");
200
- const label = `${data.context.request_id}] [${fn.key}] [${key}`;
201
- data.logger.label = label;
202
- data.logger.debug("begin");
203
- data.logger.time(label);
204
- try {
205
- const res = await Promise.resolve(
206
- fn.handler(data, dispatch.bind(null, i + 1))
207
- );
208
- data.logger.label = label;
209
- data.logger.timeEnd(label, "end");
210
- return res;
211
- } catch (err) {
212
- data.logger.label = label;
213
- data.logger.timeEnd(label, "failed");
214
- data.logger.error(err);
215
- return Promise.reject(err);
216
- }
217
- };
218
- return await dispatch(0);
219
- };
220
- }
221
- /**
222
- * First time mount the function
223
- */
224
- async mount(data = {
225
- event: /* @__PURE__ */ Object.create(null),
226
- context: /* @__PURE__ */ Object.create(null)
227
- }) {
228
- if (!data.logger) data.logger = new logger.Logger("Func");
229
- if (this.mounted) {
230
- data.logger.warn("mount() has been called, skipped.");
231
- return;
232
- }
233
- if (!data.config) data.config = this.config;
234
- if (this.autoLoadPluginsFromConfig && !this.loadedConfigPlugins)
235
- await this.loadPluginsFromConfig(data.config);
236
- data.logger.debug(
237
- `plugins: ${this.plugins.map((p) => `${p.type}#${p.name}`).join(",")}`
238
- );
239
- await this.compose("onMount")(data);
240
- this.mounted = true;
241
- }
242
- /**
243
- * Invoke the function
244
- * @param data {object} data
245
- */
246
- async invoke(data) {
247
- if (!this.mounted) await this.mount(data);
248
- try {
249
- await this.compose("onInvoke")(data);
250
- } catch (error) {
251
- data.logger.error(error);
252
- data.response = error;
253
- }
254
- }
255
- /**
256
- * Export the function
257
- */
258
- export() {
259
- const handler = async (event, context, callback) => {
260
- if (typeof context === "undefined") context = {};
261
- if (!context.request_id)
262
- context.request_id = event?.headers?.["x-faasjs-request-id"] || crypto.randomBytes(16).toString("hex");
263
- if (!context.request_at)
264
- context.request_at = crypto.randomBytes(16).toString("hex");
265
- context.callbackWaitsForEmptyEventLoop = false;
266
- const logger$1 = new logger.Logger(context.request_id);
267
- const data = {
268
- event: event ?? /* @__PURE__ */ Object.create(null),
269
- context,
270
- callback,
271
- response: void 0,
272
- handler: this.handler,
273
- logger: logger$1,
274
- config: this.config
275
- };
276
- await this.invoke(data);
277
- if (Object.prototype.toString.call(data.response) === "[object Error]")
278
- throw data.response;
279
- return data.response;
280
- };
281
- return {
282
- handler: handler.bind(this)
283
- };
284
- }
93
+ plugins;
94
+ handler;
95
+ config;
96
+ mounted = false;
97
+ filename;
98
+ cachedFunctions = Object.create(null);
99
+ /**
100
+ * Create a cloud function
101
+ * @param config {object} config
102
+ * @param config.plugins {Plugin[]} plugins list
103
+ * @param config.handler {Handler} business logic
104
+ */
105
+ constructor(config) {
106
+ if (config.handler) this.handler = config.handler;
107
+ this.plugins = config.plugins || [];
108
+ this.plugins.push(new RunHandler());
109
+ this.config = { plugins: Object.create(null) };
110
+ try {
111
+ const filename = parseFuncFilenameFromStack((/* @__PURE__ */ new Error()).stack);
112
+ if (filename) this.filename = filename;
113
+ } catch (_) {}
114
+ }
115
+ compose(key) {
116
+ let list = [];
117
+ if (this.cachedFunctions[key]) list = this.cachedFunctions[key];
118
+ else {
119
+ for (const plugin of this.plugins) {
120
+ const handler = plugin[key];
121
+ if (typeof handler === "function") list.push({
122
+ key: plugin.name,
123
+ handler: handler.bind(plugin)
124
+ });
125
+ }
126
+ this.cachedFunctions[key] = list;
127
+ }
128
+ return async (data, next) => {
129
+ let index = -1;
130
+ if (!data.logger) data.logger = new _faasjs_logger.Logger();
131
+ const dispatch = async (i) => {
132
+ if (i <= index) return Promise.reject(Error("next() called multiple times"));
133
+ index = i;
134
+ let fn = list[i];
135
+ if (i === list.length) fn = next;
136
+ if (!fn) return Promise.resolve();
137
+ if (typeof fn.key === "undefined") fn.key = `uname#${i}`;
138
+ if (!data.context) data.context = Object.create(null);
139
+ if (!data.context.request_at) data.context.request_at = (0, node_crypto.randomBytes)(16).toString("hex");
140
+ const label = `${data.context.request_id}] [${fn.key}] [${key}`;
141
+ data.logger.label = label;
142
+ data.logger.debug("begin");
143
+ data.logger.time(label);
144
+ try {
145
+ const res = await Promise.resolve(fn.handler(data, dispatch.bind(null, i + 1)));
146
+ data.logger.label = label;
147
+ data.logger.timeEnd(label, "end");
148
+ return res;
149
+ } catch (err) {
150
+ data.logger.label = label;
151
+ data.logger.timeEnd(label, "failed");
152
+ data.logger.error(err);
153
+ return Promise.reject(err);
154
+ }
155
+ };
156
+ return await dispatch(0);
157
+ };
158
+ }
159
+ /**
160
+ * First time mount the function
161
+ */
162
+ async mount(data = {
163
+ event: Object.create(null),
164
+ context: Object.create(null)
165
+ }) {
166
+ if (!data.logger) data.logger = new _faasjs_logger.Logger("Func");
167
+ if (this.mounted) {
168
+ data.logger.warn("mount() has been called, skipped.");
169
+ return;
170
+ }
171
+ if (!data.config) data.config = this.config;
172
+ data.logger.debug(`plugins: ${this.plugins.map((p) => `${p.type}#${p.name}`).join(",")}`);
173
+ await this.compose("onMount")(data);
174
+ this.mounted = true;
175
+ }
176
+ /**
177
+ * Invoke the function
178
+ * @param data {object} data
179
+ */
180
+ async invoke(data) {
181
+ if (!this.mounted) await this.mount(data);
182
+ try {
183
+ await this.compose("onInvoke")(data);
184
+ } catch (error) {
185
+ data.logger.error(error);
186
+ data.response = error;
187
+ }
188
+ }
189
+ /**
190
+ * Export the function
191
+ */
192
+ export() {
193
+ const handler = async (event, context, callback) => {
194
+ if (typeof context === "undefined") context = {};
195
+ if (!context.request_id) context.request_id = event?.headers?.["x-faasjs-request-id"] || (0, node_crypto.randomBytes)(16).toString("hex");
196
+ if (!context.request_at) context.request_at = (0, node_crypto.randomBytes)(16).toString("hex");
197
+ context.callbackWaitsForEmptyEventLoop = false;
198
+ const logger = new _faasjs_logger.Logger(context.request_id);
199
+ const data = {
200
+ event: event ?? Object.create(null),
201
+ context,
202
+ callback,
203
+ response: void 0,
204
+ logger,
205
+ config: this.config,
206
+ ...this.handler ? { handler: this.handler } : {}
207
+ };
208
+ await this.invoke(data);
209
+ if (Object.prototype.toString.call(data.response) === "[object Error]") throw data.response;
210
+ return data.response;
211
+ };
212
+ return { handler: handler.bind(this) };
213
+ }
285
214
  };
286
- var plugins = [];
215
+ let plugins = [];
287
216
  function usePlugin(plugin) {
288
- if (!plugins.find((p) => p.name === plugin.name)) plugins.push(plugin);
289
- if (!plugin.mount)
290
- plugin.mount = async (data) => {
291
- const parsedData = data || /* @__PURE__ */ Object.create(null);
292
- if (!parsedData.config) parsedData.config = /* @__PURE__ */ Object.create(null);
293
- if (!parsedData.context) parsedData.context = /* @__PURE__ */ Object.create(null);
294
- if (!parsedData.event) parsedData.event = /* @__PURE__ */ Object.create(null);
295
- if (!parsedData.logger) parsedData.logger = new logger.Logger(plugin.name);
296
- if (plugin.onMount)
297
- await plugin.onMount(parsedData, async () => Promise.resolve());
298
- return plugin;
299
- };
300
- return plugin;
217
+ if (!plugins.find((p) => p.name === plugin.name)) plugins.push(plugin);
218
+ if (!plugin.mount) plugin.mount = async (data) => {
219
+ const parsedData = data || Object.create(null);
220
+ if (!parsedData.config) parsedData.config = Object.create(null);
221
+ if (!parsedData.context) parsedData.context = Object.create(null);
222
+ if (!parsedData.event) parsedData.event = Object.create(null);
223
+ if (!parsedData.logger) parsedData.logger = new _faasjs_logger.Logger(plugin.name);
224
+ if (plugin.onMount) await plugin.onMount(parsedData, async () => Promise.resolve());
225
+ return plugin;
226
+ };
227
+ return plugin;
301
228
  }
229
+ /**
230
+ * Create a cloud function.
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * // pure function
235
+ * export const func = useFunc(() => {
236
+ * return () => {
237
+ * return 'Hello World'
238
+ * }
239
+ * })
240
+ *
241
+ * // with http
242
+ * import { useHttp } from '@faasjs/http'
243
+ *
244
+ * export const func = useFunc<{
245
+ * params: { name: string }
246
+ * }>(() => {
247
+ * useHttp()
248
+ *
249
+ * return ({ event }) => {
250
+ * return `Hello ${event.params.name}`
251
+ * }
252
+ * })
253
+ * ```
254
+ */
302
255
  function useFunc(handler) {
303
- plugins = [];
304
- const invokeHandler = handler();
305
- const func = new Func({
306
- plugins,
307
- handler: invokeHandler
308
- });
309
- plugins = [];
310
- return func;
311
- }
312
- function defineFunc(handler) {
313
- plugins = [];
314
- const func = new Func({
315
- plugins,
316
- handler,
317
- autoLoadPluginsFromConfig: true
318
- });
319
- plugins = [];
320
- return func;
256
+ plugins = [];
257
+ const invokeHandler = handler();
258
+ const func = new Func({
259
+ plugins,
260
+ handler: invokeHandler
261
+ });
262
+ plugins = [];
263
+ return func;
321
264
  }
322
265
 
266
+ //#endregion
323
267
  exports.Func = Func;
324
- exports.defineFunc = defineFunc;
325
268
  exports.nameFunc = nameFunc;
326
269
  exports.parseFuncFilenameFromStack = parseFuncFilenameFromStack;
327
270
  exports.useFunc = useFunc;
328
- exports.usePlugin = usePlugin;
271
+ exports.usePlugin = usePlugin;