proteum 2.1.3-1 → 2.1.7

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 +22 -14
  2. package/README.md +109 -17
  3. package/agents/project/AGENTS.md +188 -25
  4. package/agents/project/CODING_STYLE.md +1 -0
  5. package/agents/project/client/AGENTS.md +13 -8
  6. package/agents/project/client/pages/AGENTS.md +17 -9
  7. package/agents/project/diagnostics.md +52 -0
  8. package/agents/project/optimizations.md +48 -0
  9. package/agents/project/server/routes/AGENTS.md +9 -6
  10. package/agents/project/server/services/AGENTS.md +10 -6
  11. package/agents/project/tests/AGENTS.md +11 -5
  12. package/cli/app/config.ts +13 -14
  13. package/cli/app/index.ts +58 -0
  14. package/cli/commands/connect.ts +45 -0
  15. package/cli/commands/dev.ts +37 -13
  16. package/cli/commands/diagnose.ts +286 -0
  17. package/cli/commands/doctor.ts +18 -5
  18. package/cli/commands/explain.ts +25 -0
  19. package/cli/commands/perf.ts +243 -0
  20. package/cli/commands/trace.ts +9 -1
  21. package/cli/commands/verify.ts +281 -0
  22. package/cli/compiler/artifacts/connectedProjects.ts +453 -0
  23. package/cli/compiler/artifacts/controllers.ts +198 -49
  24. package/cli/compiler/artifacts/discovery.ts +0 -34
  25. package/cli/compiler/artifacts/manifest.ts +95 -6
  26. package/cli/compiler/artifacts/routing.ts +2 -2
  27. package/cli/compiler/artifacts/services.ts +277 -130
  28. package/cli/compiler/client/index.ts +3 -0
  29. package/cli/compiler/common/files/style.ts +52 -0
  30. package/cli/compiler/common/generatedRouteModules.ts +34 -5
  31. package/cli/compiler/common/scripts.ts +11 -5
  32. package/cli/compiler/index.ts +2 -1
  33. package/cli/compiler/server/index.ts +3 -0
  34. package/cli/presentation/commands.ts +110 -7
  35. package/cli/presentation/devSession.ts +32 -7
  36. package/cli/runtime/commands.ts +165 -6
  37. package/cli/scaffold/index.ts +18 -27
  38. package/cli/scaffold/templates.ts +48 -28
  39. package/cli/utils/agents.ts +106 -13
  40. package/cli/utils/keyboard.ts +8 -0
  41. package/client/dev/profiler/ApexChart.tsx +66 -0
  42. package/client/dev/profiler/index.tsx +2508 -302
  43. package/client/dev/profiler/runtime.noop.ts +12 -0
  44. package/client/dev/profiler/runtime.ts +195 -4
  45. package/client/services/router/request/api.ts +6 -1
  46. package/common/applicationConfig.ts +173 -0
  47. package/common/applicationConfigLoader.ts +102 -0
  48. package/common/connectedProjects.ts +113 -0
  49. package/common/dev/connect.ts +267 -0
  50. package/common/dev/console.ts +31 -0
  51. package/common/dev/contractsDoctor.ts +128 -0
  52. package/common/dev/diagnostics.ts +59 -15
  53. package/common/dev/inspection.ts +491 -0
  54. package/common/dev/performance.ts +809 -0
  55. package/common/dev/profiler.ts +3 -0
  56. package/common/dev/proteumManifest.ts +31 -6
  57. package/common/dev/requestTrace.ts +52 -1
  58. package/common/env/proteumEnv.ts +176 -50
  59. package/common/router/index.ts +1 -0
  60. package/common/router/request/api.ts +2 -0
  61. package/config.ts +5 -0
  62. package/docs/dev-commands.md +5 -1
  63. package/docs/dev-sessions.md +90 -0
  64. package/docs/diagnostics.md +74 -11
  65. package/docs/request-tracing.md +50 -3
  66. package/package.json +1 -1
  67. package/server/app/container/config.ts +16 -87
  68. package/server/app/container/console/index.ts +42 -8
  69. package/server/app/container/index.ts +10 -2
  70. package/server/app/container/trace/index.ts +105 -0
  71. package/server/app/devDiagnostics.ts +138 -0
  72. package/server/app/index.ts +18 -8
  73. package/server/app/service/container.ts +0 -12
  74. package/server/app/service/index.ts +0 -2
  75. package/server/services/prisma/index.ts +121 -4
  76. package/server/services/router/http/index.ts +305 -11
  77. package/server/services/router/index.ts +116 -57
  78. package/server/services/router/request/api.ts +160 -19
  79. package/server/services/router/request/index.ts +8 -0
  80. package/server/services/router/response/index.ts +23 -1
  81. package/server/services/router/response/page/document.tsx +31 -14
  82. package/server/services/router/response/page/index.tsx +10 -0
  83. package/agents/framework/AGENTS.md +0 -177
  84. package/server/services/auth/router/service.json +0 -6
  85. package/server/services/auth/service.json +0 -6
  86. package/server/services/cron/service.json +0 -6
  87. package/server/services/disks/drivers/local/service.json +0 -6
  88. package/server/services/disks/drivers/s3/service.json +0 -6
  89. package/server/services/disks/service.json +0 -6
  90. package/server/services/fetch/service.json +0 -7
  91. package/server/services/prisma/service.json +0 -6
  92. package/server/services/router/service.json +0 -6
  93. package/server/services/schema/router/service.json +0 -6
  94. package/server/services/schema/service.json +0 -6
  95. package/server/services/security/encrypt/aes/service.json +0 -6
