godprotocol 2.3.46 → 2.3.50

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "godprotocol",
3
- "version": "2.3.46",
3
+ "version": "2.3.50",
4
4
  "description": "A distributed computing environment for Web 4.0 — integrating AI, decentralisation, and virtual computation.",
5
5
  "main": "Index.js",
6
6
  "type": "module",
@@ -8,6 +8,28 @@ let securities = {
8
8
  none: [],
9
9
  };
10
10
 
11
+ // Sections whose raw values arrive as strings (URLSearchParams / regex
12
+ const STRINGY_SECTIONS = ["query", "params"];
13
+
14
+ // Supported arithmetic operators (value-level)
15
+ const ARITH_OPS = {
16
+ min: (a, b) => a >= b,
17
+ max: (a, b) => a <= b,
18
+ gt: (a, b) => a > b,
19
+ gte: (a, b) => a >= b,
20
+ lt: (a, b) => a < b,
21
+ lte: (a, b) => a <= b,
22
+ eq: (a, b) => a === b,
23
+ ne: (a, b) => a !== b,
24
+ };
25
+
26
+ // Length variants (work on string / array / object)
27
+ const LENGTH_OPS = {
28
+ minLength: (a, b) => (a?.length ?? Object.keys(a || {}).length) >= b,
29
+ maxLength: (a, b) => (a?.length ?? Object.keys(a || {}).length) <= b,
30
+ length: (a, b) => (a?.length ?? Object.keys(a || {}).length) === b,
31
+ };
32
+
11
33
  class Route_table extends Headers {
12
34
  constructor(gp) {
13
35
  super();
@@ -36,7 +58,6 @@ class Route_table extends Headers {
36
58
  for (const callback of this.before_callbacks) {
37
59
  const result = await callback(payload);
38
60
 
39
- // allow middleware-style rejection
40
61
  if (result && result.ok === false) {
41
62
  return result;
42
63
  }
@@ -81,7 +102,13 @@ class Route_table extends Headers {
81
102
  };
82
103
 
83
104
  init_version = async (version, config) => {
84
- this.versions[version] = { config, routes: {} };
105
+ this.versions[version] = {
106
+ config,
107
+ routes: {},
108
+ param_routes: [],
109
+ regex_routes: [],
110
+ wildcard: null,
111
+ };
85
112
 
86
113
  this.active_version = version;
87
114
  };
@@ -117,109 +144,378 @@ class Route_table extends Headers {
117
144
  }
118
145
  };
119
146
 
147
+ // =========================
148
+ // 🧭 ROUTE PATTERN COMPILATION
149
+ // =========================
150
+ compile_route_pattern = (pattern) => {
151
+ const keys = [];
152
+
153
+ const segments = pattern.split("/");
154
+
155
+ const regex_parts = segments.map((segment) => {
156
+ if (segment.startsWith(":")) {
157
+ keys.push(segment.slice(1));
158
+ return "([^/]+)";
159
+ }
160
+
161
+ if (segment === "*") {
162
+ keys.push("*");
163
+ return "(.*)";
164
+ }
165
+
166
+ return segment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
167
+ });
168
+
169
+ return {
170
+ regex: new RegExp(`^${regex_parts.join("/")}$`),
171
+ keys,
172
+ };
173
+ };
174
+
120
175
  add_route = async (name, handler, config) => {
121
- this.versions[this.active_version].routes[name] = { handler, config };
176
+ const version = this.versions[this.active_version];
177
+
178
+ version.routes[name] = { handler, config };
179
+
180
+ if (name === "*") {
181
+ version.wildcard = { handler, config, route_pattern: name };
182
+ return;
183
+ }
184
+
185
+ if (name.startsWith("^")) {
186
+ version.regex_routes.push({
187
+ pattern: name,
188
+ regex: new RegExp(name),
189
+ handler,
190
+ config,
191
+ });
192
+ return;
193
+ }
194
+
195
+ if (name.includes(":") || name.includes("*")) {
196
+ const { regex, keys } = this.compile_route_pattern(name);
197
+
198
+ version.param_routes.push({
199
+ pattern: name,
200
+ regex,
201
+ keys,
202
+ handler,
203
+ config,
204
+ });
205
+ }
122
206
  };
123
207
 
208
+ // =========================
209
+ // 🔎 ROUTE RESOLUTION
210
+ // Precedence: exact -> named param -> regex -> wildcard
211
+ // =========================
124
212
  get_route = async (name) => {
125
213
  const version = this.versions[this.active_version];
126
214
 
215
+ if (!version) return null;
216
+
127
217
  if (version.routes[name]) {
128
- return version.routes[name];
218
+ return {
219
+ ...version.routes[name],
220
+ params: {},
221
+ route_pattern: name,
222
+ };
129
223
  }
130
224
 
131
- for (const [pattern, route] of Object.entries(version.routes)) {
132
- if (pattern.startsWith("^")) {
133
- if (new RegExp(pattern).test(name)) {
134
- return route;
135
- }
225
+ for (const route of version.param_routes) {
226
+ const match = route.regex.exec(name);
227
+
228
+ if (match) {
229
+ const params = {};
230
+
231
+ route.keys.forEach((key, i) => {
232
+ params[key] = decodeURIComponent(match[i + 1]);
233
+ });
234
+
235
+ return {
236
+ handler: route.handler,
237
+ config: route.config,
238
+ params,
239
+ route_pattern: route.pattern,
240
+ };
136
241
  }
137
242
  }
138
243
 
244
+ for (const route of version.regex_routes) {
245
+ if (route.regex.test(name)) {
246
+ return {
247
+ handler: route.handler,
248
+ config: route.config,
249
+ params: {},
250
+ route_pattern: route.pattern,
251
+ };
252
+ }
253
+ }
254
+
255
+ if (version.wildcard) {
256
+ return { ...version.wildcard, params: {} };
257
+ }
258
+
139
259
  return null;
140
260
  };
141
261
 
142
262
  getValue = (obj, path) => path.split(".").reduce((o, key) => o?.[key], obj);
143
263
 
144
- check_validation = async (rule, data) => {
145
- let { prop, type, required, enum: allowed } = rule;
264
+ // =========================
265
+ // 🔁 TYPE COERCION (query / params only)
266
+ // =========================
267
+ coerce_value = (value, expected) => {
268
+ if (value === null || value === undefined) return { ok: false };
269
+
270
+ switch (expected) {
271
+ case "number": {
272
+ if (typeof value === "number") return { ok: true, value };
273
+ if (typeof value !== "string" || value.trim() === "")
274
+ return { ok: false };
275
+ const n = Number(value);
276
+ if (Number.isNaN(n)) return { ok: false };
277
+ return { ok: true, value: n };
278
+ }
279
+
280
+ case "boolean": {
281
+ if (typeof value === "boolean") return { ok: true, value };
282
+ if (value === "true") return { ok: true, value: true };
283
+ if (value === "false") return { ok: true, value: false };
284
+ return { ok: false };
285
+ }
146
286
 
287
+ case "array": {
288
+ if (Array.isArray(value)) return { ok: true, value };
289
+ try {
290
+ const parsed = JSON.parse(value);
291
+ if (Array.isArray(parsed)) return { ok: true, value: parsed };
292
+ } catch (_) {}
293
+ if (typeof value === "string" && value.length) {
294
+ return { ok: true, value: value.split(",").map((v) => v.trim()) };
295
+ }
296
+ return { ok: false };
297
+ }
298
+
299
+ case "object": {
300
+ if (value && typeof value === "object" && !Array.isArray(value)) {
301
+ return { ok: true, value };
302
+ }
303
+ try {
304
+ const parsed = JSON.parse(value);
305
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
306
+ return { ok: true, value: parsed };
307
+ }
308
+ } catch (_) {}
309
+ return { ok: false };
310
+ }
311
+
312
+ case "string":
313
+ return { ok: true, value: String(value) };
314
+
315
+ default:
316
+ return { ok: false };
317
+ }
318
+ };
319
+
320
+ /**
321
+ * Deep validation of a single field (or nested structure).
322
+ * Supports:
323
+ * - type
324
+ * - required / default_value
325
+ * - enum
326
+ * - pattern (RegExp or string)
327
+ * - arithmetic operators (min, max, gt, gte, lt, lte, eq, ne)
328
+ * - length operators (minLength, maxLength, length)
329
+ * - nested schema (for object)
330
+ * - items schema (for array)
331
+ * - custom validator name (via this.validators)
332
+ */
333
+ check_validation = async (rule, data, path = "") => {
334
+ let {
335
+ prop,
336
+ section,
337
+ type,
338
+ required = false,
339
+ enum: allowed,
340
+ pattern,
341
+ schema, // nested object schema
342
+ items, // array item schema
343
+ validator, // custom named validator
344
+ default_value,
345
+ ...rest // arithmetic + length ops land here
346
+ } = rule;
347
+
348
+ const fullPath = path ? `${path}.${prop}` : prop;
147
349
  let data_value = this.getValue(data, prop);
148
350
 
149
- if (data_value === undefined && rule.hasOwnProperty("default_value")) {
150
- data_value = rule.default_value;
151
- data[prop] = data_value;
351
+ // ---------- default ----------
352
+ if (
353
+ data_value === undefined &&
354
+ Object.prototype.hasOwnProperty.call(rule, "default_value")
355
+ ) {
356
+ data_value = default_value;
357
+ // write back so later stages see the default
358
+ const parts = prop.split(".");
359
+ let cur = data;
360
+ for (let i = 0; i < parts.length - 1; i++) {
361
+ if (cur[parts[i]] == null) cur[parts[i]] = {};
362
+ cur = cur[parts[i]];
363
+ }
364
+ cur[parts[parts.length - 1]] = data_value;
152
365
  }
153
366
 
367
+ // ---------- required ----------
154
368
  if (data_value === undefined || data_value === null) {
155
369
  if (required) {
156
370
  return {
157
371
  ok: false,
158
- message: `Field '${prop}' is required`,
372
+ message: `Field '${fullPath}' is required in '${section}'`,
373
+ path: fullPath,
159
374
  };
160
375
  }
161
376
  return { ok: true };
162
377
  }
163
378
 
164
- // type validation
165
- // =========================
166
- // Type validation
167
- // =========================
379
+ // ---------- resolve type descriptor ----------
380
+ let expected = type;
381
+ if (typeof type === "object" && type !== null && !Array.isArray(type)) {
382
+ expected = type.value ?? type;
383
+ }
168
384
 
169
- if (type) {
170
- let descriptor = type;
385
+ // ---------- coercion (query / params only) ----------
386
+ if (
387
+ STRINGY_SECTIONS.includes(section) &&
388
+ expected &&
389
+ expected !== "string" &&
390
+ typeof data_value === "string"
391
+ ) {
392
+ const coerced = this.coerce_value(data_value, expected);
393
+ if (!coerced.ok) {
394
+ return {
395
+ ok: false,
396
+ message: `Field '${fullPath}' in '${section}' could not be parsed as type '${expected}'`,
397
+ path: fullPath,
398
+ };
399
+ }
400
+ data_value = coerced.value;
401
+ // write coerced value back
402
+ const parts = prop.split(".");
403
+ let cur = data;
404
+ for (let i = 0; i < parts.length - 1; i++) {
405
+ cur = cur[parts[i]];
406
+ }
407
+ cur[parts[parts.length - 1]] = data_value;
408
+ }
171
409
 
172
- // Logical schemas may provide a map:
173
- // type: {
174
- // intervals: { value: "array", min: 1 },
175
- // throttle: { value: "number", min: 1000 }
176
- // }
177
- if (typeof type === "object" && !Array.isArray(type)) {
178
- descriptor = type[prop];
410
+ // ---------- base type check ----------
411
+ if (expected) {
412
+ const actual = Array.isArray(data_value) ? "array" : typeof data_value;
413
+ if (actual !== expected) {
414
+ return {
415
+ ok: false,
416
+ message: `Field '${fullPath}' in '${section}' must be of type '${expected}', got '${actual}'`,
417
+ path: fullPath,
418
+ };
419
+ }
420
+ }
179
421
 
180
- // No type rule for this property
181
- if (!descriptor) {
182
- descriptor = null;
183
- }
422
+ // ---------- enum ----------
423
+ if (allowed && !allowed.includes(data_value)) {
424
+ return {
425
+ ok: false,
426
+ message: `Field '${fullPath}' in '${section}' must be one of: ${allowed.join(", ")}`,
427
+ path: fullPath,
428
+ };
429
+ }
430
+
431
+ // ---------- regex / pattern ----------
432
+ if (pattern) {
433
+ const re = pattern instanceof RegExp ? pattern : new RegExp(pattern);
434
+ if (typeof data_value !== "string" || !re.test(data_value)) {
435
+ return {
436
+ ok: false,
437
+ message: `Field '${fullPath}' in '${section}' does not match required pattern`,
438
+ path: fullPath,
439
+ };
184
440
  }
441
+ }
185
442
 
186
- if (descriptor) {
187
- // Allow shorthand:
188
- // "string"
189
- // or
190
- // { value: "string", ... }
191
- if (typeof descriptor === "string") {
192
- descriptor = {
193
- value: descriptor,
443
+ // ---------- arithmetic operators ----------
444
+ for (const [op, fn] of Object.entries(ARITH_OPS)) {
445
+ if (rest[op] !== undefined) {
446
+ if (typeof data_value !== "number") {
447
+ return {
448
+ ok: false,
449
+ message: `Field '${fullPath}' must be a number to apply '${op}'`,
450
+ path: fullPath,
194
451
  };
195
452
  }
453
+ if (!fn(data_value, rest[op])) {
454
+ return {
455
+ ok: false,
456
+ message: `Field '${fullPath}' failed '${op}' constraint (expected ${op} ${rest[op]}, got ${data_value})`,
457
+ path: fullPath,
458
+ };
459
+ }
460
+ }
461
+ }
196
462
 
197
- const expected = descriptor.value;
198
-
199
- let actual;
200
-
201
- let actual = Array.isArray(data_value) ? "array" : typeof data_value;
202
-
203
- if (actual !== expected) {
463
+ // ---------- length operators ----------
464
+ for (const [op, fn] of Object.entries(LENGTH_OPS)) {
465
+ if (rest[op] !== undefined) {
466
+ if (!fn(data_value, rest[op])) {
204
467
  return {
205
468
  ok: false,
206
- message: `Field '${prop}' must be of type '${expected}', got '${actual}'`,
469
+ message: `Field '${fullPath}' failed '${op}' constraint (expected ${op} ${rest[op]})`,
470
+ path: fullPath,
207
471
  };
208
472
  }
209
473
  }
210
474
  }
211
475
 
212
- // enum validation
213
- if (
214
- data_value !== undefined &&
215
- data_value !== null &&
216
- allowed &&
217
- !allowed.includes(data_value)
218
- ) {
219
- return {
220
- ok: false,
221
- message: `Field '${prop}' must be one of: ${allowed.join(", ")}`,
222
- };
476
+ // ---------- nested object schema ----------
477
+ if (expected === "object" && schema) {
478
+ for (const [nestedProp, nestedRule] of Object.entries(schema)) {
479
+ const result = await this.check_validation(
480
+ { prop: nestedProp, section, ...nestedRule },
481
+ data_value,
482
+ fullPath,
483
+ );
484
+ if (!result.ok) return result;
485
+ }
486
+ }
487
+
488
+ // ---------- array items schema ----------
489
+ if (expected === "array" && items) {
490
+ if (!Array.isArray(data_value)) {
491
+ return {
492
+ ok: false,
493
+ message: `Field '${fullPath}' must be an array`,
494
+ path: fullPath,
495
+ };
496
+ }
497
+ for (let i = 0; i < data_value.length; i++) {
498
+ const result = await this.check_validation(
499
+ { prop: String(i), section, ...items },
500
+ { [String(i)]: data_value[i] },
501
+ `${fullPath}[${i}]`,
502
+ );
503
+ if (!result.ok) return result;
504
+ }
505
+ }
506
+
507
+ // ---------- custom validator ----------
508
+ if (validator && this.validators[validator]) {
509
+ const result = await this.validators[validator](data_value, rule, data);
510
+ if (result && result.ok === false) {
511
+ return {
512
+ ok: false,
513
+ message:
514
+ result.message ||
515
+ `Custom validator '${validator}' failed for '${fullPath}'`,
516
+ path: fullPath,
517
+ };
518
+ }
223
519
  }
224
520
 
225
521
  return { ok: true };
@@ -233,47 +529,45 @@ class Route_table extends Headers {
233
529
  for (const prop in rules) {
234
530
  const rule = rules[prop];
235
531
 
532
+ // ---------- $logic (or / and) ----------
236
533
  if (prop === "$logic") {
237
- // OR logic
534
+ // OR groups: at least one of the listed properties must satisfy the constraints
238
535
  if (rule.or) {
239
536
  for (const orRule of rule.or) {
240
537
  let passed = false;
538
+ const failedProps = [];
241
539
 
242
540
  for (const property of orRule.properties) {
243
541
  const result = await this.check_validation(
244
- { prop: property, ...orRule },
542
+ { prop: property, section, ...orRule },
245
543
  target,
246
544
  );
247
-
248
545
  if (result.ok) {
249
546
  passed = true;
250
547
  break;
251
548
  }
549
+ failedProps.push(property);
252
550
  }
253
551
 
254
552
  if (orRule.required && !passed) {
255
553
  return {
256
554
  ok: false,
257
- message: `At least one of the following fields is required: ${orRule.properties.join(
258
- ", ",
259
- )}`,
555
+ message: `At least one of the following fields is required in '${section}': ${orRule.properties.join(", ")}`,
556
+ path: `$logic.or`,
260
557
  };
261
558
  }
262
559
  }
263
560
  }
264
561
 
265
- // AND logic
562
+ // AND groups: every listed property must satisfy the constraints
266
563
  if (rule.and) {
267
564
  for (const andRule of rule.and) {
268
565
  for (const property of andRule.properties) {
269
566
  const result = await this.check_validation(
270
- { prop: property, ...andRule },
567
+ { prop: property, section, ...andRule },
271
568
  target,
272
569
  );
273
-
274
- if (!result.ok) {
275
- return result;
276
- }
570
+ if (!result.ok) return result;
277
571
  }
278
572
  }
279
573
  }
@@ -281,11 +575,12 @@ class Route_table extends Headers {
281
575
  continue;
282
576
  }
283
577
 
284
- const result = await this.check_validation({ prop, ...rule }, target);
285
-
286
- if (!result.ok) {
287
- return result;
288
- }
578
+ // ---------- normal field ----------
579
+ const result = await this.check_validation(
580
+ { prop, section, ...rule },
581
+ target,
582
+ );
583
+ if (!result.ok) return result;
289
584
  }
290
585
  }
291
586
 
@@ -295,7 +590,7 @@ class Route_table extends Headers {
295
590
  get_route_config = async (name) => {
296
591
  let route = await this.get_route(name);
297
592
 
298
- return route.config;
593
+ return route?.config;
299
594
  };
300
595
 
301
596
  // =========================
@@ -311,20 +606,23 @@ class Route_table extends Headers {
311
606
  let route = await this.get_route(name);
312
607
 
313
608
  if (!route) {
314
- route = await this.get_route("*");
315
- if (!route) {
316
- return {
317
- ok: false,
318
- status: 404,
319
- message: "Route not found",
320
- data: { path: `/${name}`, name },
321
- };
322
- }
609
+ return {
610
+ ok: false,
611
+ status: 404,
612
+ message: "Route not found",
613
+ data: { path: `/${name}`, name },
614
+ };
323
615
  }
324
616
 
325
- let { handler, config } = route;
617
+ const { handler, config, params = {}, route_pattern } = route;
618
+
619
+ payload.params = params;
620
+ payload.route_pattern = route_pattern;
621
+
622
+ if (payload.req) {
623
+ payload.req.params = params;
624
+ }
326
625
 
327
- // 📜 schema
328
626
  if (config.schema) {
329
627
  let resp = await this.validate(config.schema, payload);
330
628
 
@@ -334,14 +632,13 @@ class Route_table extends Headers {
334
632
  endpoint: name,
335
633
  args: payload.body,
336
634
  response: resp,
337
- conversation: payload.headers.meta.conversation_id,
635
+ conversation: payload.headers?.meta?.conversation_id,
338
636
  });
339
637
  }
340
638
  return resp;
341
639
  }
342
640
  }
343
641
 
344
- // ⚙️ middleware
345
642
  for (let middleware of config.middlewares || []) {
346
643
  let result = await middleware(payload);
347
644
  if (!result?.ok) {
@@ -350,22 +647,19 @@ class Route_table extends Headers {
350
647
  endpoint: name,
351
648
  args: payload.body,
352
649
  response: result,
353
- // conversation: payload.headers.meta.conversation_id,
354
650
  });
355
651
  }
356
652
  return result;
357
653
  }
358
654
  }
359
655
 
360
- // 🎯 handler
361
656
  let response = await handler(payload);
362
- if (response.agent) {
657
+ if (response?.agent) {
363
658
  await this.call_agency(
364
659
  {
365
660
  endpoint: name,
366
661
  args: payload.body,
367
662
  response,
368
- // conversation: payload.headers.meta.conversation_id,
369
663
  },
370
664
  response.agent,
371
665
  );
@@ -64,10 +64,14 @@ const respond = (result, res) => {
64
64
 
65
65
  const response = {
66
66
  ok: result?.ok ?? false,
67
- message: result?.message ?? "",
68
- data: result?.data,
69
67
  };
70
68
 
69
+ if (result?.message) {
70
+ response.message = result.message;
71
+ }
72
+
73
+ response.data = result?.data;
74
+
71
75
  if (result?.pagination) {
72
76
  response.pagination = result.pagination;
73
77
  }
@@ -111,6 +115,10 @@ const version_middleware = async (req, res, routers) => {
111
115
  const parsed_url = new URL(req.url, `http://${req.headers.host}`);
112
116
  const pathname = parsed_url.pathname;
113
117
 
118
+ // ⬇️ query params captured for every method now
119
+ const query = Object.fromEntries(parsed_url.searchParams.entries());
120
+ req.query = query;
121
+
114
122
  req.request_id = crypto.randomUUID();
115
123
  routers.gp?.resource?.incoming_request(req);
116
124
  res.gp = routers.gp;
@@ -119,7 +127,7 @@ const version_middleware = async (req, res, routers) => {
119
127
  // STATIC ROUTE DETECTION
120
128
  let version;
121
129
 
122
- const versionMatch = pathname.match(/^\/api\/(v\d+)(\/|$)/i);
130
+ const versionMatch = pathname.match(/^\/api\/([^\/]+)(\/|$)/i);
123
131
  if (req.method === "GET") {
124
132
  if (routers.static_path && pathname.startsWith(routers.static_path)) {
125
133
  res.gp = routers.gp;
@@ -159,9 +167,8 @@ const version_middleware = async (req, res, routers) => {
159
167
  let body = {};
160
168
 
161
169
  if (req.method === "GET") {
162
- const url = new URL(req.url, `http://${req.headers.host}`);
163
-
164
- body = Object.fromEntries(url.searchParams.entries());
170
+ // GET keeps existing behavior: query params double as body
171
+ body = query;
165
172
  } else {
166
173
  const contentType = req.headers["content-type"] || "";
167
174
 
@@ -181,12 +188,10 @@ const version_middleware = async (req, res, routers) => {
181
188
  const router = routers;
182
189
 
183
190
  // remove query string from route
184
- const parsedUrl = new URL(req.url, `http://${req.headers.host}`);
185
-
186
- let routePath = parsedUrl.pathname;
191
+ let routePath = pathname;
187
192
 
188
- // Remove /api/v1, /api/v2, /api/v3, etc.
189
- routePath = routePath.replace(/^\/api\/v\d+(?=\/|$)/i, "");
193
+ // Remove /api/<version>
194
+ routePath = routePath.replace(/^\/api\/[^\/]+(?=\/|$)/i, "");
190
195
 
191
196
  // Ensure leading slash exists
192
197
  if (!routePath.startsWith("/")) {
@@ -195,7 +200,7 @@ const version_middleware = async (req, res, routers) => {
195
200
 
196
201
  req.url = routePath;
197
202
 
198
- const name = routePath.replace(/^\//, "");
203
+ let name = routePath.replace(/^\//, "");
199
204
 
200
205
  const isWebhook = req.headers["is-webhook"];
201
206
 
@@ -215,6 +220,7 @@ const version_middleware = async (req, res, routers) => {
215
220
  {
216
221
  req,
217
222
  body,
223
+ query,
218
224
  route: name,
219
225
  db: db.db,
220
226
  },
@@ -225,6 +231,8 @@ const version_middleware = async (req, res, routers) => {
225
231
 
226
232
  if (callback?.body) body = callback.body;
227
233
 
234
+ if (callback?.query) query = callback.query; // only if you make `query` a `let` — see note below
235
+
228
236
  if (callback?.headers) req.headers = callback.headers;
229
237
 
230
238
  if (callback?.db) db.db = callback.db;
@@ -234,12 +242,15 @@ const version_middleware = async (req, res, routers) => {
234
242
  error_stage = "Handler";
235
243
  let payload = {
236
244
  body,
245
+ query,
237
246
  route: name,
238
247
  headers: req.headers,
248
+ req,
239
249
  db: db.db,
240
250
  gp: router.gp,
241
251
  services: router.get_service,
242
252
  method: req.method,
253
+ params: {},
243
254
  memory: router.gp.memory,
244
255
  };
245
256
  const result = await router.execute(name, payload);