proteum 2.5.8 → 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/LICENSE +21 -0
- package/README.md +208 -594
- 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/externals.ts +35 -0
- package/cli/compiler/server/index.ts +46 -15
- 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/server-externals.test.cjs +67 -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
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
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
type TResolveRequestOptions = { preferApp: boolean };
|
|
2
|
+
|
|
3
|
+
type TResolveServerExternalRequestOptions = {
|
|
4
|
+
context?: string;
|
|
5
|
+
frameworkRoots: string[];
|
|
6
|
+
request: string;
|
|
7
|
+
resolveRequest: (request: string, options: TResolveRequestOptions) => string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
|
|
11
|
+
|
|
12
|
+
export const isFrameworkSourceContext = (context: string | undefined, frameworkRoots: string[]) => {
|
|
13
|
+
const normalizedContext = normalizeModulePath(context);
|
|
14
|
+
|
|
15
|
+
return frameworkRoots.some((rootPath) => {
|
|
16
|
+
const normalizedRootPath = normalizeModulePath(rootPath);
|
|
17
|
+
|
|
18
|
+
return normalizedContext === normalizedRootPath || normalizedContext.startsWith(normalizedRootPath + '/');
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const resolveServerExternalRequest = ({
|
|
23
|
+
context,
|
|
24
|
+
frameworkRoots,
|
|
25
|
+
request,
|
|
26
|
+
resolveRequest,
|
|
27
|
+
}: TResolveServerExternalRequestOptions) => {
|
|
28
|
+
try {
|
|
29
|
+
return resolveRequest(request, {
|
|
30
|
+
preferApp: !isFrameworkSourceContext(context, frameworkRoots),
|
|
31
|
+
});
|
|
32
|
+
} catch {
|
|
33
|
+
return request;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -10,6 +10,12 @@ 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';
|
|
18
|
+
import { resolveServerExternalRequest } from './externals';
|
|
13
19
|
|
|
14
20
|
// Type
|
|
15
21
|
import type { App } from '../../app';
|
|
@@ -50,6 +56,9 @@ const getDevGeneratedRuntimeEntries = (app: App) => ({
|
|
|
50
56
|
__proteum_dev_routes: [app.paths.server.generated + '/routes.ts'],
|
|
51
57
|
__proteum_dev_controllers: [app.paths.server.generated + '/controllers.ts'],
|
|
52
58
|
});
|
|
59
|
+
const resolveFromAppOrCore = (request: string) => cli.paths.resolveRequest(request, { preferApp: true });
|
|
60
|
+
const resolvePackageRootFromAppOrCore = (packageName: string) =>
|
|
61
|
+
cli.paths.resolvePackageRoot(packageName, { preferApp: true });
|
|
53
62
|
const normalizeModulePath = (value?: string) => (value || '').replace(/\\/g, '/');
|
|
54
63
|
const getFrameworkSourceRoot = () => {
|
|
55
64
|
const installedCoreRoot = cli.paths.framework.installedRoot
|
|
@@ -119,6 +128,13 @@ export default function createCompiler(
|
|
|
119
128
|
const rspackAliases = toRspackAliases(resolvedAliases);
|
|
120
129
|
rspackAliases['proteum'] = frameworkSourceRoot;
|
|
121
130
|
rspackAliases['@/client/router$'] = frameworkSourceRoot + '/client/router.ts';
|
|
131
|
+
Object.assign(
|
|
132
|
+
rspackAliases,
|
|
133
|
+
resolveUiSingletonAliases({
|
|
134
|
+
resolvePackageRoot: resolvePackageRootFromAppOrCore,
|
|
135
|
+
resolveRequest: resolveFromAppOrCore,
|
|
136
|
+
}),
|
|
137
|
+
);
|
|
122
138
|
|
|
123
139
|
debug &&
|
|
124
140
|
console.log(
|
|
@@ -158,27 +174,42 @@ export default function createCompiler(
|
|
|
158
174
|
'./client-manifest.json',
|
|
159
175
|
|
|
160
176
|
// node_modules
|
|
161
|
-
function ({ request }, callback) {
|
|
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
|
+
|
|
162
185
|
const shouldCompile =
|
|
163
|
-
request !== undefined &&
|
|
164
186
|
// Local files
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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));
|
|
176
199
|
|
|
177
200
|
//console.log('isNodeModule', request, isNodeModule);
|
|
178
201
|
|
|
179
202
|
if (!shouldCompile) {
|
|
180
|
-
//
|
|
181
|
-
|
|
203
|
+
// Resolve server externals from their source owner. Bare runtime requires from
|
|
204
|
+
// the dev output can otherwise hit the framework node_modules symlink first.
|
|
205
|
+
const resolvedRequest = resolveServerExternalRequest({
|
|
206
|
+
context,
|
|
207
|
+
frameworkRoots,
|
|
208
|
+
request,
|
|
209
|
+
resolveRequest: (externalRequest, options) => cli.paths.resolveRequest(externalRequest, options),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return callback(undefined, 'commonjs ' + resolvedRequest);
|
|
182
213
|
}
|
|
183
214
|
|
|
184
215
|
// Continue without externalizing the import
|
|
@@ -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
|
'',
|