@@ -1,12 +1,19 @@
1
- # Page Files
1
+ # Page Contract
2
2
 
3
- This file adds page-file local rules on top of the canonical framework contract:
3
+ This is the canonical page-file contract for Proteum-based projects.
4
+ Role: keep only page-file rules here.
5
+ Keep here: `Router.page(...)` registration, SSR `setup` and `render` contracts, page payload shape, and page-local typing rules.
6
+ Do not put here: generic component rules, server/service implementation details, or app-wide workflow already covered by broader AGENTS files.
4
7
 
5
- - framework repo: `agents/framework/AGENTS.md`
6
- - installed app: `./node_modules/proteum/agents/framework/AGENTS.md`
8
+ Optimization source of truth: project-root `optimizations.md`.
9
+ Diagnostics source of truth: project-root `diagnostics.md`.
10
+ Coding style source of truth: project-root `CODING_STYLE.md`.
7
11
 
8
12
  ## Router.page Usage
9
13
 
14
+ - Proteum scans page files for top-level `Router.page(...)` and `Router.error(...)` calls.
15
+ - File path controls chunk identity and layout discovery; route path comes from the explicit `Router.page(...)` string.
16
+ - Supported page signatures are `Router.page(path, render)`, `Router.page(path, setup, render)`, `Router.page(path, options, render)`, and `Router.page(path, options, setup, render)`.
10
17
  - Prefer `Router.page(path, setup, render)` for normal SSR pages.
11
18
  - Use `Router.page(path, options, setup, render)` when a separate route-options object makes the call clearer.
12
19
  - Keep the `Router.page(...)` call compact instead of exploding each outer argument onto its own line.
@@ -15,14 +22,15 @@ This file adds page-file local rules on top of the canonical framework contract:
15
22
  ## Setup And Render
16
23
 
17
24
  - `setup` returns one flat object.
18
- - `_`-prefixed keys are route options.
19
- - every other key is SSR data and should be consumed from `render`
20
- - if a page needs route data, return it from `setup` and read it in `render`
25
+ - `_`-prefixed keys like `_auth`, `_layout`, `_static`, and `_redirectLogged` are route options.
26
+ - Every other key is SSR data and should be consumed from `render`.
27
+ - Controller fetchers and promises returned from `setup` resolve before render.
28
+ - If a page needs route data, return it from `setup` and read it in `render`.
21
29
 
22
30
  ## Page Rules
23
31
 
24
- - Prefer generated page args or the app context hook. Do not import `.proteum` files directly.
25
- - Never use `api.fetch(...)` in page files.
32
+ - Prefer generated page args or the app client context. Do not import `.proteum` implementation files directly.
33
+ - Never use `api.fetch(...)` in page files for SSR loading.
26
34
  - Never import client service values from `@app`.
27
35
  - Keep page-local curated copy, option sets, and registries in `/client/catalogs/**`.
28
36
  - When shared Shadcn-based primitives exist, compose the page UI from them instead of redefining common controls inline.
