proteum 2.1.0 → 2.1.2

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.
Files changed (95) hide show
  1. package/AGENTS.md +44 -98
  2. package/README.md +143 -10
  3. package/agents/framework/AGENTS.md +146 -886
  4. package/agents/project/AGENTS.md +73 -127
  5. package/agents/project/client/AGENTS.md +22 -93
  6. package/agents/project/client/pages/AGENTS.md +24 -26
  7. package/agents/project/server/routes/AGENTS.md +10 -8
  8. package/agents/project/server/services/AGENTS.md +22 -159
  9. package/agents/project/tests/AGENTS.md +11 -8
  10. package/cli/app/config.ts +7 -20
  11. package/cli/bin.js +8 -0
  12. package/cli/commands/command.ts +243 -0
  13. package/cli/commands/commandLocalRunner.js +198 -0
  14. package/cli/commands/create.ts +5 -0
  15. package/cli/commands/deploy/web.ts +1 -2
  16. package/cli/commands/dev.ts +98 -2
  17. package/cli/commands/doctor.ts +8 -74
  18. package/cli/commands/explain.ts +8 -186
  19. package/cli/commands/init.ts +2 -94
  20. package/cli/commands/trace.ts +228 -0
  21. package/cli/compiler/artifacts/commands.ts +217 -0
  22. package/cli/compiler/artifacts/manifest.ts +35 -21
  23. package/cli/compiler/artifacts/services.ts +300 -1
  24. package/cli/compiler/client/index.ts +43 -8
  25. package/cli/compiler/common/commands.ts +175 -0
  26. package/cli/compiler/common/index.ts +1 -1
  27. package/cli/compiler/common/proteumManifest.ts +15 -114
  28. package/cli/compiler/index.ts +25 -2
  29. package/cli/compiler/server/index.ts +31 -6
  30. package/cli/index.ts +1 -4
  31. package/cli/paths.ts +16 -1
  32. package/cli/presentation/commands.ts +104 -14
  33. package/cli/presentation/devSession.ts +22 -3
  34. package/cli/presentation/proteum_logo_400x400_square_icon.txt +400 -0
  35. package/cli/runtime/commands.ts +121 -4
  36. package/cli/scaffold/index.ts +720 -0
  37. package/cli/scaffold/templates.ts +344 -0
  38. package/cli/scaffold/types.ts +26 -0
  39. package/cli/tsconfig.json +4 -1
  40. package/cli/utils/check.ts +1 -1
  41. package/client/app/component.tsx +13 -9
  42. package/client/dev/profiler/index.tsx +2511 -0
  43. package/client/dev/profiler/noop.tsx +5 -0
  44. package/client/dev/profiler/runtime.noop.ts +116 -0
  45. package/client/dev/profiler/runtime.ts +840 -0
  46. package/client/services/router/components/router.tsx +30 -2
  47. package/client/services/router/index.tsx +27 -3
  48. package/client/services/router/request/api.ts +133 -17
  49. package/commands/proteum/diagnostics.ts +11 -0
  50. package/common/dev/commands.ts +50 -0
  51. package/common/dev/diagnostics.ts +298 -0
  52. package/common/dev/profiler.ts +92 -0
  53. package/common/dev/proteumManifest.ts +135 -0
  54. package/common/dev/requestTrace.ts +115 -0
  55. package/common/env/proteumEnv.ts +284 -0
  56. package/common/router/index.ts +4 -22
  57. package/docs/dev-commands.md +93 -0
  58. package/docs/diagnostics.md +88 -0
  59. package/docs/request-tracing.md +132 -0
  60. package/eslint.js +11 -6
  61. package/package.json +3 -3
  62. package/server/app/commands.ts +35 -370
  63. package/server/app/commandsManager.ts +393 -0
  64. package/server/app/container/config.ts +11 -49
  65. package/server/app/container/console/index.ts +2 -3
  66. package/server/app/container/index.ts +5 -2
  67. package/server/app/container/trace/index.ts +364 -0
  68. package/server/app/devCommands.ts +192 -0
  69. package/server/app/devDiagnostics.ts +53 -0
  70. package/server/app/index.ts +29 -6
  71. package/server/index.ts +0 -1
  72. package/server/services/auth/index.ts +525 -61
  73. package/server/services/auth/router/index.ts +106 -7
  74. package/server/services/cron/CronTask.ts +73 -5
  75. package/server/services/cron/index.ts +34 -11
  76. package/server/services/fetch/index.ts +3 -10
  77. package/server/services/prisma/index.ts +66 -4
  78. package/server/services/router/http/index.ts +173 -6
  79. package/server/services/router/index.ts +200 -12
  80. package/server/services/router/request/api.ts +30 -1
  81. package/server/services/router/response/index.ts +83 -10
  82. package/server/services/router/response/page/document.tsx +16 -0
  83. package/server/services/router/response/page/index.tsx +27 -1
  84. package/skills/clean-project-code/SKILL.md +7 -2
  85. package/test-results/.last-run.json +4 -0
  86. package/types/aliases.d.ts +6 -0
  87. package/types/global/utils.d.ts +7 -14
  88. package/Rte.zip +0 -0
  89. package/agents/project/agents.md.zip +0 -0
  90. package/doc/TODO.md +0 -71
  91. package/doc/front/router.md +0 -27
  92. package/doc/workspace/workspace.png +0 -0
  93. package/doc/workspace/workspace2.png +0 -0
  94. package/doc/workspace/workspace_26.01.22.png +0 -0
  95. package/server/services/router/http/session.ts.old +0 -40
