godprotocol 2.3.47 → 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.47",
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,107 +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
+ };
241
+ }
242
+ }
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
+ };
136
252
  }
137
253
  }
138
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
+ }
384
+
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
+ }
168
409
 
169
- if (type) {
170
- let descriptor = type;
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
+ }
171
421
 
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];
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
+ }
179
430
 
180
- // No type rule for this property
181
- if (!descriptor) {
182
- descriptor = null;
183
- }
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 = Array.isArray(data_value) ? "array" : typeof data_value;
200
-
201
- 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])) {
202
467
  return {
203
468
  ok: false,
204
- message: `Field '${prop}' must be of type '${expected}', got '${actual}'`,
469
+ message: `Field '${fullPath}' failed '${op}' constraint (expected ${op} ${rest[op]})`,
470
+ path: fullPath,
205
471
  };
206
472
  }
207
473
  }
208
474
  }
209
475
 
210
- // enum validation
211
- if (
212
- data_value !== undefined &&
213
- data_value !== null &&
214
- allowed &&
215
- !allowed.includes(data_value)
216
- ) {
217
- return {
218
- ok: false,
219
- message: `Field '${prop}' must be one of: ${allowed.join(", ")}`,
220
- };
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
+ }
221
519
  }
222
520
 
223
521
  return { ok: true };
@@ -231,47 +529,45 @@ class Route_table extends Headers {
231
529
  for (const prop in rules) {
232
530
  const rule = rules[prop];
233
531
 
532
+ // ---------- $logic (or / and) ----------
234
533
  if (prop === "$logic") {
235
- // OR logic
534
+ // OR groups: at least one of the listed properties must satisfy the constraints
236
535
  if (rule.or) {
237
536
  for (const orRule of rule.or) {
238
537
  let passed = false;
538
+ const failedProps = [];
239
539
 
240
540
  for (const property of orRule.properties) {
241
541
  const result = await this.check_validation(
242
- { prop: property, ...orRule },
542
+ { prop: property, section, ...orRule },
243
543
  target,
244
544
  );
245
-
246
545
  if (result.ok) {
247
546
  passed = true;
248
547
  break;
249
548
  }
549
+ failedProps.push(property);
250
550
  }
251
551
 
252
552
  if (orRule.required && !passed) {
253
553
  return {
254
554
  ok: false,
255
- message: `At least one of the following fields is required: ${orRule.properties.join(
256
- ", ",
257
- )}`,
555
+ message: `At least one of the following fields is required in '${section}': ${orRule.properties.join(", ")}`,
556
+ path: `$logic.or`,
258
557
  };
259
558
  }
260
559
  }
261
560
  }
262
561
 
263
- // AND logic
562
+ // AND groups: every listed property must satisfy the constraints
264
563
  if (rule.and) {
265
564
  for (const andRule of rule.and) {
266
565
  for (const property of andRule.properties) {
267
566
  const result = await this.check_validation(
268
- { prop: property, ...andRule },
567
+ { prop: property, section, ...andRule },
269
568
  target,
270
569
  );
271
-
272
- if (!result.ok) {
273
- return result;
274
- }
570
+ if (!result.ok) return result;
275
571
  }
276
572
  }
277
573
  }
@@ -279,11 +575,12 @@ class Route_table extends Headers {
279
575
  continue;
280
576
  }
281
577
 
282
- const result = await this.check_validation({ prop, ...rule }, target);
283
-
284
- if (!result.ok) {
285
- return result;
286
- }
578
+ // ---------- normal field ----------
579
+ const result = await this.check_validation(
580
+ { prop, section, ...rule },
581
+ target,
582
+ );
583
+ if (!result.ok) return result;
287
584
  }
288
585
  }
289
586
 
@@ -293,7 +590,7 @@ class Route_table extends Headers {
293
590
  get_route_config = async (name) => {
294
591
  let route = await this.get_route(name);
295
592
 
296
- return route.config;
593
+ return route?.config;
297
594
  };
298
595
 
299
596
  // =========================
@@ -309,20 +606,23 @@ class Route_table extends Headers {
309
606
  let route = await this.get_route(name);
310
607
 
311
608
  if (!route) {
312
- route = await this.get_route("*");
313
- if (!route) {
314
- return {
315
- ok: false,
316
- status: 404,
317
- message: "Route not found",
318
- data: { path: `/${name}`, name },
319
- };
320
- }
609
+ return {
610
+ ok: false,
611
+ status: 404,
612
+ message: "Route not found",
613
+ data: { path: `/${name}`, name },
614
+ };
321
615
  }
322
616
 
323
- 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
+ }
324
625
 
325
- // 📜 schema
326
626
  if (config.schema) {
327
627
  let resp = await this.validate(config.schema, payload);
328
628
 
@@ -332,14 +632,13 @@ class Route_table extends Headers {
332
632
  endpoint: name,
333
633
  args: payload.body,
334
634
  response: resp,
335
- conversation: payload.headers.meta.conversation_id,
635
+ conversation: payload.headers?.meta?.conversation_id,
336
636
  });
337
637
  }
338
638
  return resp;
339
639
  }
340
640
  }
341
641
 
342
- // ⚙️ middleware
343
642
  for (let middleware of config.middlewares || []) {
344
643
  let result = await middleware(payload);
345
644
  if (!result?.ok) {
@@ -348,22 +647,19 @@ class Route_table extends Headers {
348
647
  endpoint: name,
349
648
  args: payload.body,
350
649
  response: result,
351
- // conversation: payload.headers.meta.conversation_id,
352
650
  });
353
651
  }
354
652
  return result;
355
653
  }
356
654
  }
357
655
 
358
- // 🎯 handler
359
656
  let response = await handler(payload);
360
- if (response.agent) {
657
+ if (response?.agent) {
361
658
  await this.call_agency(
362
659
  {
363
660
  endpoint: name,
364
661
  args: payload.body,
365
662
  response,
366
- // conversation: payload.headers.meta.conversation_id,
367
663
  },
368
664
  response.agent,
369
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);