@@ -0,0 +1,52 @@
1
+ # Diagnostics Rules
2
+
3
+ This file is the canonical source of truth for diagnostics, temporary instrumentation, error solving, and verification method selection across Proteum-based projects.
4
+
5
+ ## Initial Triage
6
+
7
+ - Start with machine-readable app state before reading large parts of the codebase: `./.proteum/manifest.json`, `npx proteum connect --json`, `npx proteum explain --json`, `npx proteum doctor --json`, and `npx proteum doctor --contracts --json` when generated artifacts or manifest-owned files may be stale.
8
+ - When one app depends on another app's generated controllers, inspect `npx proteum connect --controllers`, `npx proteum explain --connected --controllers`, the producer `proteum.connected.json`, the consumer `proteum.config.ts` connected `source` value, and the producer `./.proteum/proteum.connected.d.ts` before assuming the contract is local.
9
+ - Use `rg -n` first to narrow the exact code path, then read only the relevant files.
10
+ - Inspect `./server/index.ts`, `./server/config/*.ts`, and the touched files under `./commands`, `./server/controllers`, `./server/services`, `./server/routes`, `./client/pages`, and `./tests`.
11
+ - Distinguish real app failures from wrapper, transport, tooling, or environment failures as early as possible.
12
+
13
+ ## Runtime Diagnostics
14
+
15
+ - For request-time issues in dev, start with `npx proteum diagnose <path> --port <port>` when you have a concrete failing route, page, controller path, or request target. It combines owner lookup, manifest diagnostics, contract diagnostics, matching trace data, and buffered server logs in one pass.
16
+ - For connected-project failures, confirm the consumer app resolves the expected `connect.<Namespace>.source` and `connect.<Namespace>.urlInternal` values, the producer app exposes `GET /api/__proteum/connected/ping`, and the imported controller entries show `scope=connected` in `proteum explain`.
17
+ - Use `npx proteum explain owner <query>` when you need a fast ownership graph for a route, controller path, source file, or generated artifact before reading code.
18
+ - For performance issues or regressions in dev, use `npx proteum perf top --since <window>` to rank hot paths, `npx proteum perf request <requestId|path>` for one request waterfall, `npx proteum perf compare --baseline <window> --target <window>` for regressions, and `npx proteum perf memory --since <window>` for heap or RSS drift.
19
+ - For request-time issues in dev, inspect traces before adding logs when the diagnose surface is still too coarse.
20
+ - If a server is already running on the default port from `PORT` or `./.proteum/manifest.json`, inspect existing traces before reproducing the issue.
21
+ - If existing traces are insufficient, arm `npx proteum trace arm --capture deep`, reproduce once, then inspect the new request with `npx proteum trace latest` or `npx proteum trace show <requestId>`.
22
+ - Inspect browser console errors and warnings for frontend, SSR, hydration, and controller-call issues.
23
+ - Inspect server startup and runtime errors.
24
+ - For protected browser or API flows in dev, prefer `npx proteum session <email> --role <role>` over driving the login UI. Use the login UI only when auth UX itself is under test.
25
+
26
+ ## Temporary Instrumentation
27
+
28
+ - When manifest inspection, trace data, browser console output, and server errors are still insufficient, add temporary targeted logs in the code to confirm control flow, payload shape, query shape, or branch selection.
29
+ - Keep temporary logs narrow, contextual, and easy to remove. Do not leave broad debug noise in shared execution paths.
30
+ - Re-run only the smallest relevant repro, request, or test after adding temporary instrumentation.
31
+ - Temporary logs added in the code for diagnosis must be cleaned at the end of tests or the repro cycle and must never be committed.
32
+
33
+ ## Error Solving
34
+
35
+ - Fix the contract boundary, not only the downstream symptom.
36
+ - Prefer explicit typed schemas, adapters, query `select`s, and narrow response shapes over casts, broad payloads, or hidden fallbacks.
37
+ - Keep patches narrow, then verify immediately at the failing layer before broadening the test surface.
38
+ - Review the resulting diff to confirm the fix removed the cause instead of masking it.
39
+
40
+ ## Verification And Testing
41
+
42
+ - Use the cheapest trustworthy verification that matches the failing layer.
43
+ - After implementing a feature or behavior change, always verify it on a running app instead of stopping at static analysis or code review.
44
+ - For compile-time or type-safety issues, start with the relevant typecheck or build command.
45
+ - For request/runtime issues, verify through the real page, route, generated controller call, or command on a running app.
46
+ - Start the smallest trustworthy runtime surface first: boot the server, run the relevant real URL, generated controller call, command, or `npx proteum diagnose <path> --port <port>`, then add targeted Playwright coverage when the change is browser-visible.
47
+ - For browser regressions, prefer targeted Playwright coverage and inspect failure artifacts such as screenshots, videos, `error-context.md`, and Playwright traces.
48
+ - Treat server startup failures, runtime errors, browser console errors or warnings, and Playwright failures as blocking unless they are clearly unrelated to the change.
49
+ - When the touched surface can affect coding-style enforcement, run the smallest relevant project check such as `npx proteum lint` or `npx proteum check` before finishing.
50
+ - Add `data-testid` when stable selectors are missing instead of relying on brittle text or DOM-shape selectors.
51
+ - If an isolated test misses prerequisite state, run the smallest broader scope that reproduces the real setup.
52
+ - After a fix, re-check traces, rendered HTML, browser console, and server output when those surfaces were part of the original failure.
@@ -0,0 +1,48 @@
1
+ # Optimization Rules
2
+
3
+ This file is the canonical source of truth for bundle size, performance, SEO, and SSR page-size guidance across Proteum-based projects.
4
+
5
+ ## Priority Order
6
+
7
+ When tradeoffs exist inside optimization work, optimize in this order:
8
+
9
+ 1. Reduce shipped client bundle size and unnecessary runtime code.
10
+ 2. Improve build-time, server-time, and browser-time performance.
11
+ 3. Improve SEO output and crawlable, semantic HTML.
12
+
13
+ ## Bundle Size And Runtime Cost
14
+
15
+ - Reduce shipped client bundle size and unnecessary runtime code.
16
+ - Before inventing a new helper, runtime, plugin, abstraction, primitive, parser, formatter, SDK wrapper, or build-time tool, first check whether the repo already depends on a suitable package.
17
+ - If the repo does not already depend on one, search npm before writing a custom implementation.
18
+ - Prefer established, flexible, well-typed, widely adopted, actively maintained packages.
19
+ - Build custom or keep custom infrastructure only when packages would clearly hurt bundle size, SSR behavior, performance, typing quality, flexibility, licensing, explicit contracts, or long-term maintainability.
20
+ - If you choose custom over a package, state briefly why.
21
+
22
+ ## SSR And Page Size
23
+
24
+ - SSR page data belongs in page `setup`, not in `api.fetch(...)`.
25
+ - Prefer `Router.page(path, setup, render)` for normal SSR pages.
26
+ - `setup` returns one flat object.
27
+ - `_`-prefixed keys are route options. Every other key is SSR data and should be consumed from `render`.
28
+ - If a page needs route data, return it from `setup` and read it in `render`.
29
+ - Controller fetchers and promises returned from `setup` resolve before render.
30
+ - Never use `api.fetch(...)` in page files for SSR loading.
31
+ - Synchronous or SSR data calls must return only the strictly necessary data for the current render path to minimize SSR payload size.
32
+ - If an existing controller or data method returns a broader shape than the SSR path needs, create a dedicated proxy controller method with a narrower typed contract instead of reusing the oversized payload.
33
+ - Keep Prisma runtime access inside services when possible and prefer explicit `select` or narrow `include` in database queries.
34
+
35
+ ## SEO And Crawlable Output
36
+
37
+ - Improve SEO output and crawlable, semantic HTML.
38
+ - For explicit crawl surfaces such as redirects, sitemap or RSS output, and public resources with custom semantics, prefer `server/routes/**` over generated controller actions when the endpoint is not a normal app API.
39
+
40
+ ## Validation
41
+
42
+ - Do not stop at static analysis for SSR, routing, emitted assets, or rendered HTML.
43
+ - After implementing a feature or change, verify that performance, load size, and SEO output did not materially regress before finishing.
44
+ - When runtime cost, hot paths, or memory can change, use the relevant `npx proteum perf ...` command against the affected request or route and compare to the pre-change behavior when possible.
45
+ - For browser or SSR changes, load the real page, inspect the rendered HTML, and confirm the change does not ship unnecessary client code or oversized SSR payloads.
46
+ - Treat clearly worse bundle size, runtime cost, or crawlable HTML quality as regressions to fix or justify explicitly, not as optional follow-up cleanup.
47
+ - Build-only checks are supplementary.
48
+ - For SSR changes, load the real page and inspect the rendered HTML plus browser console.
@@ -1,12 +1,15 @@
1
- # Server Routes
1
+ # Route Contract
2
2
 