@@ -25,12 +25,6 @@ import UsersRequestService from './request';
25
25
  - TYPES
26
26
  ----------------------------------*/
27
27
 
28
- /*----------------------------------
29
- - CONFIG
30
- ----------------------------------*/
31
-
32
- const LogPrefix = '[router][auth]';
33
-
34
28
  /*----------------------------------
35
29
  - SERVICE
36
30
  ----------------------------------*/
@@ -59,11 +53,32 @@ export default class AuthenticationRouterService<
59
53
  this.users = this.config.users;
60
54
  }
61
55
 
56
+ private traceRouteAuth(
57
+ request: TRequest,
58
+ route: TAnyRoute,
59
+ details: Record<string, any>,
60
+ minimumCapture: 'summary' | 'resolve' | 'deep' = 'resolve',
61
+ ) {
62
+ this.app.container.Trace.record(
63
+ request.id,
64
+ 'auth.route',
65
+ {
66
+ routePath: route.path || '',
67
+ routeId: route.options.id || '',
68
+ authInput: route.options.auth ?? null,
69
+ tracking: route.options.authTracking ?? null,
70
+ redirectLogged: route.options.redirectLogged ?? null,
71
+ ...details,
72
+ },
73
+ minimumCapture,
74
+ );
75
+ }
76
+
62
77
  public async ready() {
63
78
  // Decode current user
64
79
  this.parent.on('request', async (request: TRequest) => {
65
80
  // TODO: Typings. (context.user ?)
66
- const decoded = await this.users.decode(request.req, true);
81
+ const decoded = await this.users.decode(request.req, true, request.id);
67
82
 
68
83
  request.user = decoded || null;
69
84
  });
@@ -72,10 +87,48 @@ export default class AuthenticationRouterService<
72
87
  this.parent.on('resolved', async (route: TAnyRoute, request: TRequest, response: ServerResponse<TRouter>) => {
73
88
  if (route.options.auth !== undefined) {
74
89
  const tracking = route.options.authTracking ?? null;
90
+ const strategy =
91
+ route.options.auth === false
92
+ ? 'guest-only'
93
+ : route.options.auth === null
94
+ ? 'authenticated'
95
+ : typeof route.options.auth === 'object'
96
+ ? 'conditions'
97
+ : route.options.auth === true
98
+ ? tracking !== null && this.users.config.rules
99
+ ? 'user-via-rules'
100
+ : 'user'
101
+ : tracking !== null && this.users.config.rules
102
+ ? 'role-via-rules'
103
+ : 'role';
104
+
105
+ this.traceRouteAuth(
106
+ request,
107
+ route,
108
+ {
109
+ phase: 'start',
110
+ strategy,
111
+ },
112
+ 'resolve',
113
+ );
75
114
 
76
115
  // Guest-only routes can still redirect authenticated users away.
77
116
  if (route.options.auth === false) {
78
117
  const currentUser = this.users.check(request, false, tracking);
118
+ const redirected = Boolean(route.options.redirectLogged && currentUser);
119
+
120
+ this.traceRouteAuth(
121
+ request,
122
+ route,
123
+ {
124
+ phase: 'result',
125
+ strategy,
126
+ outcome: redirected ? 'redirected' : 'allowed',
127
+ userPresent: currentUser !== null,
128
+ redirectTo: redirected ? route.options.redirectLogged : null,
129
+ },
130
+ 'resolve',
131
+ );
79
132
 
80
133
  if (route.options.redirectLogged && currentUser) response.redirect(route.options.redirectLogged);
81
134
  return;
@@ -83,11 +136,13 @@ export default class AuthenticationRouterService<
83
136
 
84
137
  if (route.options.auth === null) {
85
138
  this.users.check(request, null, tracking);
139
+ this.traceRouteAuth(request, route, { phase: 'result', strategy, outcome: 'allowed' }, 'resolve');
86
140
  return;
87
141
  }
88
142
 
89
143
  if (typeof route.options.auth === 'object') {
90
144
  this.users.check(request, route.options.auth as TAuthCheckConditions, tracking);
145
+ this.traceRouteAuth(request, route, { phase: 'result', strategy, outcome: 'allowed' }, 'resolve');
91
146
  return;
92
147
  }
93
148
 
@@ -95,10 +150,32 @@ export default class AuthenticationRouterService<
95
150
  if (route.options.auth === true) {
96
151
  if (tracking !== null && this.users.config.rules) {
97
152
  this.users.check(request, { role: 'USER' }, tracking);
153
+ this.traceRouteAuth(
154
+ request,
155
+ route,
156
+ {
157
+ phase: 'result',
158
+ strategy,
159
+ outcome: 'allowed',
160
+ requiredRole: 'USER',
161
+ },
162
+ 'resolve',
163
+ );
98
164
  return;
99
165
  }
100
166
 
101
167
  this.users.check(request, true);
168
+ this.traceRouteAuth(
169
+ request,
170
+ route,
171
+ {
172
+ phase: 'result',
173
+ strategy,
174
+ outcome: 'allowed',
175
+ requiredRole: 'USER',
176
+ },
177
+ 'resolve',
178
+ );
102
179
  return;
103
180
  }
104
181
 
@@ -106,10 +183,32 @@ export default class AuthenticationRouterService<
106
183
 
107
184
  if (tracking !== null && this.users.config.rules) {
108
185
  this.users.check(request, { role: requiredRole }, tracking);
186
+ this.traceRouteAuth(
187
+ request,
188
+ route,
189
+ {
190
+ phase: 'result',
191
+ strategy,
192
+ outcome: 'allowed',
193
+ requiredRole,
194
+ },
195
+ 'resolve',
196
+ );
109
197
  return;
110
198
  }
111
199
 
112
200
  this.users.check(request, requiredRole);
201
+ this.traceRouteAuth(
202
+ request,
203
+ route,
204
+ {
205
+ phase: 'result',
206
+ strategy,
207
+ outcome: 'allowed',
208
+ requiredRole,
209
+ },
210
+ 'resolve',
211
+ );
113
212
  }
114
213
  });
115
214
  }
@@ -10,16 +10,33 @@ import cronParser, { CronExpression } from 'cron-parser';
10
10
  ----------------------------------*/
11
11
 
12
12
  import type CronManager from '.';
13
+ import type {
14
+ TProfilerCronTask,
15
+ TProfilerCronTaskFrequency,
16
+ TProfilerCronTaskRunStatus,
17
+ TProfilerCronTaskTrigger,
18
+ } from '@common/dev/profiler';
13
19
 
14
20
  export type TFrequence = string | Date;
15
21
  export type TRunner = () => Promise<any>;
22
+ const nowIso = () => new Date().toISOString();
16
23
 
17
24
  /*----------------------------------
18
25
  - CLASS
19
26
  ----------------------------------*/
20
27
  export default class CronTask {
21
28
  public cron?: CronExpression;
29
+ public frequency!: TProfilerCronTaskFrequency;
22
30
  public nextInvocation?: Date;
31
+ public registeredAt = nowIso();
32
+ public running = false;
33
+ public lastTrigger?: TProfilerCronTaskTrigger;
34
+ public lastRunStartedAt?: string;
35
+ public lastRunFinishedAt?: string;
36
+ public lastRunDurationMs?: number;
37
+ public lastRunStatus?: TProfilerCronTaskRunStatus;
38
+ public lastErrorMessage?: string;
39
+ public runCount = 0;
23
40
 
24
41
  public constructor(
25
42
  private manager: CronManager,
@@ -35,6 +52,10 @@ export default class CronTask {
35
52
 
36
53
  public schedule(next: TFrequence) {
37
54
  this.cron = undefined;
55
+ this.frequency =
56
+ typeof next === 'string'
57
+ ? { kind: 'cron', value: next }
58
+ : { kind: 'date', value: next.toISOString() };
38
59
 
39
60
  // Cron expression
40
61
  if (typeof next === 'string') {
@@ -58,16 +79,63 @@ export default class CronTask {
58
79
  else this.nextInvocation = undefined;
59
80
  }
60
81
 
61
- public run(now: boolean = false) {
82
+ public toProfilerTask(): TProfilerCronTask {
83
+ return {
84
+ name: this.nom,
85
+ registeredAt: this.registeredAt,
86
+ frequency: this.frequency,
87
+ autoexec: Boolean(this.autoexec),
88
+ automaticExecution: this.manager.isAutomaticExecutionEnabled(),
89
+ nextInvocation: this.nextInvocation?.toISOString(),
90
+ running: this.running,
91
+ lastTrigger: this.lastTrigger,
92
+ lastRunStartedAt: this.lastRunStartedAt,
93
+ lastRunFinishedAt: this.lastRunFinishedAt,
94
+ lastRunDurationMs: this.lastRunDurationMs,
95
+ lastRunStatus: this.lastRunStatus,
96
+ lastErrorMessage: this.lastErrorMessage,
97
+ runCount: this.runCount,
98
+ };
99
+ }
100
+
101
+ public async run(now: boolean = false, trigger: TProfilerCronTaskTrigger = 'scheduler') {
62
102
  // Update invocation date
63
103
  const maintenant = new Date();
64
104
  const runnable = this.nextInvocation !== undefined && this.nextInvocation.valueOf() <= maintenant.valueOf();
65
105
  if (runnable) this.scheduleNext();
66
- else if (now === false) return;
106
+ else if (now === false) return false;
107
+
108
+ if (this.running) return false;
109
+
110
+ this.running = true;
111
+ this.lastTrigger = trigger;
112
+ const startedAt = nowIso();
113
+ this.lastRunStartedAt = startedAt;
114
+ this.lastRunFinishedAt = undefined;
115
+ this.lastRunDurationMs = undefined;
116
+ this.lastRunStatus = undefined;
117
+ this.lastErrorMessage = undefined;
67
118
 
68
119
  // Execution
69
- this.runner().then(() => {
70
- this.manager.config.debug && console.info(`Task runned.`);
71
- });
120
+ try {
121
+ await this.runner();
122
+ this.runCount += 1;
123
+ const finishedAt = nowIso();
124
+ this.lastRunFinishedAt = finishedAt;
125
+ this.lastRunDurationMs = Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt));
126
+ this.lastRunStatus = 'completed';
127
+ this.manager.config.debug && console.info(`[cron][${this.nom}] Task completed.`);
128
+ return true;
129
+ } catch (error) {
130
+ this.runCount += 1;
131
+ const finishedAt = nowIso();
132
+ this.lastRunFinishedAt = finishedAt;
133
+ this.lastRunDurationMs = Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt));
134
+ this.lastRunStatus = 'error';
135
+ this.lastErrorMessage = error instanceof Error ? error.message : String(error);
136
+ throw error;
137
+ } finally {
138
+ this.running = false;
139
+ }
72
140
  }
73
141
  }
@@ -6,6 +6,7 @@
6
6
  import type { Application } from '@server/app/index';
7
7
  import Service from '@server/app/service';
8
8
  import { NotFound } from '@common/errors';
9
+ import type { TProfilerCronTaskTrigger } from '@common/dev/profiler';
9
10
  import context from '@server/context';
10
11
 
11
12
  /*----------------------------------
@@ -32,7 +33,7 @@ export type Services = {};
32
33
 
33
34
  export default class CronManager extends Service<Config, Hooks, Application, Application> {
34
35
  public static taches: { [nom: string]: CronTask } = {};
35
- public static timer: NodeJS.Timeout;
36
+ public static timer?: NodeJS.Timeout;
36
37
 
37
38
  /*----------------------------------
38
39
  - LIFECICLE
@@ -40,8 +41,17 @@ export default class CronManager extends Service<Config, Hooks, Application, App
40
41
 
41
42
  public async ready() {
42
43
  clearInterval(CronManager.timer);
44
+ if (!this.isAutomaticExecutionEnabled()) {
45
+ this.config.debug && console.info('[cron] Automatic execution disabled in dev mode.');
46
+ return;
47
+ }
48
+
43
49
  CronManager.timer = setInterval(() => {
44
- for (const id in CronManager.taches) CronManager.taches[id].run();
50
+ for (const id in CronManager.taches) {
51
+ void this.runTask(id, false, 'scheduler').catch((error) => {
52
+ console.error(`[cron][${id}] Task failed.`, error);
53
+ });
54
+ }
45
55
  }, 10000);
46
56
  }
47
57
 
@@ -59,16 +69,12 @@ export default class CronManager extends Service<Config, Hooks, Application, App
59
69
  * @param autoexec true to execute the task immediatly
60
70
  * @returns The CronTask that just have been created
61
71
  */
62
- public task(nom: string, frequence: TFrequence, run: TRunner, autoexec?: boolean) {
63
- return new Promise<CronTask>((resolve, reject) => {
64
- context.run({ channelType: 'cron', channelId: nom }, async () => {
65
- CronManager.taches[nom] = new CronTask(this, nom, frequence, run, autoexec);
72
+ public async task(nom: string, frequence: TFrequence, run: TRunner, autoexec?: boolean) {
73
+ CronManager.taches[nom] = new CronTask(this, nom, frequence, run, autoexec);
66
74
 
67
- if (autoexec) await CronManager.taches[nom].run(true);
75
+ if (autoexec && this.isAutomaticExecutionEnabled()) await this.runTask(nom, true, 'autoexec');
68
76
 
69
- resolve(CronManager.taches[nom]);
70
- });
71
- });
77
+ return CronManager.taches[nom];
72
78
  }
73
79
 
74
80
  public async exec(nom: string) {
@@ -76,7 +82,8 @@ export default class CronManager extends Service<Config, Hooks, Application, App
76
82
 
77
83
  if (tache === undefined) throw new NotFound('Tâche NotFound: ' + nom);
78
84
 
79
- await tache.run(true);
85
+ await this.runTask(nom, true, 'manual');
86
+ return tache;
80
87
  }
81
88
  public get(): typeof CronManager.taches;
82
89
  public get(name: string): CronTask;
@@ -87,4 +94,20 @@ export default class CronManager extends Service<Config, Hooks, Application, App
87
94
  if (cron === undefined) throw new Error(`L'instance de la tâche cron ${name} n'a pas été trouvée`);
88
95
  return cron;
89
96
  }
97
+
98
+ public isAutomaticExecutionEnabled() {
99
+ return !__DEV__;
100
+ }
101
+
102
+ public listTasks() {
103
+ return Object.values(CronManager.taches).map((task) => task.toProfilerTask());
104
+ }
105
+
106
+ private async runTask(name: string, now: boolean, trigger: TProfilerCronTaskTrigger) {
107
+ const task = this.get(name);
108
+
109
+ return context.run({ channelType: 'cron', channelId: name }, async () => {
110
+ return task.run(now, trigger);
111
+ });
112
+ }
90
113
  }
@@ -7,9 +7,6 @@ import type { default as sharp, Sharp } from 'sharp';
7
7
  import fs from 'fs-extra';
8
8
  import got, { Method, Options, Response as GotResponse } from 'got';
9
9
 
10
- // Node
11
- import request from 'request';
12
-
13
10
  // Core: general
14
11
  import type { Application } from '@server/app/index';
15
12
  import Service from '@server/app/service';
@@ -109,14 +106,10 @@ export default class FetchService extends Service<Config, Hooks, Application, Ap
109
106
  ----------------------------------*/
110
107
 
111
108
  public toBuffer(uri: string): Promise<Buffer> {
112
- return new Promise<Buffer>((resolve, reject) => {
113
- request(uri, { encoding: null }, (err, res, body) => {
114
- if (err) return reject(err);
115
-
116
- if (!body) return reject(`Body is empty for ${uri}.`);
109
+ return got(uri, { responseType: 'buffer', throwHttpErrors: false }).then((response) => {
110
+ if (!response.body || response.body.length === 0) throw new Error(`Body is empty for ${uri}.`);
117
111
 
118
- resolve(body);
119
- });
112
+ return response.body;
120
113
  });
121
114
  }
122
115
 
@@ -23,6 +23,62 @@ import { NotFound } from '@common/errors';
23
23
 
24
24
  export type SqlQuery = ReturnType<ModelsManager['SQL']>;
25
25
 
26
+ type DecimalLike = {
27
+ constructor?: { name?: string };
28
+ equals: (value: number) => boolean;
29
+ toNumber: () => number;
30
+ toString: () => string;
31
+ };
32
+
33
+ /*----------------------------------
34
+ - HELPERS
35
+ ----------------------------------*/
36
+
37
+ const isDecimalLike = (value: object): value is DecimalLike =>
38
+ 'constructor' in value &&
39
+ 'equals' in value &&
40
+ 'toNumber' in value &&
41
+ 'toString' in value &&
42
+ typeof value.constructor === 'function' &&
43
+ value.constructor.name === 'Decimal' &&
44
+ typeof value.equals === 'function' &&
45
+ typeof value.toNumber === 'function' &&
46
+ typeof value.toString === 'function';
47
+
48
+ const isPlainObject = (value: object) => {
49
+ const prototype = Object.getPrototypeOf(value);
50
+ return prototype === Object.prototype || prototype === null;
51
+ };
52
+
53
+ const normalizeBigInt = (value: bigint) => {
54
+ const number = Number(value);
55
+ return Number.isSafeInteger(number) ? number : value.toString();
56
+ };
57
+
58
+ const normalizeDecimal = (value: DecimalLike) => {
59
+ const number = value.toNumber();
60
+ return Number.isFinite(number) && value.equals(number) ? number : value.toString();
61
+ };
62
+
63
+ const normalizeSqlScalar = (value: bigint | DecimalLike) =>
64
+ typeof value === 'bigint' ? normalizeBigInt(value) : normalizeDecimal(value);
65
+
66
+ const normalizeSqlResult = <T>(value: T): T => {
67
+ if (typeof value === 'bigint') return normalizeSqlScalar(value) as T;
68
+
69
+ if (Array.isArray(value)) return value.map((item) => normalizeSqlResult(item)) as T;
70
+
71
+ if (value === null || value === undefined || typeof value !== 'object' || value instanceof Date) return value;
72
+
73
+ if (isDecimalLike(value)) return normalizeSqlScalar(value) as T;
74
+
75
+ if (!isPlainObject(value)) return value;
76
+
77
+ return Object.fromEntries(
78
+ Object.entries(value).map(([key, nestedValue]) => [key, normalizeSqlResult(nestedValue)]),
79
+ ) as T;
80
+ };
81
+
26
82
  /*----------------------------------
27
83
  - SERVICE CONFIG
28
84
  ----------------------------------*/
@@ -33,6 +89,11 @@ export type Hooks = {};
33
89
 
34
90
  export type Services = {};
35
91
 
92
+ // Fix: Do not know how to serialize a BigInt
93
+ BigInt.prototype.toJSON = function () {
94
+ return normalizeBigInt(this.valueOf());
95
+ };
96
+
36
97
  /*----------------------------------
37
98
  - CLASSE
38
99
  ----------------------------------*/
@@ -43,7 +104,7 @@ export default class ModelsManager extends Service<Config, Hooks, Application, A
43
104
  public constructor(...args: TServiceArgs<ModelsManager>) {
44
105
  super(...args);
45
106
 
46
- dotenv.config();
107
+ dotenv.config({ quiet: true });
47
108
 
48
109
  const databaseUrl = process.env.DATABASE_URL;
49
110
  if (!databaseUrl)
@@ -74,9 +135,10 @@ export default class ModelsManager extends Service<Config, Hooks, Application, A
74
135
  public SQL<TRowData extends {} | number | string>(strings: TemplateStringsArray, ...data: any[]) {
75
136
  const string = this.string(strings, ...data);
76
137
 
77
- const query = () => {
78
- return this.client.$queryRawUnsafe(string) as Promise<TRowData[]>;
79
- };
138
+ const query = () =>
139
+ this.client.$queryRawUnsafe(string).then((resultatRequetes) => normalizeSqlResult(resultatRequetes)) as Promise<
140
+ TRowData[]
141
+ >;
80
142
 
81
143
  query.all = query;
82
144
  query.value = <TValue extends any = number>() =>