proteum 2.1.0-5 → 2.1.1
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/AGENTS.md +37 -49
- package/README.md +52 -1
- package/agents/framework/AGENTS.md +104 -236
- package/agents/project/AGENTS.md +36 -70
- package/cli/commands/command.ts +243 -0
- package/cli/commands/commandLocalRunner.js +198 -0
- package/cli/commands/dev.ts +95 -1
- package/cli/commands/doctor.ts +8 -74
- package/cli/commands/explain.ts +8 -194
- package/cli/commands/trace.ts +8 -0
- package/cli/compiler/artifacts/commands.ts +217 -0
- package/cli/compiler/artifacts/manifest.ts +17 -2
- package/cli/compiler/artifacts/services.ts +291 -0
- package/cli/compiler/client/index.ts +13 -0
- package/cli/compiler/common/commands.ts +175 -0
- package/cli/compiler/common/proteumManifest.ts +15 -124
- package/cli/compiler/index.ts +25 -2
- package/cli/compiler/server/index.ts +3 -0
- package/cli/presentation/commands.ts +37 -5
- package/cli/runtime/commands.ts +29 -1
- package/cli/tsconfig.json +4 -1
- package/cli/utils/check.ts +1 -1
- package/client/app/component.tsx +11 -0
- package/client/dev/profiler/index.tsx +1511 -0
- package/client/dev/profiler/noop.tsx +5 -0
- package/client/dev/profiler/runtime.noop.ts +116 -0
- package/client/dev/profiler/runtime.ts +840 -0
- package/client/services/router/components/router.tsx +30 -2
- package/client/services/router/index.tsx +25 -0
- package/client/services/router/request/api.ts +133 -17
- package/commands/proteum/diagnostics.ts +11 -0
- package/common/dev/commands.ts +50 -0
- package/common/dev/diagnostics.ts +298 -0
- package/common/dev/profiler.ts +91 -0
- package/common/dev/proteumManifest.ts +135 -0
- package/common/dev/requestTrace.ts +28 -1
- package/docs/dev-commands.md +86 -0
- package/docs/request-tracing.md +2 -0
- package/package.json +1 -2
- package/server/app/commands.ts +35 -370
- package/server/app/commandsManager.ts +393 -0
- package/server/app/container/console/index.ts +0 -2
- package/server/app/container/trace/index.ts +88 -8
- package/server/app/devCommands.ts +192 -0
- package/server/app/devDiagnostics.ts +53 -0
- package/server/app/index.ts +27 -4
- package/server/services/cron/CronTask.ts +73 -5
- package/server/services/cron/index.ts +34 -11
- package/server/services/fetch/index.ts +3 -10
- package/server/services/prisma/index.ts +1 -1
- package/server/services/router/http/index.ts +132 -21
- package/server/services/router/index.ts +40 -4
- package/server/services/router/request/api.ts +30 -1
- package/skills/clean-project-code/SKILL.md +7 -2
- package/test-results/.last-run.json +4 -0
- package/types/aliases.d.ts +6 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
import { Cli, Command as ClipanionCommand, Option, UsageError } from 'clipanion';
|
|
6
|
+
|
|
7
|
+
import type { Application } from './index';
|
|
8
|
+
import Service from '@server/app/service';
|
|
9
|
+
import { InputError, NotFound } from '@common/errors';
|
|
10
|
+
|
|
11
|
+
/*----------------------------------
|
|
12
|
+
- TYPES
|
|
13
|
+
----------------------------------*/
|
|
14
|
+
|
|
15
|
+
type CommandCallback<TArgs extends any[]> = (...args: TArgs) => Promise<any>;
|
|
16
|
+
|
|
17
|
+
export type CommandsList = { [commandName: string]: RuntimeCommand };
|
|
18
|
+
|
|
19
|
+
export type RuntimeCommand<TArgs extends any[] = any[]> = {
|
|
20
|
+
name: string;
|
|
21
|
+
description: string;
|
|
22
|
+
run?: CommandCallback<TArgs>;
|
|
23
|
+
childrens: CommandsList;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/*----------------------------------
|
|
27
|
+
- SERVICE TYPES
|
|
28
|
+
----------------------------------*/
|
|
29
|
+
|
|
30
|
+
const LogPrefix = `[commands]`;
|
|
31
|
+
|
|
32
|
+
export type Config = { debug: boolean };
|
|
33
|
+
|
|
34
|
+
export type Hooks = {};
|
|
35
|
+
|
|
36
|
+
type TCommandArgumentValue = string | number | boolean;
|
|
37
|
+
type TParsedCommandArgs = { [key: string]: TCommandArgumentValue | TCommandArgumentValue[] };
|
|
38
|
+
|
|
39
|
+
const commandValuePattern = /^-?(?:\d+|\d*\.\d+)$/;
|
|
40
|
+
|
|
41
|
+
const tokenizeCommandString = (commandString: string) => {
|
|
42
|
+
const tokens: string[] = [];
|
|
43
|
+
let currentToken = '';
|
|
44
|
+
let quote: '"' | "'" | undefined;
|
|
45
|
+
let escaping = false;
|
|
46
|
+
|
|
47
|
+
for (const character of commandString) {
|
|
48
|
+
if (escaping) {
|
|
49
|
+
currentToken += character;
|
|
50
|
+
escaping = false;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (character === '\\') {
|
|
55
|
+
escaping = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (quote) {
|
|
60
|
+
if (character === quote) {
|
|
61
|
+
quote = undefined;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
currentToken += character;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (character === '"' || character === "'") {
|
|
70
|
+
quote = character;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (/\s/.test(character)) {
|
|
75
|
+
if (!currentToken) continue;
|
|
76
|
+
|
|
77
|
+
tokens.push(currentToken);
|
|
78
|
+
currentToken = '';
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
currentToken += character;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (escaping) currentToken += '\\';
|
|
86
|
+
if (currentToken) tokens.push(currentToken);
|
|
87
|
+
|
|
88
|
+
return tokens;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const isOptionToken = (token: string) => /^-(?!\d+(\.\d+)?$).+/.test(token);
|
|
92
|
+
|
|
93
|
+
const normalizeCommandValue = (value: string): TCommandArgumentValue => {
|
|
94
|
+
if (value === 'true') return true;
|
|
95
|
+
if (value === 'false') return false;
|
|
96
|
+
if (commandValuePattern.test(value)) return Number(value);
|
|
97
|
+
|
|
98
|
+
return value;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const addParsedArgValue = (args: TParsedCommandArgs, key: string, value: TCommandArgumentValue) => {
|
|
102
|
+
const existingValue = args[key];
|
|
103
|
+
|
|
104
|
+
if (existingValue === undefined) {
|
|
105
|
+
args[key] = value;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
args[key] = Array.isArray(existingValue) ? [...existingValue, value] : [existingValue, value];
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const parseCommandOptionTokens = (tokens: string[]) => {
|
|
113
|
+
const namedArguments: TParsedCommandArgs = {};
|
|
114
|
+
const positionalArguments: string[] = [];
|
|
115
|
+
|
|
116
|
+
let usePositionalOnly = false;
|
|
117
|
+
|
|
118
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
119
|
+
const token = tokens[index];
|
|
120
|
+
|
|
121
|
+
if (usePositionalOnly) {
|
|
122
|
+
positionalArguments.push(token);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (token === '--') {
|
|
127
|
+
usePositionalOnly = true;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (token === '--help' || token === '-h') return { help: true as const, args: namedArguments, positionalArguments };
|
|
132
|
+
|
|
133
|
+
if (token.startsWith('--') && token.length > 2) {
|
|
134
|
+
const body = token.slice(2);
|
|
135
|
+
|
|
136
|
+
if (body.startsWith('no-') && body.length > 3) {
|
|
137
|
+
addParsedArgValue(namedArguments, body.slice(3), false);
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const equalsIndex = body.indexOf('=');
|
|
142
|
+
|
|
143
|
+
if (equalsIndex >= 0) {
|
|
144
|
+
addParsedArgValue(
|
|
145
|
+
namedArguments,
|
|
146
|
+
body.slice(0, equalsIndex),
|
|
147
|
+
normalizeCommandValue(body.slice(equalsIndex + 1)),
|
|
148
|
+
);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const nextToken = tokens[index + 1];
|
|
153
|
+
|
|
154
|
+
if (nextToken !== undefined && !isOptionToken(nextToken)) {
|
|
155
|
+
addParsedArgValue(namedArguments, body, normalizeCommandValue(nextToken));
|
|
156
|
+
index++;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
addParsedArgValue(namedArguments, body, true);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (token.startsWith('-') && token.length > 1 && !commandValuePattern.test(token)) {
|
|
165
|
+
const body = token.slice(1);
|
|
166
|
+
const equalsIndex = body.indexOf('=');
|
|
167
|
+
|
|
168
|
+
if (equalsIndex >= 0) {
|
|
169
|
+
addParsedArgValue(
|
|
170
|
+
namedArguments,
|
|
171
|
+
body.slice(0, equalsIndex),
|
|
172
|
+
normalizeCommandValue(body.slice(equalsIndex + 1)),
|
|
173
|
+
);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const nextToken = tokens[index + 1];
|
|
178
|
+
|
|
179
|
+
if (body.length === 1 && nextToken !== undefined && !isOptionToken(nextToken)) {
|
|
180
|
+
addParsedArgValue(namedArguments, body, normalizeCommandValue(nextToken));
|
|
181
|
+
index++;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const shortFlag of body) addParsedArgValue(namedArguments, shortFlag, true);
|
|
186
|
+
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
positionalArguments.push(token);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return { help: false as const, args: namedArguments, positionalArguments };
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/*----------------------------------
|
|
197
|
+
- SERVICE
|
|
198
|
+
----------------------------------*/
|
|
199
|
+
|
|
200
|
+
export default class CommandsManager extends Service<Config, Hooks, Application> {
|
|
201
|
+
public priority = 2 as 2;
|
|
202
|
+
|
|
203
|
+
public commandsIndex: CommandsList = {};
|
|
204
|
+
|
|
205
|
+
private runtimeCli?: Cli;
|
|
206
|
+
|
|
207
|
+
public command<TArgs extends any[]>(
|
|
208
|
+
...args:
|
|
209
|
+
| [name: string, description: string, childrens: RuntimeCommand[]]
|
|
210
|
+
| [name: string, description: string, run: CommandCallback<TArgs>, childrens?: RuntimeCommand[]]
|
|
211
|
+
): RuntimeCommand {
|
|
212
|
+
let name: string, description: string;
|
|
213
|
+
let childrens: RuntimeCommand[] | undefined;
|
|
214
|
+
let run: CommandCallback<TArgs> | undefined;
|
|
215
|
+
|
|
216
|
+
if (typeof args[2] === 'object') [name, description, childrens] = args;
|
|
217
|
+
else [name, description, run, childrens] = args;
|
|
218
|
+
|
|
219
|
+
const command: RuntimeCommand = { name, description, run, childrens: childrens ? this.indexFromList(childrens) : {} };
|
|
220
|
+
|
|
221
|
+
return command;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
private indexFromList(list: RuntimeCommand[]): CommandsList {
|
|
225
|
+
const index: CommandsList = {};
|
|
226
|
+
for (const command of list) index[command.name] = command;
|
|
227
|
+
|
|
228
|
+
return index;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private invalidateRuntimeCli() {
|
|
232
|
+
this.runtimeCli = undefined;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private createRuntimeCommandClass(command: RuntimeCommand, path: string[]) {
|
|
236
|
+
const manager = this;
|
|
237
|
+
const usage = ClipanionCommand.Usage({ description: command.description });
|
|
238
|
+
|
|
239
|
+
if (command.run === undefined) {
|
|
240
|
+
class RuntimeNamespaceCommand extends ClipanionCommand {
|
|
241
|
+
public static override paths = [path];
|
|
242
|
+
public static override usage = usage;
|
|
243
|
+
|
|
244
|
+
public async execute() {
|
|
245
|
+
throw new NotFound(`This command isn't runnable.`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
Object.defineProperty(RuntimeNamespaceCommand, 'name', {
|
|
250
|
+
value: `${path.map((segment) => segment.replace(/[^A-Za-z0-9]/g, '_')).join('_') || 'Root'}NamespaceCommand`,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
return RuntimeNamespaceCommand;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
class RuntimeRunnableCommand extends ClipanionCommand {
|
|
257
|
+
public static override paths = [path];
|
|
258
|
+
public static override usage = usage;
|
|
259
|
+
|
|
260
|
+
public proxy = Option.Proxy({ name: 'args' });
|
|
261
|
+
|
|
262
|
+
public async execute() {
|
|
263
|
+
return manager.executeRegisteredCommand(command, path, this.proxy);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
Object.defineProperty(RuntimeRunnableCommand, 'name', {
|
|
268
|
+
value: `${path.map((segment) => segment.replace(/[^A-Za-z0-9]/g, '_')).join('_') || 'Root'}RunnableCommand`,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
return RuntimeRunnableCommand;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private createRuntimeCli() {
|
|
275
|
+
const cli = new Cli({
|
|
276
|
+
binaryName: this.app.identity.identifier || 'app',
|
|
277
|
+
enableCapture: false,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const registerCommands = (commands: CommandsList, parentPath: string[] = []) => {
|
|
281
|
+
for (const command of Object.values(commands)) {
|
|
282
|
+
const path = [...parentPath, command.name];
|
|
283
|
+
|
|
284
|
+
cli.register(this.createRuntimeCommandClass(command, path));
|
|
285
|
+
|
|
286
|
+
if (Object.keys(command.childrens).length > 0) registerCommands(command.childrens, path);
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
registerCommands(this.commandsIndex);
|
|
291
|
+
|
|
292
|
+
return cli;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private getRuntimeCli() {
|
|
296
|
+
this.runtimeCli ??= this.createRuntimeCli();
|
|
297
|
+
|
|
298
|
+
return this.runtimeCli;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private async executeRegisteredCommand(command: RuntimeCommand, path: string[], proxyTokens: string[]) {
|
|
302
|
+
if (command.run === undefined) throw new NotFound(`This command isn't runnable.`);
|
|
303
|
+
|
|
304
|
+
const { help, args, positionalArguments } = parseCommandOptionTokens(proxyTokens);
|
|
305
|
+
|
|
306
|
+
this.config.debug &&
|
|
307
|
+
console.log(LogPrefix, `Run command path: ${path.join(' ')} | Parsed proxy tokens:`, {
|
|
308
|
+
proxyTokens,
|
|
309
|
+
args,
|
|
310
|
+
positionalArguments,
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
if (help) {
|
|
314
|
+
const cli = this.getRuntimeCli();
|
|
315
|
+
const runtimeCommand = cli.process(path, Cli.defaultContext);
|
|
316
|
+
|
|
317
|
+
return cli.usage(runtimeCommand, { detailed: true });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (positionalArguments.length > 0) {
|
|
321
|
+
throw new UsageError(
|
|
322
|
+
`Unexpected positional arguments for "${path.join(' ')}": ${positionalArguments.join(', ')}.`,
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const argsList = Object.values(args);
|
|
327
|
+
|
|
328
|
+
return command.run(...(argsList as Parameters<NonNullable<typeof command.run>>));
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private createRuntimeCliApi(cli: Cli) {
|
|
332
|
+
return {
|
|
333
|
+
binaryLabel: cli.binaryLabel,
|
|
334
|
+
binaryName: cli.binaryName,
|
|
335
|
+
binaryVersion: cli.binaryVersion,
|
|
336
|
+
enableCapture: cli.enableCapture,
|
|
337
|
+
enableColors: cli.enableColors,
|
|
338
|
+
definitions: () => cli.definitions(),
|
|
339
|
+
definition: (commandClass: any) => cli.definition(commandClass),
|
|
340
|
+
error: (error: Error, opts?: any) => cli.error(error, opts),
|
|
341
|
+
format: (colored?: boolean) => cli.format(colored),
|
|
342
|
+
process: (input: string[]) => cli.process(input, Cli.defaultContext),
|
|
343
|
+
run: (input: string[]) => cli.run(input, Cli.defaultContext),
|
|
344
|
+
usage: (command?: any, opts?: any) => cli.usage(command, opts),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/*----------------------------------
|
|
349
|
+
- REGISTER
|
|
350
|
+
----------------------------------*/
|
|
351
|
+
public fromList(list: RuntimeCommand[]) {
|
|
352
|
+
for (const command of list) {
|
|
353
|
+
if (this.commandsIndex[command.name] !== undefined)
|
|
354
|
+
throw new Error(`Tried to register command "${command.name}", but it already has been defined.`);
|
|
355
|
+
|
|
356
|
+
this.commandsIndex[command.name] = command;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
this.invalidateRuntimeCli();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/*----------------------------------
|
|
363
|
+
- RUN
|
|
364
|
+
----------------------------------*/
|
|
365
|
+
public async run(commandString: string) {
|
|
366
|
+
const tokens = tokenizeCommandString(commandString);
|
|
367
|
+
|
|
368
|
+
this.config.debug && console.log(LogPrefix, `Run command: ${commandString} | Tokens:`, tokens);
|
|
369
|
+
|
|
370
|
+
if (tokens.length === 0) throw new NotFound(`Command not found.`);
|
|
371
|
+
|
|
372
|
+
const cli = this.getRuntimeCli();
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
const command = cli.process(tokens, Cli.defaultContext);
|
|
376
|
+
|
|
377
|
+
if (command.help) return cli.usage(command, { detailed: true });
|
|
378
|
+
|
|
379
|
+
command.context = Cli.defaultContext;
|
|
380
|
+
command.cli = this.createRuntimeCliApi(cli);
|
|
381
|
+
|
|
382
|
+
return await command.validateAndExecute();
|
|
383
|
+
} catch (error) {
|
|
384
|
+
if (error instanceof UsageError) throw new InputError(error.message);
|
|
385
|
+
|
|
386
|
+
if (error instanceof Error && ['UnknownSyntaxError', 'AmbiguousSyntaxError'].includes(error.name)) {
|
|
387
|
+
throw new NotFound(error.message);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
throw error;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
@@ -5,6 +5,8 @@ import type ApplicationContainer from '..';
|
|
|
5
5
|
import {
|
|
6
6
|
traceCaptureModes,
|
|
7
7
|
type TTraceCaptureMode,
|
|
8
|
+
type TTraceCall,
|
|
9
|
+
type TTraceCallOrigin,
|
|
8
10
|
type TTraceEvent,
|
|
9
11
|
type TTraceEventType,
|
|
10
12
|
type TTraceSummaryValue,
|
|
@@ -53,12 +55,11 @@ const summarizeValue = (
|
|
|
53
55
|
if (value === undefined) return { kind: 'undefined' };
|
|
54
56
|
if (value === null) return null;
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
if (
|
|
59
|
-
if (
|
|
60
|
-
if (
|
|
61
|
-
if (primitiveType === 'function') return { kind: 'function', name: value.name || 'anonymous' };
|
|
58
|
+
if (typeof value === 'string') return summarizeString(value);
|
|
59
|
+
if (typeof value === 'number' || typeof value === 'boolean') return value;
|
|
60
|
+
if (typeof value === 'bigint') return { kind: 'bigint', value: value.toString() };
|
|
61
|
+
if (typeof value === 'symbol') return { kind: 'symbol', value: value.toString() };
|
|
62
|
+
if (typeof value === 'function') return { kind: 'function', name: value.name || 'anonymous' };
|
|
62
63
|
|
|
63
64
|
if (value instanceof Date) return { kind: 'date', value: value.toISOString() };
|
|
64
65
|
if (value instanceof Error) return summarizeError(value);
|
|
@@ -113,6 +114,9 @@ const summarizeDetails = (details: TTraceDetails, capture: TTraceCaptureMode) =>
|
|
|
113
114
|
return summarized;
|
|
114
115
|
};
|
|
115
116
|
|
|
117
|
+
const summarizeCaptureValue = (value: TTraceInspectable, capture: TTraceCaptureMode, key: string) =>
|
|
118
|
+
summarizeValue(value, capture === 'deep' ? 3 : 1, new WeakSet<object>(), [key]);
|
|
119
|
+
|
|
116
120
|
const nowIso = () => new Date().toISOString();
|
|
117
121
|
|
|
118
122
|
export default class Trace {
|
|
@@ -126,7 +130,7 @@ export default class Trace {
|
|
|
126
130
|
) {}
|
|
127
131
|
|
|
128
132
|
public isEnabled() {
|
|
129
|
-
return this.config.enable && this.container.Environment.profile === 'dev';
|
|
133
|
+
return __DEV__ && this.config.enable && this.container.Environment.profile === 'dev';
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
public armNextRequest(capture: string) {
|
|
@@ -138,7 +142,17 @@ export default class Trace {
|
|
|
138
142
|
return capture;
|
|
139
143
|
}
|
|
140
144
|
|
|
141
|
-
public startRequest(input: {
|
|
145
|
+
public startRequest(input: {
|
|
146
|
+
id: string;
|
|
147
|
+
method: string;
|
|
148
|
+
path: string;
|
|
149
|
+
url: string;
|
|
150
|
+
headers: object;
|
|
151
|
+
data: object;
|
|
152
|
+
profilerSessionId?: string;
|
|
153
|
+
profilerOrigin?: string;
|
|
154
|
+
profilerParentRequestId?: string;
|
|
155
|
+
}) {
|
|
142
156
|
if (!this.isEnabled()) return;
|
|
143
157
|
|
|
144
158
|
const capture = this.armedCapture ?? this.config.capture;
|
|
@@ -150,8 +164,12 @@ export default class Trace {
|
|
|
150
164
|
path: input.path,
|
|
151
165
|
url: input.url,
|
|
152
166
|
capture,
|
|
167
|
+
profilerSessionId: input.profilerSessionId,
|
|
168
|
+
profilerOrigin: input.profilerOrigin,
|
|
169
|
+
profilerParentRequestId: input.profilerParentRequestId,
|
|
153
170
|
startedAt: nowIso(),
|
|
154
171
|
droppedEvents: 0,
|
|
172
|
+
calls: [],
|
|
155
173
|
events: [],
|
|
156
174
|
};
|
|
157
175
|
|
|
@@ -224,6 +242,64 @@ export default class Trace {
|
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
|
|
245
|
+
public startCall(
|
|
246
|
+
requestId: string,
|
|
247
|
+
input: {
|
|
248
|
+
origin: TTraceCallOrigin;
|
|
249
|
+
label: string;
|
|
250
|
+
method?: string;
|
|
251
|
+
path?: string;
|
|
252
|
+
fetcherId?: string;
|
|
253
|
+
parentId?: string;
|
|
254
|
+
requestDataKeys?: string[];
|
|
255
|
+
requestData?: TTraceInspectable;
|
|
256
|
+
},
|
|
257
|
+
) {
|
|
258
|
+
const trace = this.requests.get(requestId);
|
|
259
|
+
if (!trace) return undefined;
|
|
260
|
+
|
|
261
|
+
const call: TTraceCall = {
|
|
262
|
+
id: `${requestId}:call:${trace.calls.length}`,
|
|
263
|
+
parentId: input.parentId,
|
|
264
|
+
origin: input.origin,
|
|
265
|
+
label: input.label,
|
|
266
|
+
method: input.method || '',
|
|
267
|
+
path: input.path || '',
|
|
268
|
+
fetcherId: input.fetcherId,
|
|
269
|
+
startedAt: nowIso(),
|
|
270
|
+
requestDataKeys: input.requestDataKeys || [],
|
|
271
|
+
requestData: input.requestData !== undefined ? summarizeCaptureValue(input.requestData, trace.capture, 'requestData') : undefined,
|
|
272
|
+
resultKeys: [],
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
trace.calls.push(call);
|
|
276
|
+
return call.id;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
public finishCall(
|
|
280
|
+
requestId: string,
|
|
281
|
+
callId: string | undefined,
|
|
282
|
+
output: {
|
|
283
|
+
statusCode?: number;
|
|
284
|
+
errorMessage?: string;
|
|
285
|
+
resultKeys?: string[];
|
|
286
|
+
result?: TTraceInspectable;
|
|
287
|
+
} = {},
|
|
288
|
+
) {
|
|
289
|
+
if (!callId) return;
|
|
290
|
+
|
|
291
|
+
const trace = this.requests.get(requestId);
|
|
292
|
+
const call = trace?.calls.find((candidate) => candidate.id === callId);
|
|
293
|
+
if (!trace || !call) return;
|
|
294
|
+
|
|
295
|
+
call.finishedAt = nowIso();
|
|
296
|
+
call.durationMs = Math.max(0, Date.parse(call.finishedAt) - Date.parse(call.startedAt));
|
|
297
|
+
call.statusCode = output.statusCode;
|
|
298
|
+
call.errorMessage = output.errorMessage;
|
|
299
|
+
call.resultKeys = output.resultKeys || [];
|
|
300
|
+
call.result = output.result !== undefined ? summarizeCaptureValue(output.result, trace.capture, 'result') : undefined;
|
|
301
|
+
}
|
|
302
|
+
|
|
227
303
|
public listRequests(limit = 20): TRequestTraceListItem[] {
|
|
228
304
|
return [...this.order]
|
|
229
305
|
.reverse()
|
|
@@ -244,7 +320,11 @@ export default class Trace {
|
|
|
244
320
|
droppedEvents: trace.droppedEvents,
|
|
245
321
|
persistedFilepath: trace.persistedFilepath,
|
|
246
322
|
errorMessage: trace.errorMessage,
|
|
323
|
+
profilerSessionId: trace.profilerSessionId,
|
|
324
|
+
profilerOrigin: trace.profilerOrigin,
|
|
325
|
+
profilerParentRequestId: trace.profilerParentRequestId,
|
|
247
326
|
eventCount: trace.events.length,
|
|
327
|
+
callCount: trace.calls.length,
|
|
248
328
|
}));
|
|
249
329
|
}
|
|
250
330
|
|