3
- This file adds route-area local rules on top of the canonical framework contract:
3
+ This is the canonical route-area contract for Proteum-based projects.
4
+ Role: keep only manual-route rules here.
5
+ Keep here: explicit HTTP route guidance, public/crawlable endpoint rules, absolute URL generation, and route-specific catalog placement.
6
+ Do not put here: controller contracts, service-layer business logic, page SSR rules, or broad project workflow already defined elsewhere.
4
7
 
5
- - framework repo: `agents/framework/AGENTS.md`
6
- - installed app: `./node_modules/proteum/agents/framework/AGENTS.md`
8
+ Optimization source of truth: project-root `optimizations.md`.
9
+ Diagnostics source of truth: project-root `diagnostics.md`.
7
10
 
8
- - Use `/server/routes/**` only for explicit custom HTTP behavior that should not be generated from controllers.
9
- - If the endpoint is just a normal app API, prefer `/server/controllers/**/*.ts`.
11
+ - Use `server/routes/**` only for explicit HTTP behavior that should not be generated from controllers.
12
+ - If the endpoint is a normal app API, prefer `server/controllers/**/*.ts`.
10
13
  - Good fits include redirects, resources, OAuth callbacks, webhooks, sitemap-like output, and custom public endpoints.
11
14
  - If a route needs a curated registry, keep server-only data in `/server/catalogs/**` and shared data in `/common/catalogs/**`.
