allserver 2.2.1 → 2.3.0

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
@@ -361,8 +361,6 @@ app.use("/route-with-allsever", middleware);
361
361
 
362
362
  Doesn't require a dedicated client transport. Use the HTTP client below.
363
363
 
364
- NB: not yet tested in production.
365
-
366
364
  ```js
367
365
  const { Allserver, LambdaTransport } = require("allserver");
368
366
 
@@ -913,7 +911,7 @@ const client = AllserverClient({
913
911
  ctx.http.headers.authorization = "Basic my-token";
914
912
  },
915
913
  async after(ctx) {
916
- if (ctx.error) console.error(ctx.error) else console.log(ctx.result);
914
+ if (ctx.error) console.error(ctx.error); else console.log(ctx.result);
917
915
  },
918
916
  });
919
917
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "allserver",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "Multi-protocol simple RPC server and [optional] client. Boilerplate-less. Opinionated. Minimalistic. DX-first.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -65,6 +65,7 @@
65
65
  "@grpc/grpc-js": "^1.1.7",
66
66
  "@grpc/proto-loader": "^0.7.5",
67
67
  "bullmq": "^3.10.1",
68
+ "cls-hooked": "^4.2.2",
68
69
  "eslint": "^8.55.0",
69
70
  "express": "^4.18.2",
70
71
  "lambda-local": "^1.7.3",
@@ -263,6 +263,10 @@ module.exports = require("stampit")({
263
263
  },
264
264
 
265
265
  async call(procedureName, arg) {
266
+ if (!arg) arg = {};
267
+ if (!arg._) arg._ = {};
268
+ arg._.procedureName = procedureName;
269
+
266
270
  const transport = this[p].transport;
267
271
  const defaultCtx = { procedureName, arg, client: this };
268
272
  const ctx = transport.createCallContext(defaultCtx);
@@ -28,12 +28,15 @@ module.exports = require("./ClientTransport").compose({
28
28
  return result;
29
29
  },
30
30
 
31
- async call({ procedureName, lambda }) {
31
+ async call(ctx) {
32
32
  let promise = this.awsSdkLambdaClient.invoke({
33
33
  FunctionName: this.uri.substring("lambda://".length),
34
+ // TODO: change to this during the next major release:
35
+ // Payload: JSON.stringify(ctx.arg),
34
36
  Payload: JSON.stringify({
35
- callContext: { ...lambda.callContext, procedureName },
36
- callArg: lambda.callArg,
37
+ callContext: { ...ctx.lambda.callContext, procedureName: ctx.procedureName },
38
+ callArg: ctx.lambda.callArg,
39
+ ...ctx.arg,
37
40
  }),
38
41
  });
39
42
  if (typeof promise.promise === "function") promise = promise.promise(); // AWS SDK v2 adoption
@@ -44,6 +47,8 @@ module.exports = require("./ClientTransport").compose({
44
47
  createCallContext(defaultCtx) {
45
48
  return {
46
49
  ...defaultCtx,
50
+ // TODO: change to this during the next major release:
51
+ // lambda: {}
47
52
  lambda: {
48
53
  callContext: {},
49
54
  callArg: defaultCtx.arg,
@@ -50,6 +50,13 @@ module.exports = require("stampit")({
50
50
  await this.transport.prepareIntrospectionReply(ctx);
51
51
  },
52
52
 
53
+ /**
54
+ * This method does not throw.
55
+ * The `ctx.procedure` is the function to call.
56
+ * @param ctx
57
+ * @return {Promise<void>}
58
+ * @private
59
+ */
53
60
  async _callProcedure(ctx) {
54
61
  if (!isFunction(ctx.procedure)) {
55
62
  ctx.result = {
@@ -91,16 +98,29 @@ module.exports = require("stampit")({
91
98
  }
92
99
  },
93
100
 
94
- async _callMiddlewares(ctx, middlewareType) {
95
- if (!this[middlewareType]) return;
96
-
97
- const middlewares = [].concat(this[middlewareType]).filter(isFunction);
98
- for (const middleware of middlewares) {
99
- try {
100
- const result = await middleware.call(this, ctx);
101
+ async _callMiddlewares(ctx, middlewareType, next) {
102
+ const runMiddlewares = async (middlewares) => {
103
+ if (!middlewares?.length) {
104
+ // no middlewares to run
105
+ if (next) return await next();
106
+ return;
107
+ }
108
+ const middleware = middlewares[0];
109
+ async function handleMiddlewareResult(result) {
101
110
  if (result !== undefined) {
102
111
  ctx.result = result;
103
- break;
112
+ // Do not call any more middlewares
113
+ } else {
114
+ await runMiddlewares(middlewares.slice(1));
115
+ }
116
+ }
117
+ try {
118
+ if (middleware.length > 1) {
119
+ // This middleware accepts more than one argument
120
+ await middleware.call(this, ctx, handleMiddlewareResult);
121
+ } else {
122
+ const result = await middleware.call(this, ctx);
123
+ await handleMiddlewareResult(result);
104
124
  }
105
125
  } catch (err) {
106
126
  const code = err.code || "ALLSERVER_MIDDLEWARE_ERROR";
@@ -111,9 +131,14 @@ module.exports = require("stampit")({
111
131
  code,
112
132
  message: `'${err.message}' error in '${middlewareType}' middleware`,
113
133
  };
114
- return;
134
+ // Do not call any more middlewares
135
+ if (next) return await next();
115
136
  }
116
- }
137
+ };
138
+
139
+ const middlewares = [].concat(this[middlewareType]).filter(isFunction);
140
+
141
+ return await runMiddlewares(middlewares);
117
142
  },
118
143
 
119
144
  async handleCall(ctx) {
@@ -123,18 +148,22 @@ module.exports = require("stampit")({
123
148
  ctx.isIntrospection = this.transport.isIntrospection(ctx);
124
149
  if (!ctx.isIntrospection && ctx.procedureName) ctx.procedure = this.procedures[ctx.procedureName];
125
150
 
126
- await this._callMiddlewares(ctx, "before");
151
+ if (!ctx.arg) ctx.arg = {};
152
+ if (!ctx.arg._) ctx.arg._ = {};
153
+ if (!ctx.arg._.procedureName) ctx.arg._.procedureName = ctx.procedureName;
127
154
 
128
- if (!ctx.result) {
129
- if (ctx.isIntrospection) {
130
- await this._introspect(ctx);
131
- } else {
132
- await this._callProcedure(ctx);
155
+ await this._callMiddlewares(ctx, "before", async () => {
156
+ if (!ctx.result) {
157
+ if (ctx.isIntrospection) {
158
+ await this._introspect(ctx);
159
+ } else {
160
+ await this._callProcedure(ctx);
161
+ }
133
162
  }
134
- }
135
163
 
136
- // Warning! This call might overwrite an existing result.
137
- await this._callMiddlewares(ctx, "after");
164
+ // Warning! This call might overwrite an existing result.
165
+ await this._callMiddlewares(ctx, "after");
166
+ });
138
167
 
139
168
  return this.transport.reply(ctx);
140
169
  },
@@ -21,7 +21,9 @@ module.exports = require("./Transport").compose({
21
21
  return false;
22
22
  }
23
23
  } else {
24
- ctx.arg = ctx.lambda.invoke.callArg || {};
24
+ // TODO: change to this during the next major release:
25
+ // ctx.arg = ctx.lambda.invoke || {};
26
+ ctx.arg = ctx.lambda.invoke.callArg || ctx.lambda.invoke || {};
25
27
  return true;
26
28
  }
27
29
  },
@@ -54,7 +56,11 @@ module.exports = require("./Transport").compose({
54
56
  headers: event?.headers,
55
57
  };
56
58
  } else {
57
- lambda.invoke = { callContext: event?.callContext, callArg: event?.callArg };
59
+ // TODO: change to this during the next major release:
60
+ // lambda.invoke = event || {};
61
+ lambda.invoke = event?.callArg
62
+ ? { callContext: event?.callContext, callArg: event?.callArg }
63
+ : event || {};
58
64
  }
59
65
 
60
66
  this._handleRequest({ ...defaultCtx, lambda });
@@ -64,7 +70,9 @@ module.exports = require("./Transport").compose({
64
70
 
65
71
  getProcedureName(ctx) {
66
72
  const { isHttp, http, invoke } = ctx.lambda;
67
- return isHttp ? http.path.substr(1) : invoke.callContext?.procedureName;
73
+ // TODO: change to this during the next major release:
74
+ // return (isHttp ? http.path.substr(1) : invoke._?.procedureName) || ""; // if no procedureName then it's an introspection
75
+ return isHttp ? http.path.substr(1) : invoke.callContext?.procedureName || invoke._?.procedureName || ""; // if no procedureName then it's an introspection
68
76
  },
69
77
 
70
78
  isIntrospection(ctx) {