proteum 2.1.2 → 2.1.6
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 +22 -14
- package/README.md +112 -17
- package/agents/project/AGENTS.md +188 -25
- package/agents/project/CODING_STYLE.md +1 -0
- package/agents/project/client/AGENTS.md +13 -8
- package/agents/project/client/pages/AGENTS.md +17 -9
- package/agents/project/diagnostics.md +52 -0
- package/agents/project/optimizations.md +48 -0
- package/agents/project/server/routes/AGENTS.md +9 -6
- package/agents/project/server/services/AGENTS.md +10 -6
- package/agents/project/tests/AGENTS.md +11 -5
- package/cli/app/config.ts +13 -14
- package/cli/app/index.ts +58 -0
- package/cli/commands/command.ts +8 -0
- package/cli/commands/connect.ts +45 -0
- package/cli/commands/dev.ts +26 -11
- package/cli/commands/diagnose.ts +286 -0
- package/cli/commands/doctor.ts +18 -5
- package/cli/commands/explain.ts +25 -0
- package/cli/commands/perf.ts +243 -0
- package/cli/commands/session.ts +254 -0
- package/cli/commands/sessionLocalRunner.js +188 -0
- package/cli/commands/trace.ts +17 -1
- package/cli/commands/verify.ts +281 -0
- package/cli/compiler/artifacts/connectedProjects.ts +453 -0
- package/cli/compiler/artifacts/controllers.ts +198 -49
- package/cli/compiler/artifacts/discovery.ts +0 -34
- package/cli/compiler/artifacts/manifest.ts +90 -6
- package/cli/compiler/artifacts/routing.ts +2 -2
- package/cli/compiler/artifacts/services.ts +277 -130
- package/cli/compiler/client/index.ts +3 -0
- package/cli/compiler/common/files/style.ts +52 -0
- package/cli/compiler/common/generatedRouteModules.ts +34 -5
- package/cli/compiler/common/scripts.ts +11 -5
- package/cli/compiler/index.ts +2 -1
- package/cli/compiler/server/index.ts +3 -0
- package/cli/presentation/commands.ts +136 -7
- package/cli/presentation/devSession.ts +32 -7
- package/cli/runtime/commands.ts +193 -6
- package/cli/scaffold/index.ts +14 -25
- package/cli/scaffold/templates.ts +41 -27
- package/cli/utils/agents.ts +4 -2
- package/cli/utils/keyboard.ts +8 -0
- package/client/dev/profiler/ApexChart.tsx +66 -0
- package/client/dev/profiler/index.tsx +2798 -417
- package/client/dev/profiler/runtime.noop.ts +12 -0
- package/client/dev/profiler/runtime.ts +195 -4
- package/client/services/router/request/api.ts +6 -1
- package/common/applicationConfig.ts +173 -0
- package/common/applicationConfigLoader.ts +102 -0
- package/common/connectedProjects.ts +113 -0
- package/common/dev/connect.ts +267 -0
- package/common/dev/console.ts +31 -0
- package/common/dev/contractsDoctor.ts +128 -0
- package/common/dev/diagnostics.ts +59 -15
- package/common/dev/inspection.ts +491 -0
- package/common/dev/performance.ts +809 -0
- package/common/dev/profiler.ts +3 -0
- package/common/dev/proteumManifest.ts +31 -6
- package/common/dev/requestTrace.ts +56 -1
- package/common/dev/session.ts +24 -0
- package/common/env/proteumEnv.ts +176 -50
- package/common/router/index.ts +1 -0
- package/common/router/request/api.ts +2 -0
- package/config.ts +5 -0
- package/docs/dev-commands.md +5 -1
- package/docs/dev-sessions.md +90 -0
- package/docs/diagnostics.md +74 -11
- package/docs/request-tracing.md +50 -3
- package/package.json +1 -1
- package/server/app/container/config.ts +16 -87
- package/server/app/container/console/index.ts +42 -8
- package/server/app/container/index.ts +3 -1
- package/server/app/container/trace/index.ts +153 -0
- package/server/app/devDiagnostics.ts +138 -0
- package/server/app/index.ts +18 -8
- package/server/app/service/container.ts +0 -12
- package/server/app/service/index.ts +0 -2
- package/server/services/prisma/index.ts +121 -4
- package/server/services/router/http/index.ts +352 -0
- package/server/services/router/index.ts +50 -47
- package/server/services/router/request/api.ts +160 -19
- package/server/services/router/request/index.ts +8 -0
- package/server/services/router/response/index.ts +24 -1
- package/server/services/router/response/page/document.tsx +5 -0
- package/server/services/router/response/page/index.tsx +10 -0
- package/agents/framework/AGENTS.md +0 -177
- package/server/services/auth/router/service.json +0 -6
- package/server/services/auth/service.json +0 -6
- package/server/services/cron/service.json +0 -6
- package/server/services/disks/drivers/local/service.json +0 -6
- package/server/services/disks/drivers/s3/service.json +0 -6
- package/server/services/disks/service.json +0 -6
- package/server/services/fetch/service.json +0 -7
- package/server/services/prisma/service.json +0 -6
- package/server/services/router/service.json +0 -6
- package/server/services/schema/router/service.json +0 -6
- package/server/services/schema/service.json +0 -6
- package/server/services/security/encrypt/aes/service.json +0 -6
package/cli/runtime/commands.ts
CHANGED
|
@@ -10,7 +10,7 @@ class InitCommand extends ProteumCommand {
|
|
|
10
10
|
public static usage = buildUsage('init');
|
|
11
11
|
|
|
12
12
|
public name = Option.String('--name', { description: 'Human-readable app name.' });
|
|
13
|
-
public description = Option.String('--description', { description: 'App description used in identity.
|
|
13
|
+
public description = Option.String('--description', { description: 'App description used in identity.config.ts and package.json.' });
|
|
14
14
|
public identifier = Option.String('--identifier', { description: 'Application class and identity identifier.' });
|
|
15
15
|
public port = Option.String('--port', { description: 'Default local router port used in .env.' });
|
|
16
16
|
public url = Option.String('--url', { description: 'Default absolute URL used in .env.' });
|
|
@@ -179,19 +179,43 @@ class CheckCommand extends ProteumCommand {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
class ConnectCommand extends ProteumCommand {
|
|
183
|
+
public static paths = [['connect']];
|
|
184
|
+
|
|
185
|
+
public static usage = buildUsage('connect');
|
|
186
|
+
|
|
187
|
+
public controllers = Option.Boolean('--controllers', false, {
|
|
188
|
+
description: 'Include imported connected controllers in the output.',
|
|
189
|
+
});
|
|
190
|
+
public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
|
|
191
|
+
public strict = Option.Boolean('--strict', false, { description: 'Exit with failure if any connect diagnostics exist.' });
|
|
192
|
+
public legacyArgs = Option.Rest();
|
|
193
|
+
|
|
194
|
+
public async execute() {
|
|
195
|
+
const args = { controllers: this.controllers, json: this.json, strict: this.strict } satisfies TArgsObject;
|
|
196
|
+
|
|
197
|
+
applyLegacyBooleanArgs('connect', this.legacyArgs, ['controllers', 'json', 'strict'], args);
|
|
198
|
+
this.setCliArgs(args);
|
|
199
|
+
await runCommandModule(() => import('../commands/connect'));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
182
203
|
class DoctorCommand extends ProteumCommand {
|
|
183
204
|
public static paths = [['doctor']];
|
|
184
205
|
|
|
185
206
|
public static usage = buildUsage('doctor');
|
|
186
207
|
|
|
208
|
+
public contracts = Option.Boolean('--contracts', false, {
|
|
209
|
+
description: 'Run contract-focused diagnostics for generated artifacts and manifest-owned source files.',
|
|
210
|
+
});
|
|
187
211
|
public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
|
|
188
212
|
public strict = Option.Boolean('--strict', false, { description: 'Exit with failure if any diagnostics exist.' });
|
|
189
213
|
public legacyArgs = Option.Rest();
|
|
190
214
|
|
|
191
215
|
public async execute() {
|
|
192
|
-
const args = { json: this.json, strict: this.strict } satisfies TArgsObject;
|
|
216
|
+
const args = { contracts: this.contracts, json: this.json, strict: this.strict } satisfies TArgsObject;
|
|
193
217
|
|
|
194
|
-
applyLegacyBooleanArgs('doctor', this.legacyArgs, ['json', 'strict'], args);
|
|
218
|
+
applyLegacyBooleanArgs('doctor', this.legacyArgs, ['contracts', 'json', 'strict'], args);
|
|
195
219
|
this.setCliArgs(args);
|
|
196
220
|
await runCommandModule(() => import('../commands/doctor'));
|
|
197
221
|
}
|
|
@@ -207,6 +231,7 @@ class ExplainCommand extends ProteumCommand {
|
|
|
207
231
|
public app = Option.Boolean('--app', false, { description: 'Include the app section.' });
|
|
208
232
|
public conventions = Option.Boolean('--conventions', false, { description: 'Include the conventions section.' });
|
|
209
233
|
public env = Option.Boolean('--env', false, { description: 'Include the env section.' });
|
|
234
|
+
public connected = Option.Boolean('--connected', false, { description: 'Include the connected-projects section.' });
|
|
210
235
|
public services = Option.Boolean('--services', false, { description: 'Include the services section.' });
|
|
211
236
|
public controllers = Option.Boolean('--controllers', false, { description: 'Include the controllers section.' });
|
|
212
237
|
public commands = Option.Boolean('--commands', false, { description: 'Include the commands section.' });
|
|
@@ -215,14 +240,25 @@ class ExplainCommand extends ProteumCommand {
|
|
|
215
240
|
public diagnostics = Option.Boolean('--diagnostics', false, {
|
|
216
241
|
description: 'Include the diagnostics section.',
|
|
217
242
|
});
|
|
218
|
-
public
|
|
243
|
+
public args = Option.Rest();
|
|
219
244
|
|
|
220
245
|
public async execute() {
|
|
246
|
+
const [mode = '', ...restArgs] = this.args;
|
|
247
|
+
if (mode === 'owner') {
|
|
248
|
+
this.setCliArgs({
|
|
249
|
+
json: this.json,
|
|
250
|
+
ownerQuery: restArgs.join(' ').trim(),
|
|
251
|
+
});
|
|
252
|
+
await runCommandModule(() => import('../commands/explain'));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
221
256
|
const args = {
|
|
222
257
|
json: this.json,
|
|
223
258
|
all: this.all,
|
|
224
259
|
app: this.app,
|
|
225
260
|
conventions: this.conventions,
|
|
261
|
+
connected: this.connected,
|
|
226
262
|
env: this.env,
|
|
227
263
|
services: this.services,
|
|
228
264
|
controllers: this.controllers,
|
|
@@ -234,8 +270,8 @@ class ExplainCommand extends ProteumCommand {
|
|
|
234
270
|
|
|
235
271
|
applyLegacyBooleanArgs(
|
|
236
272
|
'explain',
|
|
237
|
-
this.
|
|
238
|
-
['json', 'all', 'app', 'conventions', 'env', 'services', 'controllers', 'commands', 'routes', 'layouts', 'diagnostics'],
|
|
273
|
+
this.args,
|
|
274
|
+
['json', 'all', 'app', 'conventions', 'env', 'connected', 'services', 'controllers', 'commands', 'routes', 'layouts', 'diagnostics'],
|
|
239
275
|
args,
|
|
240
276
|
);
|
|
241
277
|
this.setCliArgs(args);
|
|
@@ -296,6 +332,147 @@ class CommandCommand extends ProteumCommand {
|
|
|
296
332
|
}
|
|
297
333
|
}
|
|
298
334
|
|
|
335
|
+
class SessionCommand extends ProteumCommand {
|
|
336
|
+
public static paths = [['session']];
|
|
337
|
+
|
|
338
|
+
public static usage = buildUsage('session');
|
|
339
|
+
|
|
340
|
+
public role = Option.String('--role', { description: 'Require the resolved user to have the given role.' });
|
|
341
|
+
public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
|
|
342
|
+
public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
|
|
343
|
+
public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
|
|
344
|
+
public args = Option.Rest();
|
|
345
|
+
|
|
346
|
+
public async execute() {
|
|
347
|
+
const [email = ''] = this.args;
|
|
348
|
+
|
|
349
|
+
this.setCliArgs({
|
|
350
|
+
email,
|
|
351
|
+
role: this.role ?? '',
|
|
352
|
+
port: this.port ?? '',
|
|
353
|
+
url: this.url ?? '',
|
|
354
|
+
json: this.json,
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
await runCommandModule(() => import('../commands/session'));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
class DiagnoseCommand extends ProteumCommand {
|
|
362
|
+
public static paths = [['diagnose']];
|
|
363
|
+
|
|
364
|
+
public static usage = buildUsage('diagnose');
|
|
365
|
+
|
|
366
|
+
public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
|
|
367
|
+
public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
|
|
368
|
+
public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
|
|
369
|
+
public hit = Option.String('--hit', { description: 'Issue one HTTP request before diagnosing. Defaults to the target path when it starts with /.' });
|
|
370
|
+
public method = Option.String('--method', { description: 'HTTP method used with `--hit`.' });
|
|
371
|
+
public dataJson = Option.String('--data-json', { description: 'JSON request body used with `--hit`.' });
|
|
372
|
+
public sessionEmail = Option.String('--session-email', {
|
|
373
|
+
description: 'Mint a dev session before `--hit` and attach the returned cookie.',
|
|
374
|
+
});
|
|
375
|
+
public sessionRole = Option.String('--session-role', { description: 'Require the dev session user to have this role.' });
|
|
376
|
+
public capture = Option.String('--capture', { description: 'Trace capture mode armed before `--hit`.' });
|
|
377
|
+
public logsLevel = Option.String('--logs-level', { description: 'Minimum server log level included in the diagnose response.' });
|
|
378
|
+
public logsLimit = Option.String('--logs-limit', { description: 'Maximum number of server log lines included in the diagnose response.' });
|
|
379
|
+
public args = Option.Rest();
|
|
380
|
+
|
|
381
|
+
public async execute() {
|
|
382
|
+
const [target = ''] = this.args;
|
|
383
|
+
|
|
384
|
+
this.setCliArgs({
|
|
385
|
+
capture: this.capture ?? '',
|
|
386
|
+
dataJson: this.dataJson ?? '',
|
|
387
|
+
hit: this.hit ?? '',
|
|
388
|
+
json: this.json,
|
|
389
|
+
logsLevel: this.logsLevel ?? '',
|
|
390
|
+
logsLimit: this.logsLimit ?? '',
|
|
391
|
+
method: this.method ?? '',
|
|
392
|
+
port: this.port ?? '',
|
|
393
|
+
sessionEmail: this.sessionEmail ?? '',
|
|
394
|
+
sessionRole: this.sessionRole ?? '',
|
|
395
|
+
target,
|
|
396
|
+
url: this.url ?? '',
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
await runCommandModule(() => import('../commands/diagnose'));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class PerfCommand extends ProteumCommand {
|
|
404
|
+
public static paths = [['perf']];
|
|
405
|
+
|
|
406
|
+
public static usage = buildUsage('perf');
|
|
407
|
+
|
|
408
|
+
public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
|
|
409
|
+
public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
|
|
410
|
+
public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
|
|
411
|
+
public since = Option.String('--since', { description: 'Window used by `top` and `memory`, for example `today`, `yesterday`, or `1h`.' });
|
|
412
|
+
public baseline = Option.String('--baseline', { description: 'Baseline window used by `compare`.' });
|
|
413
|
+
public target = Option.String('--target', { description: 'Target window used by `compare`.' });
|
|
414
|
+
public groupBy = Option.String('--group-by', { description: 'Aggregate by `path`, `route`, or `controller`.' });
|
|
415
|
+
public limit = Option.String('--limit', { description: 'Maximum number of rows to print.' });
|
|
416
|
+
public args = Option.Rest();
|
|
417
|
+
|
|
418
|
+
public async execute() {
|
|
419
|
+
const [action = 'top', target = ''] = this.args;
|
|
420
|
+
|
|
421
|
+
this.setCliArgs({
|
|
422
|
+
action,
|
|
423
|
+
baseline: this.baseline ?? '',
|
|
424
|
+
groupBy: this.groupBy ?? '',
|
|
425
|
+
json: this.json,
|
|
426
|
+
limit: this.limit ?? '',
|
|
427
|
+
port: this.port ?? '',
|
|
428
|
+
since: this.since ?? '',
|
|
429
|
+
target,
|
|
430
|
+
targetWindow: this.target ?? '',
|
|
431
|
+
url: this.url ?? '',
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
await runCommandModule(() => import('../commands/perf'));
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
class VerifyCommand extends ProteumCommand {
|
|
439
|
+
public static paths = [['verify']];
|
|
440
|
+
|
|
441
|
+
public static usage = buildUsage('verify');
|
|
442
|
+
|
|
443
|
+
public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
|
|
444
|
+
public crosspath = Option.String('--crosspath', { description: 'Override the CrossPath reference app path.' });
|
|
445
|
+
public product = Option.String('--product', { description: 'Override the Unique Domains Product reference app path.' });
|
|
446
|
+
public website = Option.String('--website', { description: 'Override the Unique Domains Website reference app path.' });
|
|
447
|
+
public crosspathPort = Option.String('--crosspath-port', { description: 'Port used for the CrossPath validation server.' });
|
|
448
|
+
public productPort = Option.String('--product-port', {
|
|
449
|
+
description: 'Port used for the Unique Domains Product validation server.',
|
|
450
|
+
});
|
|
451
|
+
public websitePort = Option.String('--website-port', {
|
|
452
|
+
description: 'Port used for the Unique Domains Website validation server.',
|
|
453
|
+
});
|
|
454
|
+
public route = Option.String('--route', { description: 'Route loaded in both apps during validation.' });
|
|
455
|
+
public args = Option.Rest();
|
|
456
|
+
|
|
457
|
+
public async execute() {
|
|
458
|
+
const [action = 'framework-change'] = this.args;
|
|
459
|
+
|
|
460
|
+
this.setCliArgs({
|
|
461
|
+
action,
|
|
462
|
+
crosspath: this.crosspath ?? '',
|
|
463
|
+
crosspathPort: this.crosspathPort ?? '',
|
|
464
|
+
json: this.json,
|
|
465
|
+
product: this.product ?? '',
|
|
466
|
+
productPort: this.productPort ?? '',
|
|
467
|
+
route: this.route ?? '',
|
|
468
|
+
website: this.website ?? '',
|
|
469
|
+
websitePort: this.websitePort ?? '',
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
await runCommandModule(() => import('../commands/verify'));
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
299
476
|
export const registeredCommands = {
|
|
300
477
|
init: InitCommand,
|
|
301
478
|
create: CreateCommand,
|
|
@@ -305,10 +482,15 @@ export const registeredCommands = {
|
|
|
305
482
|
typecheck: TypecheckCommand,
|
|
306
483
|
lint: LintCommand,
|
|
307
484
|
check: CheckCommand,
|
|
485
|
+
connect: ConnectCommand,
|
|
308
486
|
doctor: DoctorCommand,
|
|
309
487
|
explain: ExplainCommand,
|
|
488
|
+
diagnose: DiagnoseCommand,
|
|
489
|
+
perf: PerfCommand,
|
|
310
490
|
trace: TraceCommand,
|
|
311
491
|
command: CommandCommand,
|
|
492
|
+
session: SessionCommand,
|
|
493
|
+
verify: VerifyCommand,
|
|
312
494
|
} as const;
|
|
313
495
|
|
|
314
496
|
export const createCli = (version: string) => {
|
|
@@ -329,10 +511,15 @@ export const createCli = (version: string) => {
|
|
|
329
511
|
clipanion.register(TypecheckCommand);
|
|
330
512
|
clipanion.register(LintCommand);
|
|
331
513
|
clipanion.register(CheckCommand);
|
|
514
|
+
clipanion.register(ConnectCommand);
|
|
332
515
|
clipanion.register(DoctorCommand);
|
|
333
516
|
clipanion.register(ExplainCommand);
|
|
517
|
+
clipanion.register(DiagnoseCommand);
|
|
518
|
+
clipanion.register(PerfCommand);
|
|
334
519
|
clipanion.register(TraceCommand);
|
|
335
520
|
clipanion.register(CommandCommand);
|
|
521
|
+
clipanion.register(SessionCommand);
|
|
522
|
+
clipanion.register(VerifyCommand);
|
|
336
523
|
|
|
337
524
|
return clipanion;
|
|
338
525
|
};
|
package/cli/scaffold/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import slugify from 'slugify';
|
|
4
|
-
import yaml from 'yaml';
|
|
5
4
|
import { UsageError } from 'clipanion';
|
|
6
5
|
|
|
7
6
|
import cli from '..';
|
|
7
|
+
import { loadApplicationIdentityConfig } from '../../common/applicationConfigLoader';
|
|
8
8
|
import { ensureProjectAgentSymlinks } from '../utils/agents';
|
|
9
9
|
import { runProcess } from '../utils/runProcess';
|
|
10
10
|
import {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
createInitSummary,
|
|
19
19
|
createPackageJsonTemplate,
|
|
20
20
|
createPageTemplate,
|
|
21
|
+
createProteumConfigTemplate,
|
|
21
22
|
createRouteTemplate,
|
|
22
23
|
createRouterConfigTemplate,
|
|
23
24
|
createServerIndexTemplate,
|
|
@@ -133,16 +134,16 @@ const defaultRouteFromSegments = (segments: string[]) => {
|
|
|
133
134
|
const resolveRootServiceLeaf = (segments: string[]) => segments[segments.length - 1];
|
|
134
135
|
|
|
135
136
|
const readIdentityConfig = (appRoot: string): TIdentityConfig => {
|
|
136
|
-
const identityFilepath = path.join(appRoot, 'identity.
|
|
137
|
+
const identityFilepath = path.join(appRoot, 'identity.config.ts');
|
|
137
138
|
if (!fs.existsSync(identityFilepath)) {
|
|
138
|
-
throw new UsageError(`Missing identity.
|
|
139
|
+
throw new UsageError(`Missing identity.config.ts in ${appRoot}. Run \`proteum init\` first or target a Proteum app root.`);
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
const parsed =
|
|
142
|
-
const identifier = typeof parsed
|
|
143
|
-
const name = typeof parsed
|
|
142
|
+
const parsed = loadApplicationIdentityConfig(appRoot);
|
|
143
|
+
const identifier = typeof parsed.identifier === 'string' ? parsed.identifier.trim() : '';
|
|
144
|
+
const name = typeof parsed.name === 'string' ? parsed.name.trim() : '';
|
|
144
145
|
|
|
145
|
-
if (!identifier) throw new UsageError(`identity.
|
|
146
|
+
if (!identifier) throw new UsageError(`identity.config.ts in ${appRoot} is missing a valid "identifier" field.`);
|
|
146
147
|
|
|
147
148
|
return {
|
|
148
149
|
identifier,
|
|
@@ -151,7 +152,7 @@ const readIdentityConfig = (appRoot: string): TIdentityConfig => {
|
|
|
151
152
|
};
|
|
152
153
|
|
|
153
154
|
const assertProteumAppRoot = (appRoot: string) => {
|
|
154
|
-
const expectedEntries = ['package.json', 'identity.
|
|
155
|
+
const expectedEntries = ['package.json', 'identity.config.ts', 'proteum.config.ts', 'client', 'server'];
|
|
155
156
|
const missing = expectedEntries.filter((entry) => !fs.existsSync(path.join(appRoot, entry)));
|
|
156
157
|
if (missing.length > 0) {
|
|
157
158
|
throw new UsageError(
|
|
@@ -459,21 +460,9 @@ const createServicePlan = ({
|
|
|
459
460
|
const configExportName = `${configFileBase}Config`;
|
|
460
461
|
const relativeServiceDir = path.join('server', 'services', ...segments);
|
|
461
462
|
const relativeServiceFilepath = path.join(relativeServiceDir, 'index.ts');
|
|
462
|
-
const relativeServiceConfigFilepath = path.join(relativeServiceDir, 'service.json');
|
|
463
463
|
const relativeConfigFilepath = path.join('server', 'config', `${configFileBase}.ts`);
|
|
464
464
|
const propertyName = serviceImportName;
|
|
465
465
|
|
|
466
|
-
const serviceJson = JSON.stringify(
|
|
467
|
-
{
|
|
468
|
-
id: `${appIdentifier}/${serviceImportName}`,
|
|
469
|
-
name: `${appIdentifier}${serviceImportName}`,
|
|
470
|
-
parent: 'app',
|
|
471
|
-
dependences: [],
|
|
472
|
-
},
|
|
473
|
-
null,
|
|
474
|
-
4,
|
|
475
|
-
) + '\n';
|
|
476
|
-
|
|
477
466
|
return {
|
|
478
467
|
files: [
|
|
479
468
|
{
|
|
@@ -483,10 +472,6 @@ const createServicePlan = ({
|
|
|
483
472
|
className,
|
|
484
473
|
}),
|
|
485
474
|
},
|
|
486
|
-
{
|
|
487
|
-
relativePath: relativeServiceConfigFilepath,
|
|
488
|
-
content: serviceJson,
|
|
489
|
-
},
|
|
490
475
|
{
|
|
491
476
|
relativePath: relativeConfigFilepath,
|
|
492
477
|
content: createServiceConfigTemplate({
|
|
@@ -628,13 +613,17 @@ const createInitFilePlans = (config: TScaffoldInitConfig): TScaffoldFilePlan[] =
|
|
|
628
613
|
}),
|
|
629
614
|
},
|
|
630
615
|
{
|
|
631
|
-
relativePath: 'identity.
|
|
616
|
+
relativePath: 'identity.config.ts',
|
|
632
617
|
content: createIdentityTemplate({
|
|
633
618
|
appName: config.name,
|
|
634
619
|
appIdentifier: config.identifier,
|
|
635
620
|
appDescription: config.description,
|
|
636
621
|
}),
|
|
637
622
|
},
|
|
623
|
+
{
|
|
624
|
+
relativePath: 'proteum.config.ts',
|
|
625
|
+
content: createProteumConfigTemplate(),
|
|
626
|
+
},
|
|
638
627
|
{
|
|
639
628
|
relativePath: '.env',
|
|
640
629
|
content: createEnvTemplate({
|
|
@@ -227,6 +227,8 @@ export const createServerTsconfigTemplate = () => `{
|
|
|
227
227
|
},
|
|
228
228
|
"include": [
|
|
229
229
|
".",
|
|
230
|
+
"../identity.config.ts",
|
|
231
|
+
"../proteum.config.ts",
|
|
230
232
|
"../var/typings",
|
|
231
233
|
"../node_modules/proteum/types/global",
|
|
232
234
|
"../.proteum/server/services.d.ts",
|
|
@@ -236,11 +238,12 @@ export const createServerTsconfigTemplate = () => `{
|
|
|
236
238
|
`;
|
|
237
239
|
|
|
238
240
|
export const createGitignoreTemplate = () => `node_modules
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
bin
|
|
242
|
-
dev
|
|
243
|
-
var
|
|
241
|
+
/.proteum
|
|
242
|
+
/.cache
|
|
243
|
+
/bin
|
|
244
|
+
/dev
|
|
245
|
+
/var
|
|
246
|
+
/proteum.connected.json
|
|
244
247
|
.env
|
|
245
248
|
`;
|
|
246
249
|
|
|
@@ -248,6 +251,7 @@ export const createEnvTemplate = ({ port, url }: { port: number; url: string })
|
|
|
248
251
|
ENV_PROFILE=dev
|
|
249
252
|
PORT=${port}
|
|
250
253
|
URL=${url}
|
|
254
|
+
URL_INTERNAL=${url}
|
|
251
255
|
|
|
252
256
|
# Optional trace settings
|
|
253
257
|
# TRACE_ENABLE=true
|
|
@@ -306,28 +310,38 @@ export const createIdentityTemplate = ({
|
|
|
306
310
|
appName: string;
|
|
307
311
|
appIdentifier: string;
|
|
308
312
|
appDescription: string;
|
|
309
|
-
}) => `
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
web:
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
313
|
+
}) => `import { Application } from 'proteum/config';
|
|
314
|
+
|
|
315
|
+
export default Application.identity({
|
|
316
|
+
name: ${JSON.stringify(appName)},
|
|
317
|
+
identifier: ${JSON.stringify(appIdentifier)},
|
|
318
|
+
description: ${JSON.stringify(appDescription)},
|
|
319
|
+
author: {
|
|
320
|
+
name: ${JSON.stringify(appName)},
|
|
321
|
+
url: 'localhost',
|
|
322
|
+
email: 'team@example.com',
|
|
323
|
+
},
|
|
324
|
+
social: {},
|
|
325
|
+
language: 'en',
|
|
326
|
+
locale: 'en-US',
|
|
327
|
+
maincolor: 'white',
|
|
328
|
+
iconsPack: 'light',
|
|
329
|
+
web: {
|
|
330
|
+
title: ${JSON.stringify(appName)},
|
|
331
|
+
titleSuffix: ${JSON.stringify(appName)},
|
|
332
|
+
fullTitle: ${JSON.stringify(appName)},
|
|
333
|
+
description: ${JSON.stringify(appDescription)},
|
|
334
|
+
version: '0.0.1',
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
`;
|
|
338
|
+
|
|
339
|
+
export const createProteumConfigTemplate = () => `import { Application } from 'proteum/config';
|
|
340
|
+
|
|
341
|
+
export default Application.setup({
|
|
342
|
+
transpile: [],
|
|
343
|
+
connect: {},
|
|
344
|
+
});
|
|
331
345
|
`;
|
|
332
346
|
|
|
333
347
|
export const createInitSummary = (result: TScaffoldResult, config: TScaffoldInitConfig) => ({
|
package/cli/utils/agents.ts
CHANGED
|
@@ -19,10 +19,12 @@ type TAgentLinkDefinition = { projectPath: string; sourcePath: string; ensurePar
|
|
|
19
19
|
- CONSTANTS
|
|
20
20
|
----------------------------------*/
|
|
21
21
|
|
|
22
|
-
// Project-local
|
|
22
|
+
// Project-local instruction entrypoints mapped to their canonical shipped source files.
|
|
23
23
|
const projectAgentLinkDefinitions: TAgentLinkDefinition[] = [
|
|
24
24
|
{ projectPath: 'AGENTS.md', sourcePath: 'AGENTS.md' },
|
|
25
25
|
{ projectPath: 'CODING_STYLE.md', sourcePath: 'CODING_STYLE.md' },
|
|
26
|
+
{ projectPath: 'diagnostics.md', sourcePath: 'diagnostics.md' },
|
|
27
|
+
{ projectPath: 'optimizations.md', sourcePath: 'optimizations.md' },
|
|
26
28
|
{ projectPath: path.join('client', 'AGENTS.md'), sourcePath: path.join('client', 'AGENTS.md') },
|
|
27
29
|
{ projectPath: path.join('client', 'pages', 'AGENTS.md'), sourcePath: path.join('client', 'pages', 'AGENTS.md') },
|
|
28
30
|
{
|
|
@@ -81,7 +83,7 @@ function ensureSymlinks(appRoot: string, linkDefinitions: TAgentLinkDefinition[]
|
|
|
81
83
|
|
|
82
84
|
const sourceFilepath = linkDefinition.sourcePath;
|
|
83
85
|
if (!fs.existsSync(sourceFilepath)) {
|
|
84
|
-
throw new Error(`Missing
|
|
86
|
+
throw new Error(`Missing project instruction asset: ${sourceFilepath}`);
|
|
85
87
|
}
|
|
86
88
|
|
|
87
89
|
const symlinkTarget = path.relative(projectParentDir, sourceFilepath);
|
package/cli/utils/keyboard.ts
CHANGED
|
@@ -23,7 +23,15 @@ class KeyboardCommands {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
private listen() {
|
|
26
|
+
if (!process.stdin) return;
|
|
27
|
+
|
|
26
28
|
readline.emitKeypressEvents(process.stdin);
|
|
29
|
+
|
|
30
|
+
if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== 'function') {
|
|
31
|
+
logVerbose('Keyboard shortcuts disabled because stdin is not an interactive TTY.');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
process.stdin.setRawMode(true);
|
|
28
36
|
process.stdin.on('keypress', async (chunk: string, key: Key) => {
|
|
29
37
|
let str = key.name;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ApexOptions } from 'apexcharts';
|
|
3
|
+
|
|
4
|
+
const readErrorMessage = (error: unknown) => (error instanceof Error ? error.message : String(error));
|
|
5
|
+
|
|
6
|
+
export default function ApexChart({
|
|
7
|
+
emptyLabel = 'No chart data available.',
|
|
8
|
+
options,
|
|
9
|
+
}: {
|
|
10
|
+
emptyLabel?: string;
|
|
11
|
+
options?: ApexOptions;
|
|
12
|
+
}) {
|
|
13
|
+
const mountRef = React.useRef<HTMLDivElement | null>(null);
|
|
14
|
+
const [errorMessage, setErrorMessage] = React.useState<string>();
|
|
15
|
+
|
|
16
|
+
React.useEffect(() => {
|
|
17
|
+
const target = mountRef.current;
|
|
18
|
+
if (!target || !options) return;
|
|
19
|
+
|
|
20
|
+
let disposed = false;
|
|
21
|
+
let chart: { destroy: () => void } | undefined;
|
|
22
|
+
|
|
23
|
+
target.innerHTML = '';
|
|
24
|
+
setErrorMessage(undefined);
|
|
25
|
+
|
|
26
|
+
void (async () => {
|
|
27
|
+
try {
|
|
28
|
+
const module = await import('apexcharts');
|
|
29
|
+
if (disposed || !mountRef.current) return;
|
|
30
|
+
|
|
31
|
+
const ApexCharts = module.default;
|
|
32
|
+
chart = new ApexCharts(mountRef.current, options);
|
|
33
|
+
await chart.render();
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (!disposed) setErrorMessage(readErrorMessage(error));
|
|
36
|
+
}
|
|
37
|
+
})();
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
disposed = true;
|
|
41
|
+
chart?.destroy();
|
|
42
|
+
target.innerHTML = '';
|
|
43
|
+
};
|
|
44
|
+
}, [options]);
|
|
45
|
+
|
|
46
|
+
if (!options) return <div className="proteum-profiler__empty">{emptyLabel}</div>;
|
|
47
|
+
|
|
48
|
+
if (errorMessage) {
|
|
49
|
+
return (
|
|
50
|
+
<div className="proteum-profiler__chartMount">
|
|
51
|
+
<div className="proteum-profiler__row">
|
|
52
|
+
<div className="proteum-profiler__rowHeader">
|
|
53
|
+
<strong>Chart error</strong>
|
|
54
|
+
</div>
|
|
55
|
+
<div className="proteum-profiler__mono">{errorMessage}</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className="proteum-profiler__chartMount">
|
|
63
|
+
<div ref={mountRef} />
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|