12
15
 
@@ -1,20 +1,24 @@
1
- # Server Services
1
+ # Service Contract
2
2
 
3
- This file adds service-area local rules on top of the canonical framework contract:
3
+ This is the canonical service-area contract for Proteum-based projects.
4
+ Role: keep only service-layer rules here.
5
+ Keep here: service placement, service responsibilities, model access, query-shaping, return-type guidance, and service error-handling rules.
6
+ Do not put here: request parsing, page/render rules, controller transport details, or broad project workflow already defined in higher-level AGENTS files.
4
7
 
5
- - framework repo: `agents/framework/AGENTS.md`
6
- - installed app: `./node_modules/proteum/agents/framework/AGENTS.md`
8
+ Optimization source of truth: project-root `optimizations.md`.
9
+ Diagnostics source of truth: project-root `diagnostics.md`.
7
10
 
8
11
  ## Placement
9
12
 
10
13
  - Root business services live in `/server/services/<Feature>/index.ts`.
11
- - Root-service metadata lives in `/server/services/<Feature>/service.json`.
12
14
  - Root-service config lives in `/server/config/*.ts` when the service needs config.
13
15
  - Companion client-callable entrypoints live in `/server/controllers/**`.
14
16
 
15
- ## Local Service Rules
17
+ ## Service Rules
16
18
 
19
+ - Business logic belongs in classes that extend `Service` and use `this.services`, `this.models`, and `this.app`.
17
20
  - Keep business logic in services and keep request/auth/input handling in controllers.
21
+ - Normal service methods should not read request-scoped state directly.
18
22
  - If a feature grows several coherent domains, split it into explicit subservices.
19
23
  - Server-only catalogs live in `/server/catalogs/**`.
20
24
  - Shared cross-runtime catalogs live in `/common/catalogs/**`.
@@ -1,12 +1,18 @@
1
- # E2E Tests
1
+ # Test Contract
2
2
 
3
- This file adds test-area local rules on top of the canonical framework contract:
3
+ This is the canonical test-area contract for Proteum-based projects.
4
+ Role: keep only test-area rules here.
5
+ Keep here: runtime verification guidance, selector strategy, test-specific reuse rules, and authenticated test-flow expectations.
6
+ Do not put here: production implementation rules, page/service architecture contracts, or duplicated project workflow that belongs in broader AGENTS files.
4
7
 
5
- - framework repo: `agents/framework/AGENTS.md`
6
- - installed app: `./node_modules/proteum/agents/framework/AGENTS.md`
8
+ Diagnostics source of truth: project-root `diagnostics.md`.
7
9
 
8
10
  - Understand the real user flow and the main feature branches before writing tests.
9
- - Test the current controller/page runtime model, not legacy `@Route` or `api.fetch` behavior.
11
+ - Test the current controller/page runtime model, not legacy `@Route` or `api.fetch(...)` behavior.
12
+ - Verify routing, controllers, SSR, and router plugins against a running app when behavior depends on real request handling.
13
+ - After implementing a browser-visible feature or change, run targeted Playwright coverage or a real browser repro against a running app before finishing; this verification is required, not optional cleanup.
14
+ - Exercise real URLs, generated controller calls, or real browser flows instead of re-deriving framework internals in tests.
10
15
  - Locate elements with `data-testid`.
11
16
  - Add `data-testid` where needed instead of relying on brittle selectors.
12
17
  - Reuse root catalog files from `/client/catalogs/**`, `/server/catalogs/**`, or `/common/catalogs/**` instead of duplicating catalog constants in tests.
18
+ - For protected dev flows, prefer `npx proteum session <email> --role <role>` over automating login unless the login flow itself is under test.
package/cli/app/config.ts CHANGED
@@ -2,12 +2,8 @@
2
2
  - DEPENDANCES
