proteum 2.5.9 → 2.5.10
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 +4 -4
- package/agents/project/AGENTS.md +131 -91
- package/agents/project/CODING_STYLE.md +42 -40
- package/agents/project/client/AGENTS.md +0 -1
- package/agents/project/diagnostics.md +6 -5
- package/agents/project/optimizations.md +1 -7
- package/agents/project/server/services/AGENTS.md +1 -3
- package/agents/project/tests/AGENTS.md +3 -3
- package/cli/commands/session.ts +36 -5
- package/cli/commands/verify.ts +6 -1
- package/cli/compiler/client/index.ts +11 -4
- package/cli/compiler/common/uiSingletons.ts +76 -0
- package/cli/compiler/server/index.ts +34 -12
- package/cli/presentation/commands.ts +6 -1
- package/cli/runtime/commands.ts +2 -0
- package/cli/scaffold/index.ts +3 -0
- package/cli/scaffold/templates.ts +62 -6
- package/cli/utils/agents.ts +2 -2
- package/client/dev/profiler/index.tsx +761 -455
- package/common/dev/session.ts +32 -0
- package/common/errors/index.tsx +0 -1
- package/docs/agent-routing.md +2 -2
- package/package.json +1 -1
- package/server/app/container/console/index.ts +0 -17
- package/server/services/router/http/index.ts +130 -33
- package/tests/agents-utils.test.cjs +0 -4
- package/tests/dev-session-login-url.test.cjs +33 -0
- package/tests/ui-singletons.test.cjs +104 -0
- package/agents/project/app-root/AGENTS.md +0 -14
- package/agents/project/root/AGENTS.md +0 -399
|
@@ -9,9 +9,9 @@ Diagnostics source of truth: root-level `diagnostics.md`.
|
|
|
9
9
|
|
|
10
10
|
- Understand the real user flow and the main feature branches before writing tests.
|
|
11
11
|
- Test the current controller/page runtime model, not legacy `@Route` or `api.fetch(...)` behavior.
|
|
12
|
-
- For every production change, add or update focused unit tests and run the targeted test command that matches the changed behavior. When the repository defines `proteum.verify.config.ts`, use `npx proteum verify changed` first so changed test files, related source tests, and project-specific suites are selected consistently.
|
|
12
|
+
- For every production change, add or update focused unit tests and run the targeted test command that matches the changed behavior. When the repository defines `proteum.verify.config.ts`, use `npx proteum verify changed` first so changed test files, related source tests, and project-specific suites are selected consistently. Coverage, commit-time, and push-time gates follow the root contract `Verification Policy` and `Commit Workflow`.
|
|
13
13
|
- Verify routing, controllers, SSR, and router plugins against a running app when behavior depends on real request handling.
|
|
14
|
-
- After implementing a new feature or changing existing feature behavior, update the end-to-end coverage for that behavior and run the
|
|
14
|
+
- After implementing a new feature or changing existing feature behavior, update the end-to-end coverage for that behavior and run the targeted Playwright scenarios that cover it before finishing; reserve the full suite for push workflows or explicit requests per the root contract `Verification Policy`. Prefer `npx proteum e2e --port <port>` for Playwright runs so base URLs and auth tokens are passed through Proteum-managed child env instead of shell-leading environment assignments. For new UI-visible features, the root `Verification Policy` still requires a browser MCP pass against the real page before finishing.
|
|
15
15
|
- Exercise real URLs, generated controller calls, or real browser flows instead of re-deriving framework internals in tests.
|
|
16
16
|
- Organize end-to-end tests following the Crosspath platform layout under `tests/e2e/**`.
|
|
17
17
|
- Put runnable scenario entrypoints in `tests/e2e/features/**`, `tests/e2e/specs/<domain>/**`, or `tests/e2e/journeys/**` depending on scope.
|
|
@@ -23,7 +23,7 @@ Diagnostics source of truth: root-level `diagnostics.md`.
|
|
|
23
23
|
- Add `data-testid` where needed instead of relying on brittle selectors.
|
|
24
24
|
- Keep end-to-end tests clean, well organized, and non-redundant. Prefer extending or reshaping the most relevant existing scenario over duplicating coverage, and remove or consolidate overlap when the suite becomes repetitive.
|
|
25
25
|
- Reuse root catalog files from `/client/catalogs/**`, `/server/catalogs/**`, or `/common/catalogs/**` instead of duplicating catalog constants in tests.
|
|
26
|
-
- For
|
|
26
|
+
- For browser MCP validation in dev, start from the project instructions' admin account with `npx proteum session <admin-email> --role GOD` or its `browserLoginUrl` unless the scenario intentionally covers auth, anonymous, or non-admin behavior. For protected E2E dev flows, prefer `npx proteum e2e --session-email <email> --session-role <role>` over automating login unless the login flow itself is under test.
|
|
27
27
|
|
|
28
28
|
### Real-World Journey E2E
|
|
29
29
|
|
package/cli/commands/session.ts
CHANGED
|
@@ -4,7 +4,13 @@ import { spawn } from 'child_process';
|
|
|
4
4
|
import { UsageError } from 'clipanion';
|
|
5
5
|
|
|
6
6
|
import cli from '..';
|
|
7
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
buildDevSessionLoginUrl,
|
|
9
|
+
devSessionStartPath,
|
|
10
|
+
normalizeDevSessionRedirectPath,
|
|
11
|
+
type TDevSessionErrorResponse,
|
|
12
|
+
type TDevSessionStartResponse,
|
|
13
|
+
} from '../../common/dev/session';
|
|
8
14
|
|
|
9
15
|
const localSessionResultMarker = '__PROTEUM_SESSION_RESULT__';
|
|
10
16
|
|
|
@@ -13,6 +19,7 @@ type TResolvedSessionOutput = {
|
|
|
13
19
|
user: TDevSessionStartResponse['user'];
|
|
14
20
|
session: TDevSessionStartResponse['session'];
|
|
15
21
|
browserCookie: string;
|
|
22
|
+
browserLoginUrl: string;
|
|
16
23
|
curlCookieHeader: string;
|
|
17
24
|
playwright: {
|
|
18
25
|
cookies: Array<{
|
|
@@ -28,6 +35,13 @@ type TResolvedSessionOutput = {
|
|
|
28
35
|
};
|
|
29
36
|
|
|
30
37
|
const normalizeBaseUrl = (value: string) => value.replace(/\/+$/, '');
|
|
38
|
+
const normalizeRedirectPath = (value: string): string => {
|
|
39
|
+
try {
|
|
40
|
+
return normalizeDevSessionRedirectPath(value);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new UsageError(error instanceof Error ? error.message : String(error));
|
|
43
|
+
}
|
|
44
|
+
};
|
|
31
45
|
|
|
32
46
|
const getRouterPortFromManifest = () => {
|
|
33
47
|
const manifestFilepath = path.join(cli.args.workdir as string, '.proteum', 'manifest.json');
|
|
@@ -82,7 +96,7 @@ const requestSession = async (email: string, role: string) => {
|
|
|
82
96
|
|
|
83
97
|
for (const baseUrl of getRouterBaseUrls()) {
|
|
84
98
|
try {
|
|
85
|
-
const response = await got(`${baseUrl}
|
|
99
|
+
const response = await got(`${baseUrl}${devSessionStartPath}`, {
|
|
86
100
|
method: 'POST',
|
|
87
101
|
json: role ? { email, role } : { email },
|
|
88
102
|
responseType: 'json',
|
|
@@ -92,7 +106,7 @@ const requestSession = async (email: string, role: string) => {
|
|
|
92
106
|
|
|
93
107
|
if (response.statusCode >= 400) {
|
|
94
108
|
if (response.statusCode === 404 && !hasStructuredSessionError(response.body as TDevSessionErrorResponse | object | string | undefined)) {
|
|
95
|
-
attempts.push(`${baseUrl}
|
|
109
|
+
attempts.push(`${baseUrl}${devSessionStartPath}: returned 404`);
|
|
96
110
|
continue;
|
|
97
111
|
}
|
|
98
112
|
|
|
@@ -109,7 +123,7 @@ const requestSession = async (email: string, role: string) => {
|
|
|
109
123
|
if (error instanceof UsageError) throw error;
|
|
110
124
|
|
|
111
125
|
const message = error instanceof Error ? error.message : String(error);
|
|
112
|
-
attempts.push(`${baseUrl}
|
|
126
|
+
attempts.push(`${baseUrl}${devSessionStartPath}: ${message}`);
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
129
|
|
|
@@ -124,9 +138,13 @@ const requestSession = async (email: string, role: string) => {
|
|
|
124
138
|
|
|
125
139
|
const buildSessionOutput = ({
|
|
126
140
|
baseUrl,
|
|
141
|
+
redirect,
|
|
142
|
+
role,
|
|
127
143
|
response,
|
|
128
144
|
}: {
|
|
129
145
|
baseUrl: string;
|
|
146
|
+
redirect: string;
|
|
147
|
+
role: string;
|
|
130
148
|
response: TDevSessionStartResponse;
|
|
131
149
|
}): TResolvedSessionOutput => {
|
|
132
150
|
const expires = Math.floor(Date.parse(response.session.expiresAt) / 1000);
|
|
@@ -137,6 +155,12 @@ const buildSessionOutput = ({
|
|
|
137
155
|
user: response.user,
|
|
138
156
|
session: response.session,
|
|
139
157
|
browserCookie: `${response.session.cookieName}=${response.session.token}; Path=/`,
|
|
158
|
+
browserLoginUrl: buildDevSessionLoginUrl({
|
|
159
|
+
baseUrl,
|
|
160
|
+
email: response.user.email,
|
|
161
|
+
redirect,
|
|
162
|
+
role,
|
|
163
|
+
}),
|
|
140
164
|
curlCookieHeader: `Cookie: ${response.session.cookieName}=${response.session.token}`,
|
|
141
165
|
playwright: {
|
|
142
166
|
cookies: [
|
|
@@ -170,6 +194,8 @@ const renderSession = (value: TResolvedSessionOutput) =>
|
|
|
170
194
|
JSON.stringify(value.playwright, null, 2),
|
|
171
195
|
'Browser Cookie',
|
|
172
196
|
value.browserCookie,
|
|
197
|
+
'Browser Login URL',
|
|
198
|
+
value.browserLoginUrl,
|
|
173
199
|
].join('\n');
|
|
174
200
|
|
|
175
201
|
const runLocalSession = async (email: string, role: string) => {
|
|
@@ -232,6 +258,7 @@ const runLocalSession = async (email: string, role: string) => {
|
|
|
232
258
|
export const run = async () => {
|
|
233
259
|
const email = typeof cli.args.email === 'string' ? cli.args.email.trim() : '';
|
|
234
260
|
const role = typeof cli.args.role === 'string' ? cli.args.role.trim() : '';
|
|
261
|
+
const redirect = normalizeRedirectPath(typeof cli.args.redirect === 'string' ? cli.args.redirect : '/');
|
|
235
262
|
const shouldPrintJson = cli.args.json === true;
|
|
236
263
|
const shouldUseRemoteServer =
|
|
237
264
|
(typeof cli.args.port === 'string' && cli.args.port.length > 0) ||
|
|
@@ -242,7 +269,11 @@ export const run = async () => {
|
|
|
242
269
|
}
|
|
243
270
|
|
|
244
271
|
const resolved = buildSessionOutput(
|
|
245
|
-
|
|
272
|
+
{
|
|
273
|
+
...(shouldUseRemoteServer ? await requestSession(email, role) : await runLocalSession(email, role)),
|
|
274
|
+
redirect,
|
|
275
|
+
role,
|
|
276
|
+
},
|
|
246
277
|
);
|
|
247
278
|
|
|
248
279
|
if (shouldPrintJson) {
|
package/cli/commands/verify.ts
CHANGED
|
@@ -1149,7 +1149,12 @@ const runChangedVerify = async () => {
|
|
|
1149
1149
|
code: 'changed/check-failed',
|
|
1150
1150
|
message: `Verification check "${execution.checkId}" failed with exit code ${execution.exitCode ?? 'unknown'}.`,
|
|
1151
1151
|
source: 'changed',
|
|
1152
|
-
details: [
|
|
1152
|
+
details: [
|
|
1153
|
+
`command=${execution.command}`,
|
|
1154
|
+
`cwd=${execution.cwd}`,
|
|
1155
|
+
`durationMs=${execution.durationMs}`,
|
|
1156
|
+
'fix=Fix the failing surface and re-run npx proteum verify changed; broader gates only per the root AGENTS.md Verification Policy.',
|
|
1157
|
+
],
|
|
1153
1158
|
}));
|
|
1154
1159
|
|
|
1155
1160
|
return finalizeResult({
|
|
@@ -12,6 +12,7 @@ import { rspack, type Configuration, type Module } from '@rspack/core';
|
|
|
12
12
|
import createCommonConfig, { TCompileMode, TCompileOutputTarget, regex } from '../common';
|
|
13
13
|
import { createClientBundleAnalysisPlugins } from '../common/bundleAnalysis';
|
|
14
14
|
import { toRspackAliases } from '../common/rspackAliases';
|
|
15
|
+
import { resolveUiSingletonAliases } from '../common/uiSingletons';
|
|
15
16
|
import identityAssets from './identite';
|
|
16
17
|
import cli from '../..';
|
|
17
18
|
import { logVerbose } from '../../runtime/verbose';
|
|
@@ -35,7 +36,9 @@ const getFrameworkSourceRoot = () => {
|
|
|
35
36
|
return activeCoreRoot;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
const resolveFromAppOrCore = (
|
|
39
|
+
const resolveFromAppOrCore = (request: string) => cli.paths.resolveRequest(request, { preferApp: true });
|
|
40
|
+
const resolvePackageRootFromAppOrCore = (packageName: string) =>
|
|
41
|
+
cli.paths.resolvePackageRoot(packageName, { preferApp: true });
|
|
39
42
|
const rewriteFrameworkAliasTargets = (aliases: Record<string, string | string[]>) => {
|
|
40
43
|
const visibleFrameworkRoots = [
|
|
41
44
|
...cli.paths.getVisiblePackageInstallRoots('proteum'),
|
|
@@ -145,9 +148,13 @@ export default function createCompiler(
|
|
|
145
148
|
const rspackAliases = toRspackAliases(resolvedAliases);
|
|
146
149
|
rspackAliases['proteum'] = frameworkSourceRoot;
|
|
147
150
|
rspackAliases['@/client/router$'] = frameworkSourceRoot + '/client/router.ts';
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
+
Object.assign(
|
|
152
|
+
rspackAliases,
|
|
153
|
+
resolveUiSingletonAliases({
|
|
154
|
+
resolvePackageRoot: resolvePackageRootFromAppOrCore,
|
|
155
|
+
resolveRequest: resolveFromAppOrCore,
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
151
158
|
|
|
152
159
|
debug && console.log('client aliases', rspackAliases);
|
|
153
160
|
const config: Configuration = {
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
type TUiSingletonResolvers = {
|
|
2
|
+
resolvePackageRoot: (packageName: string) => string;
|
|
3
|
+
resolveRequest: (request: string) => string;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
const uiSingletonPackages = ['preact', 'preact-render-to-string', 'react', 'react-dom'];
|
|
7
|
+
|
|
8
|
+
const uiSingletonPackageAliases: Array<{ alias: string; packageName: string }> = [
|
|
9
|
+
{ alias: 'preact', packageName: 'preact' },
|
|
10
|
+
{ alias: 'preact-render-to-string', packageName: 'preact-render-to-string' },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const uiSingletonRequestAliases: Array<{ alias: string; request: string }> = [
|
|
14
|
+
{ alias: 'preact$', request: 'preact' },
|
|
15
|
+
{ alias: 'preact/hooks$', request: 'preact/hooks' },
|
|
16
|
+
{ alias: 'preact/compat$', request: 'preact/compat' },
|
|
17
|
+
{ alias: 'preact/compat/client$', request: 'preact/compat/client' },
|
|
18
|
+
{ alias: 'preact/jsx-runtime$', request: 'preact/jsx-runtime' },
|
|
19
|
+
{ alias: 'preact/jsx-dev-runtime$', request: 'preact/jsx-dev-runtime' },
|
|
20
|
+
{ alias: 'react$', request: 'preact/compat' },
|
|
21
|
+
{ alias: 'react-dom$', request: 'preact/compat' },
|
|
22
|
+
{ alias: 'react-dom/client$', request: 'preact/compat/client' },
|
|
23
|
+
{ alias: 'react-dom/test-utils$', request: 'preact/test-utils' },
|
|
24
|
+
{ alias: 'react/jsx-runtime$', request: 'preact/jsx-runtime' },
|
|
25
|
+
{ alias: 'react/jsx-dev-runtime$', request: 'preact/jsx-dev-runtime' },
|
|
26
|
+
{ alias: 'preact-render-to-string$', request: 'preact-render-to-string' },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const uiSingletonServerExternalRequests: Array<{ request: string; resolvedRequest: string }> = [
|
|
30
|
+
{ request: 'preact', resolvedRequest: 'preact' },
|
|
31
|
+
{ request: 'preact/hooks', resolvedRequest: 'preact/hooks' },
|
|
32
|
+
{ request: 'preact/compat', resolvedRequest: 'preact/compat' },
|
|
33
|
+
{ request: 'preact/compat/client', resolvedRequest: 'preact/compat/client' },
|
|
34
|
+
{ request: 'preact/jsx-runtime', resolvedRequest: 'preact/jsx-runtime' },
|
|
35
|
+
{ request: 'preact/jsx-dev-runtime', resolvedRequest: 'preact/jsx-dev-runtime' },
|
|
36
|
+
{ request: 'preact/test-utils', resolvedRequest: 'preact/test-utils' },
|
|
37
|
+
{ request: 'react', resolvedRequest: 'preact/compat' },
|
|
38
|
+
{ request: 'react-dom', resolvedRequest: 'preact/compat' },
|
|
39
|
+
{ request: 'react-dom/client', resolvedRequest: 'preact/compat/client' },
|
|
40
|
+
{ request: 'react-dom/test-utils', resolvedRequest: 'preact/test-utils' },
|
|
41
|
+
{ request: 'react/jsx-runtime', resolvedRequest: 'preact/jsx-runtime' },
|
|
42
|
+
{ request: 'react/jsx-dev-runtime', resolvedRequest: 'preact/jsx-dev-runtime' },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export const isUiSingletonRequest = (request: string | undefined) =>
|
|
46
|
+
typeof request === 'string' &&
|
|
47
|
+
uiSingletonPackages.some((packageName) => request === packageName || request.startsWith(`${packageName}/`));
|
|
48
|
+
|
|
49
|
+
export const resolveUiSingletonAliases = ({ resolvePackageRoot, resolveRequest }: TUiSingletonResolvers) => ({
|
|
50
|
+
...Object.fromEntries(
|
|
51
|
+
uiSingletonRequestAliases.map(({ alias, request }) => [
|
|
52
|
+
alias,
|
|
53
|
+
resolveRequest(request),
|
|
54
|
+
]),
|
|
55
|
+
),
|
|
56
|
+
...Object.fromEntries(
|
|
57
|
+
uiSingletonPackageAliases.map(({ alias, packageName }) => [
|
|
58
|
+
alias,
|
|
59
|
+
resolvePackageRoot(packageName),
|
|
60
|
+
]),
|
|
61
|
+
),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export const resolveUiSingletonServerExternalRequest = (
|
|
65
|
+
request: string | undefined,
|
|
66
|
+
resolveRequest: TUiSingletonResolvers['resolveRequest'],
|
|
67
|
+
) => {
|
|
68
|
+
if (request === undefined) return undefined;
|
|
69
|
+
|
|
70
|
+
const exactExternalRequest = uiSingletonServerExternalRequests.find((entry) => entry.request === request);
|
|
71
|
+
if (exactExternalRequest) return resolveRequest(exactExternalRequest.resolvedRequest);
|
|
72
|
+
|
|
73
|
+
if (request.startsWith('preact/')) return resolveRequest(request);
|
|
74
|
+
|
|
75
|
+
return undefined;
|
|
76
|
+
};
|
|
@@ -10,6 +10,11 @@ import { type Configuration } from '@rspack/core';
|
|
|
10
10
|
import cli from '@cli';
|
|
11
11
|
import createCommonConfig, { TCompileMode, TCompileOutputTarget, regex } from '../common';
|
|
12
12
|
import { toRspackAliases } from '../common/rspackAliases';
|
|
13
|
+
import {
|
|
14
|
+
isUiSingletonRequest,
|
|
15
|
+
resolveUiSingletonAliases,
|
|
16
|
+
resolveUiSingletonServerExternalRequest,
|
|
17
|
+
} from '../common/uiSingletons';
|
|
13
18
|
import { resolveServerExternalRequest } from './externals';
|
|
14
19
|
|
|
15
20
|
// Type
|
|
@@ -51,6 +56,9 @@ const getDevGeneratedRuntimeEntries = (app: App) => ({
|
|
|
51
56
|
__proteum_dev_routes: [app.paths.server.generated + '/routes.ts'],
|
|
52
57
|
__proteum_dev_controllers: [app.paths.server.generated + '/controllers.ts'],
|
|
53
58
|
});
|
|
59
|
+
const resolveFromAppOrCore = (request: string) => cli.paths.resolveRequest(request, { preferApp: true });
|
|
60
|
+
const resolvePackageRootFromAppOrCore = (packageName: string) =>
|
|
61
|
+
cli.paths.resolvePackageRoot(packageName, { preferApp: true });
|
|
54
62
|
const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
|
|
55
63
|
const getFrameworkSourceRoot = () => {
|
|
56
64
|
const installedCoreRoot = cli.paths.framework.installedRoot
|
|
@@ -120,6 +128,13 @@ export default function createCompiler(
|
|
|
120
128
|
const rspackAliases = toRspackAliases(resolvedAliases);
|
|
121
129
|
rspackAliases['proteum'] = frameworkSourceRoot;
|
|
122
130
|
rspackAliases['@/client/router$'] = frameworkSourceRoot + '/client/router.ts';
|
|
131
|
+
Object.assign(
|
|
132
|
+
rspackAliases,
|
|
133
|
+
resolveUiSingletonAliases({
|
|
134
|
+
resolvePackageRoot: resolvePackageRootFromAppOrCore,
|
|
135
|
+
resolveRequest: resolveFromAppOrCore,
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
123
138
|
|
|
124
139
|
debug &&
|
|
125
140
|
console.log(
|
|
@@ -160,20 +175,27 @@ export default function createCompiler(
|
|
|
160
175
|
|
|
161
176
|
// node_modules
|
|
162
177
|
function ({ context, request }, callback) {
|
|
178
|
+
if (request === undefined) return callback();
|
|
179
|
+
|
|
180
|
+
const uiSingletonExternalRequest = resolveUiSingletonServerExternalRequest(request, resolveFromAppOrCore);
|
|
181
|
+
if (uiSingletonExternalRequest !== undefined) {
|
|
182
|
+
return callback(undefined, 'commonjs ' + uiSingletonExternalRequest);
|
|
183
|
+
}
|
|
184
|
+
|
|
163
185
|
const shouldCompile =
|
|
164
|
-
request !== undefined &&
|
|
165
186
|
// Local files
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
187
|
+
request[0] === '.' ||
|
|
188
|
+
request[0] === '/' ||
|
|
189
|
+
// Aliased modules
|
|
190
|
+
app.aliases.server.containsAlias(request) ||
|
|
191
|
+
// TODO: proteum.conf: compile: include
|
|
192
|
+
app.isTranspileModuleRequest(request) ||
|
|
193
|
+
// Compile proteum modules
|
|
194
|
+
request.startsWith('proteum') ||
|
|
195
|
+
// React/Preact singleton packages and React-based UI packages must pass through the alias layer on the server,
|
|
196
|
+
// otherwise SSR can mix real React packages with the Preact compat runtime.
|
|
197
|
+
isUiSingletonRequest(request) ||
|
|
198
|
+
serverReactCompatCompilePrefixes.some((prefix) => request.startsWith(prefix));
|
|
177
199
|
|
|
178
200
|
//console.log('isNodeModule', request, isNodeModule);
|
|
179
201
|
|
|
@@ -594,7 +594,7 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
|
|
|
594
594
|
name: 'session',
|
|
595
595
|
category: 'Manifest and contracts',
|
|
596
596
|
summary: 'Mint a dev-only auth session token and cookie payload for a known user.',
|
|
597
|
-
usage: 'proteum session <email> [--role <role>] [--port <port>|--url <baseUrl>] [--json]',
|
|
597
|
+
usage: 'proteum session <email> [--role <role>] [--redirect <path>] [--port <port>|--url <baseUrl>] [--json]',
|
|
598
598
|
bestFor:
|
|
599
599
|
'Starting browser or API automation from an authenticated state without driving the login UI, while still using the app-configured auth service.',
|
|
600
600
|
examples: [
|
|
@@ -606,11 +606,16 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
|
|
|
606
606
|
description: 'Mint a GOD session for unique.domains and print machine-readable cookie data',
|
|
607
607
|
command: 'proteum session god@example.com --role GOD --json',
|
|
608
608
|
},
|
|
609
|
+
{
|
|
610
|
+
description: 'Print a browser login URL that sets the cookie and opens a protected page',
|
|
611
|
+
command: 'proteum session admin@example.com --port 3101 --redirect /admin --json',
|
|
612
|
+
},
|
|
609
613
|
],
|
|
610
614
|
notes: [
|
|
611
615
|
'Sessions are available only in dev mode and use the auth service registered on the current app router.',
|
|
612
616
|
'You must provide the target user email explicitly; Proteum does not guess your admin account universally across apps.',
|
|
613
617
|
'The command returns a token plus Playwright-ready cookie JSON so agents can inject the session into a browser context directly.',
|
|
618
|
+
'The command also returns a browser login URL that works on localhost dev servers, sets the same session cookie, and redirects to a local path.',
|
|
614
619
|
'Without `--port` or `--url`, Proteum refreshes generated artifacts, builds the dev output, starts a temporary local dev server, creates the session, prints the payload, and exits.',
|
|
615
620
|
],
|
|
616
621
|
status: 'experimental',
|
package/cli/runtime/commands.ts
CHANGED
|
@@ -617,6 +617,7 @@ class SessionCommand extends ProteumCommand {
|
|
|
617
617
|
public role = Option.String('--role', { description: 'Require the resolved user to have the given role.' });
|
|
618
618
|
public port = Option.String('--port', { description: 'Target an existing dev server on the given port.' });
|
|
619
619
|
public url = Option.String('--url', { description: 'Target an existing dev server at the given base URL.' });
|
|
620
|
+
public redirect = Option.String('--redirect', { description: 'Local path used by the browser login URL.' });
|
|
620
621
|
public json = Option.Boolean('--json', false, { description: 'Print JSON output.' });
|
|
621
622
|
public args = Option.Rest();
|
|
622
623
|
|
|
@@ -628,6 +629,7 @@ class SessionCommand extends ProteumCommand {
|
|
|
628
629
|
role: this.role ?? '',
|
|
629
630
|
port: this.port ?? '',
|
|
630
631
|
url: this.url ?? '',
|
|
632
|
+
redirect: this.redirect ?? '',
|
|
631
633
|
json: this.json,
|
|
632
634
|
});
|
|
633
635
|
|
package/cli/scaffold/index.ts
CHANGED
|
@@ -556,6 +556,9 @@ export const runCreateScaffold = async () => {
|
|
|
556
556
|
|
|
557
557
|
result.notes.push(...plan.notes);
|
|
558
558
|
result.nextSteps.push(...plan.nextSteps);
|
|
559
|
+
result.nextSteps.push(
|
|
560
|
+
'Adapt the generated code to the real feature and record non-obvious decisions as why-comments per `CODING_STYLE.md` (`Decision and context comments`).',
|
|
561
|
+
);
|
|
559
562
|
printResult(result);
|
|
560
563
|
};
|
|
561
564
|
|
|
@@ -22,7 +22,15 @@ export const createPageTemplate = ({
|
|
|
22
22
|
routePath: string;
|
|
23
23
|
heading: string;
|
|
24
24
|
message: string;
|
|
25
|
-
}) =>
|
|
25
|
+
}) => `/*----------------------------------
|
|
26
|
+
- DEPENDANCES
|
|
27
|
+
----------------------------------*/
|
|
28
|
+
|
|
29
|
+
import { definePageRoute } from '@common/router/definitions';
|
|
30
|
+
|
|
31
|
+
/*----------------------------------
|
|
32
|
+
- PAGE
|
|
33
|
+
----------------------------------*/
|
|
26
34
|
|
|
27
35
|
export default definePageRoute({
|
|
28
36
|
path: ${JSON.stringify(routePath)},
|
|
@@ -53,7 +61,15 @@ export const createControllerTemplate = ({
|
|
|
53
61
|
appIdentifier: string;
|
|
54
62
|
className: string;
|
|
55
63
|
methodName: string;
|
|
56
|
-
}) =>
|
|
64
|
+
}) => `/*----------------------------------
|
|
65
|
+
- DEPENDANCES
|
|
66
|
+
----------------------------------*/
|
|
67
|
+
|
|
68
|
+
import { defineAction, defineController } from '@generated/server/controller';
|
|
69
|
+
|
|
70
|
+
/*----------------------------------
|
|
71
|
+
- CONTROLEUR
|
|
72
|
+
----------------------------------*/
|
|
57
73
|
|
|
58
74
|
export default defineController({
|
|
59
75
|
actions: {
|
|
@@ -74,11 +90,23 @@ export const createCommandTemplate = ({
|
|
|
74
90
|
}: {
|
|
75
91
|
className: string;
|
|
76
92
|
methodName: string;
|
|
77
|
-
}) =>
|
|
93
|
+
}) => `/*----------------------------------
|
|
94
|
+
- DEPENDANCES
|
|
95
|
+
----------------------------------*/
|
|
96
|
+
|
|
97
|
+
import { Commands } from '@server/app/commands';
|
|
78
98
|
import type AppApplication from '@/server/index';
|
|
79
99
|
|
|
100
|
+
/*----------------------------------
|
|
101
|
+
- TYPES
|
|
102
|
+
----------------------------------*/
|
|
103
|
+
|
|
80
104
|
type App = InstanceType<typeof AppApplication>;
|
|
81
105
|
|
|
106
|
+
/*----------------------------------
|
|
107
|
+
- COMMANDS
|
|
108
|
+
----------------------------------*/
|
|
109
|
+
|
|
82
110
|
export default class ${className} extends Commands<App> {
|
|
83
111
|
public async ${methodName}() {
|
|
84
112
|
return {
|
|
@@ -95,7 +123,15 @@ export const createRouteTemplate = ({
|
|
|
95
123
|
}: {
|
|
96
124
|
httpMethod: string;
|
|
97
125
|
routePath: string;
|
|
98
|
-
}) =>
|
|
126
|
+
}) => `/*----------------------------------
|
|
127
|
+
- DEPENDANCES
|
|
128
|
+
----------------------------------*/
|
|
129
|
+
|
|
130
|
+
import { defineServerRoute } from '@common/router/definitions';
|
|
131
|
+
|
|
132
|
+
/*----------------------------------
|
|
133
|
+
- ROUTES
|
|
134
|
+
----------------------------------*/
|
|
99
135
|
|
|
100
136
|
export default defineServerRoute({
|
|
101
137
|
method: ${JSON.stringify(httpMethod.toUpperCase())},
|
|
@@ -115,12 +151,24 @@ export const createServiceTemplate = ({
|
|
|
115
151
|
}: {
|
|
116
152
|
appIdentifier: string;
|
|
117
153
|
className: string;
|
|
118
|
-
}) =>
|
|
154
|
+
}) => `/*----------------------------------
|
|
155
|
+
- DEPENDANCES
|
|
156
|
+
----------------------------------*/
|
|
157
|
+
|
|
158
|
+
import Service from '@server/app/service';
|
|
159
|
+
|
|
160
|
+
/*----------------------------------
|
|
161
|
+
- TYPES
|
|
162
|
+
----------------------------------*/
|
|
119
163
|
|
|
120
164
|
export type Config = {
|
|
121
165
|
debug?: boolean;
|
|
122
166
|
};
|
|
123
167
|
|
|
168
|
+
/*----------------------------------
|
|
169
|
+
- SERVICE
|
|
170
|
+
----------------------------------*/
|
|
171
|
+
|
|
124
172
|
export default class ${className} extends Service<Config, {}, ${appIdentifier}, ${appIdentifier}> {
|
|
125
173
|
public async health() {
|
|
126
174
|
return {
|
|
@@ -138,9 +186,17 @@ export const createServiceConfigTemplate = ({
|
|
|
138
186
|
configExportName: string;
|
|
139
187
|
serviceImportPath: string;
|
|
140
188
|
serviceImportName: string;
|
|
141
|
-
}) =>
|
|
189
|
+
}) => `/*----------------------------------
|
|
190
|
+
- DEPENDANCES
|
|
191
|
+
----------------------------------*/
|
|
192
|
+
|
|
193
|
+
import { Services } from '@server/app';
|
|
142
194
|
import ${serviceImportName} from ${JSON.stringify(serviceImportPath)};
|
|
143
195
|
|
|
196
|
+
/*----------------------------------
|
|
197
|
+
- CONFIG
|
|
198
|
+
----------------------------------*/
|
|
199
|
+
|
|
144
200
|
export const ${configExportName} = Services.config(${serviceImportName}, {});
|
|
145
201
|
`;
|
|
146
202
|
|
package/cli/utils/agents.ts
CHANGED
|
@@ -1053,11 +1053,11 @@ function renderEmbeddedProjectInstructions({
|
|
|
1053
1053
|
'- Worktree Preflight (`cwd` inside `/.codex/worktrees/`, newly created Proteum worktree, or before editing in a Codex worktree): read Root contract fallback, run `npx proteum worktree init --source <source-app-root>` when the bootstrap marker is missing, run `npx proteum worktree init --source <source-app-root> --refresh` when Proteum reports stale bootstrap state, use `--skip-deps --reason "..."` only for intentional dependency skips, then run `npx proteum runtime status`; for runtime-visible work start or reuse one tracked `npx proteum dev` session using the Task Lifecycle launch workflow.',
|
|
1054
1054
|
'- Git lifecycle (`commit`, `and commit`, `stage`, `push`, `PR`, pull request): read Root contract fallback before any git write.',
|
|
1055
1055
|
'- Before git writes after a bug fix, behavior change, decision change, or docs-relevant production change: read `DOCUMENTATION.md` and verify required docs, fix notes, or ADRs were updated or explicitly skipped with a reason.',
|
|
1056
|
-
'- Before finishing production code changes: read Root contract fallback, `DOCUMENTATION.md`, `CODING_STYLE.md`, `tests/AGENTS.md`, and any touched area `AGENTS.md`.',
|
|
1056
|
+
'- Before finishing production code changes: read Root contract fallback, `DOCUMENTATION.md`, `CODING_STYLE.md`, `tests/AGENTS.md`, and any touched area `AGENTS.md`. Run the `CODING_STYLE.md` self-check on the final diff; a non-obvious decision, workaround, or bug fix without a why-comment is a defect to fix before finishing.',
|
|
1057
1057
|
'- Runtime-visible, request-time, router, SSR, browser, or controller behavior: read Root contract fallback and `diagnostics.md` for verification routing.',
|
|
1058
1058
|
'- Bug fixes, regressions, incidents, broken public routes, auth/OAuth failures, integration failures, or production behavior fixes: read `DOCUMENTATION.md` before editing and update the relevant fix/regression docs when required.',
|
|
1059
1059
|
'- Non-trivial feature, product, business-rule, UX, copy, or docs changes: read `DOCUMENTATION.md` before editing.',
|
|
1060
|
-
'- Implementation edits: read `CODING_STYLE.md` before editing, plus the matching area file from the routing table.',
|
|
1060
|
+
'- Implementation edits: read `CODING_STYLE.md` before editing, plus the matching area file from the routing table. While editing, record non-obvious decisions, workarounds, and constraints as why-comments per its `Decision and context comments` section.',
|
|
1061
1061
|
'',
|
|
1062
1062
|
'## Routing Table',
|
|
1063
1063
|
'',
|