3
3
  ----------------------------------*/
4
4
 
5
- // Npm
6
- import fs from 'fs-extra';
7
- import yaml from 'yaml';
8
-
9
- // Types
10
5
  import { parseProteumEnvConfig, type TProteumLoadedEnvConfig } from '../../common/env/proteumEnv';
6
+ import { loadApplicationIdentityConfig, loadApplicationSetupConfig } from '../../common/applicationConfigLoader';
11
7
  import { logVerbose } from '../runtime/verbose';
12
8
 
13
9
  /*----------------------------------
@@ -20,25 +16,28 @@ export default class ConfigParser {
20
16
  public routerPortOverride?: number,
21
17
  ) {}
22
18
 
23
- private loadYaml(filepath: string) {
24
- logVerbose(`Loading config ${filepath}`);
25
- const rawConfig = fs.readFileSync(filepath, 'utf-8');
26
- return yaml.parse(rawConfig);
27
- }
28
-
29
19
  public env(): TProteumLoadedEnvConfig {
30
20
  logVerbose('[app] Loading Proteum env vars from process.env');
21
+ const setup = this.setup();
31
22
  return {
32
23
  ...parseProteumEnvConfig({
33
24
  appDir: this.appDir,
25
+ connectedProjects: setup.connect,
34
26
  routerPortOverride: this.routerPortOverride,
35
27
  }),
36
28
  version: 'CLI',
37
29
  };
38
30
  }
39
31
 
40
- public identity() {
41
- const identityFile = this.appDir + '/identity.yaml';
42
- return this.loadYaml(identityFile);
32
+ public identity(): Config.Identity {
33
+ const identityFile = this.appDir + '/identity.config.ts';
34
+ logVerbose(`Loading config ${identityFile}`);
35
+ return loadApplicationIdentityConfig(this.appDir);
36
+ }
37
+
38
+ public setup(): Config.Setup {
39
+ const setupFile = this.appDir + '/proteum.config.ts';
40
+ logVerbose(`Loading config ${setupFile}`);
41
+ return loadApplicationSetupConfig(this.appDir);
43
42
  }
44
43
  }
package/cli/app/index.ts CHANGED
@@ -11,6 +11,8 @@ import fs from 'fs-extra';
11
11
  import cli from '..';
12
12
 
13
13
  // Specific
14
+ import { normalizeTranspileConfig } from '../../common/applicationConfig';
15
+ import { normalizeConnectedProjectsConfig } from '../../common/connectedProjects';
14
16
  import ConfigParser from './config';
15
17
  import type { TEnvConfig } from '../../server/app/container/config';
16
18
 
@@ -32,6 +34,30 @@ const parseRouterPortOverride = (rawPort: string | boolean | string[] | undefine
32
34
  return port;
33
35
  };
34
36
 
37
+ const normalizeModulePath = (value: string) => value.replace(/\\/g, '/').replace(/\/$/, '');
38
+
39
+ const resolveTranspileModuleDirectories = ({
40
+ moduleNames,
41
+ searchRoots,
42
+ }: {
43
+ moduleNames: string[];
44
+ searchRoots: string[];
45
+ }) => {
46
+ const directories = new Set<string>();
47
+
48
+ for (const moduleName of moduleNames) {
49
+ for (const searchRoot of searchRoots) {
50
+ const candidate = normalizeModulePath(path.join(searchRoot, 'node_modules', moduleName));
51
+ if (!fs.existsSync(candidate)) continue;
52
+
53
+ directories.add(candidate);
54
+ directories.add(normalizeModulePath(fs.realpathSync(candidate)));
55
+ }
56
+ }
57
+
58
+ return [...directories];
59
+ };
60
+
35
61
  /*----------------------------------
36
62
  - SERVICE
37
63
  ----------------------------------*/
@@ -40,6 +66,7 @@ export class App {
40
66
  // WARNING: High level config files (env and services) shouldn't be loaded from the CLI
41
67
  // The CLI will be run on CircleCI, and no env file should be sent to this service
42
68
  public identity: Config.Identity;
69
+ public setup: Config.Setup;
43
70
 
44
71
  public env: TEnvConfig;
45
72
 
@@ -77,6 +104,7 @@ export class App {
77
104
  //'Services',
78
105
  'Environment',
79
106
  'Identity',
107
+ 'Setup',
80
108
  /*'Application',
81
109
  'Path',
82
110
  'Event'*/
@@ -88,6 +116,7 @@ export class App {
88
116
 
89
117
  const configParser = new ConfigParser(cli.paths.appRoot, undefined, this.routerPortOverride);
90
118
  this.identity = configParser.identity();
119
+ this.setup = configParser.setup();
91
120
  this.env = configParser.env();
92
121
  this.packageJson = this.loadPkg();
93
122
  }
@@ -96,6 +125,35 @@ export class App {
96
125
  return target === 'dev' ? this.paths.dev : this.paths.bin;
97
126
  }
98
127
 
128
+ public get transpile() {
129
+ return normalizeTranspileConfig(this.setup.transpile);
130
+ }
131
+
132
+ public get connectedProjects() {
133
+ return normalizeConnectedProjectsConfig(this.setup.connect);
134
+ }
135
+
136
+ public get transpileModuleDirectories() {
137
+ return resolveTranspileModuleDirectories({
138
+ moduleNames: this.transpile,
139
+ searchRoots: [this.paths.root, cli.paths.core.root],
140
+ });
141
+ }
142
+
143
+ public isTranspileModuleFile(filepath: string) {
144
+ const normalizedFilepath = normalizeModulePath(path.resolve(filepath));
145
+
146
+ return this.transpileModuleDirectories.some(
147
+ (directory) => normalizedFilepath === directory || normalizedFilepath.startsWith(directory + '/'),
148
+ );
149
+ }
150
+
151
+ public isTranspileModuleRequest(request: string) {
152
+ if (path.isAbsolute(request)) return this.isTranspileModuleFile(request);
153
+
154
+ return this.transpile.some((moduleName) => request === moduleName || request.startsWith(moduleName + '/'));
155
+ }
156
+
99
157
  /*----------------------------------
100
158
  - ALIAS
101
159
  ----------------------------------*/
@@ -0,0 +1,45 @@
1
+ import cli from '..';
2
+ import Compiler from '../compiler';
3
+ import { readProteumManifest } from '../compiler/common/proteumManifest';
4
+ import { buildConnectResponse, renderConnectHuman } from '@common/dev/connect';
5
+
6
+ const allowedConnectArgs = new Set(['controllers', 'json', 'strict']);
7
+
8
+ const validateConnectArgs = () => {
9
+ const enabledArgs = Object.entries(cli.args)
10
+ .filter(([name, value]) => name !== 'workdir' && name !== 'verbose' && value === true)
11
+ .map(([name]) => name);
12
+
13
+ const invalidArgs = enabledArgs.filter((arg) => !allowedConnectArgs.has(arg));
14
+
15
+ if (invalidArgs.length > 0) {
16
+ throw new Error(
17
+ `Unknown connect argument(s): ${invalidArgs.join(', ')}. Allowed values: ${[...allowedConnectArgs].join(', ')}.`,
18
+ );
19
+ }
20
+ };
21
+
22
+ export const run = async (): Promise<void> => {
23
+ validateConnectArgs();
24
+
25
+ const compiler = new Compiler('dev');
26
+ await compiler.refreshGeneratedTypings();
27
+
28
+ const manifest = readProteumManifest(cli.paths.appRoot);
29
+ const response = buildConnectResponse(manifest, {
30
+ includeControllers: cli.args.controllers === true,
31
+ strict: cli.args.strict === true,
32
+ });
33
+
34
+ if (cli.args.json === true) {
35
+ console.log(JSON.stringify(response, null, 2));
36
+ } else {
37
+ console.log(renderConnectHuman(manifest, response));
38
+ }
39
+
40
+ if (cli.args.strict === true && response.diagnostics.length > 0) {
41
+ throw new Error(
42
+ `Proteum connect failed in strict mode with ${response.summary.errors} errors and ${response.summary.warnings} warnings.`,
43
+ );
44
+ }
45
+ };
@@ -33,7 +33,7 @@ import { app, App } from '../app';
33
33
  ----------------------------------*/
34
34
 
35
35
  // Watch rules shared by the dev compiler and hot reload gate.
36
- const ignoredWatchPathPatterns = /(node_modules\/(?!proteum\/))|(\.generated\/)|(\.cache\/)|(\.proteum\/)|(\/var\/traces\/)/;
36
+ const ignoredWatchPathPatterns = /(\.generated\/)|(\.cache\/)|(\.proteum\/)|(\/var\/traces\/)/;
37
37
  const hotReloadableServerPathPatterns = [
38
38
  /^client\/pages\//,
39
39
  /^client\/components\//,
@@ -102,14 +102,26 @@ const waitForChildExit = async (child: ChildProcess, timeoutMs: number) =>
102
102
  child.once('close', onClose);
103
103
  });
104
104
 
105
- const escapeForRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
106
- const createIgnoredWatchPattern = (outputPaths: string[]) =>
107
- new RegExp(
108
- [
109
- ignoredWatchPathPatterns.source,
110
- ...outputPaths.map((outputPath) => `(?:^${escapeForRegExp(outputPath)}(?:/|$))`),
111
- ].join('|'),
112
- );
105
+ const shouldIgnoreNodeModulesWatchPath = (watchPath: string) => {
106
+ if (!watchPath.includes('/node_modules/')) return false;
107
+ if (watchPath.includes('/node_modules/proteum/') && !watchPath.includes('/node_modules/proteum/node_modules/')) {
108
+ return false;
109
+ }
110
+
111
+ return !app.isTranspileModuleFile(watchPath);
112
+ };
113
+
114
+ const createIgnoredWatchMatcher = (outputPaths: string[]) => (watchPath: string) => {
115
+ const normalizedWatchPath = normalizeWatchPath(watchPath);
116
+
117
+ if (outputPaths.some((outputPath) => normalizedWatchPath === outputPath || normalizedWatchPath.startsWith(outputPath + '/'))) {
118
+ return true;
119
+ }
120
+
121
+ if (shouldIgnoreNodeModulesWatchPath(normalizedWatchPath)) return true;
122
+
123
+ return ignoredWatchPathPatterns.test(normalizedWatchPath);
124
+ };
113
125
  const getDevAppName = (app: App) =>
114
126
  app.identity.web?.fullTitle || app.identity.web?.title || app.identity.name || app.packageJson.name || app.paths.root;
115
127
 
@@ -162,17 +174,27 @@ async function startApp(app: App) {
162
174
  });
163
175
 
164
176
  const child = cp;
177
+ let childReady = false;
178
+
179
+ child.on('exit', (code, signal) => {
180
+ const isCurrentChild = cp === child;
181
+ if (isCurrentChild) cp = undefined;
182
+ if (!isCurrentChild || devSessionStopping || childReady) return;
165
183
 
166
- child.on('exit', () => {
167
- if (cp === child) cp = undefined;
184
+ console.error(
185
+ `Proteum dev server exited before reporting ready.${code !== null ? ` Exit code: ${code}.` : ''}${signal ? ` Signal: ${signal}.` : ''}`,
186
+ );
187
+ process.exit(code && code !== 0 ? code : 1);
168
188
  });
169
189
 
170
190
  child.on('message', (message: unknown) => {
171
191
  if (isServerReadyMessage(message)) {
192
+ childReady = true;
172
193
  void (async () => {
173
194
  console.info(
174
195
  await renderServerReadyBanner({
175
196
  appName: getDevAppName(app),
197
+ connectedProjectsCount: Object.keys(app.env.connectedProjects).length,
176
198
  publicUrl: message.publicUrl,
177
199
  routerPort: app.env.router.port,
178
200
  }),
@@ -342,8 +364,10 @@ export const run = async () => {
342
364
  await renderDevSession({
343
365
  appName: getDevAppName(app),
344
366
  appRoot: app.paths.root === process.cwd() ? '.' : app.paths.root,
367
+ connectedProjects: Object.values(app.env.connectedProjects),
345
368
  routerPort: app.env.router.port,
346
369
  devEventPort: devEventServer.port,
370
+ proteumVersion: String(cli.packageJson.version || ''),
347
371
  }),
348
372
  );
349
373
 
@@ -364,7 +388,7 @@ export const run = async () => {
364
388
 
365
389
  const multiCompiler = await compiler.create();
366
390
  const ignoredOutputPaths = [app.paths.bin, app.paths.dev].map(normalizeWatchPath);
367
- const ignoredWatchPattern = createIgnoredWatchPattern(ignoredOutputPaths);
391
+ const ignoredWatchMatcher = createIgnoredWatchMatcher(ignoredOutputPaths);
368
392
 
369
393
  const watching = multiCompiler.watch(
370
394
  {
@@ -377,7 +401,7 @@ export const run = async () => {
377
401
  // - Node modules except 5HTP core (framework dev mode)
378
402
  // - Generated files during runtime (cause infinite loop. Ex: models.d.ts)
379
403
  // - Webpack output folders (`./dev`, legacy `./bin`)
380
- ignored: ignoredWatchPattern,
404
+ ignored: ignoredWatchMatcher,
381
405
 
382
406
  //aggregateTimeout: 1000,
383
407
  },