proteum 2.1.2 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/AGENTS.md +22 -14
  2. package/README.md +112 -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/command.ts +8 -0
  15. package/cli/commands/connect.ts +45 -0
  16. package/cli/commands/dev.ts +26 -11
  17. package/cli/commands/diagnose.ts +286 -0
  18. package/cli/commands/doctor.ts +18 -5
  19. package/cli/commands/explain.ts +25 -0
  20. package/cli/commands/perf.ts +243 -0
  21. package/cli/commands/session.ts +254 -0
  22. package/cli/commands/sessionLocalRunner.js +188 -0
  23. package/cli/commands/trace.ts +17 -1
  24. package/cli/commands/verify.ts +281 -0
  25. package/cli/compiler/artifacts/connectedProjects.ts +453 -0
  26. package/cli/compiler/artifacts/controllers.ts +198 -49
  27. package/cli/compiler/artifacts/discovery.ts +0 -34
  28. package/cli/compiler/artifacts/manifest.ts +90 -6
  29. package/cli/compiler/artifacts/routing.ts +2 -2
  30. package/cli/compiler/artifacts/services.ts +277 -130
  31. package/cli/compiler/client/index.ts +3 -0
  32. package/cli/compiler/common/files/style.ts +52 -0
  33. package/cli/compiler/common/generatedRouteModules.ts +34 -5
  34. package/cli/compiler/common/scripts.ts +11 -5
  35. package/cli/compiler/index.ts +2 -1
  36. package/cli/compiler/server/index.ts +3 -0
  37. package/cli/presentation/commands.ts +136 -7
  38. package/cli/presentation/devSession.ts +32 -7
  39. package/cli/runtime/commands.ts +193 -6
  40. package/cli/scaffold/index.ts +14 -25
  41. package/cli/scaffold/templates.ts +41 -27
  42. package/cli/utils/agents.ts +4 -2
  43. package/cli/utils/keyboard.ts +8 -0
  44. package/client/dev/profiler/ApexChart.tsx +66 -0
  45. package/client/dev/profiler/index.tsx +2798 -417
  46. package/client/dev/profiler/runtime.noop.ts +12 -0
  47. package/client/dev/profiler/runtime.ts +195 -4
  48. package/client/services/router/request/api.ts +6 -1
  49. package/common/applicationConfig.ts +173 -0
  50. package/common/applicationConfigLoader.ts +102 -0
  51. package/common/connectedProjects.ts +113 -0
  52. package/common/dev/connect.ts +267 -0
  53. package/common/dev/console.ts +31 -0
  54. package/common/dev/contractsDoctor.ts +128 -0
  55. package/common/dev/diagnostics.ts +59 -15
  56. package/common/dev/inspection.ts +491 -0
  57. package/common/dev/performance.ts +809 -0
  58. package/common/dev/profiler.ts +3 -0
  59. package/common/dev/proteumManifest.ts +31 -6
  60. package/common/dev/requestTrace.ts +56 -1
  61. package/common/dev/session.ts +24 -0
  62. package/common/env/proteumEnv.ts +176 -50
  63. package/common/router/index.ts +1 -0
  64. package/common/router/request/api.ts +2 -0
  65. package/config.ts +5 -0
  66. package/docs/dev-commands.md +5 -1
  67. package/docs/dev-sessions.md +90 -0
  68. package/docs/diagnostics.md +74 -11
  69. package/docs/request-tracing.md +50 -3
  70. package/package.json +1 -1
  71. package/server/app/container/config.ts +16 -87
  72. package/server/app/container/console/index.ts +42 -8
  73. package/server/app/container/index.ts +3 -1
  74. package/server/app/container/trace/index.ts +153 -0
  75. package/server/app/devDiagnostics.ts +138 -0
  76. package/server/app/index.ts +18 -8
  77. package/server/app/service/container.ts +0 -12
  78. package/server/app/service/index.ts +0 -2
  79. package/server/services/prisma/index.ts +121 -4
  80. package/server/services/router/http/index.ts +352 -0
  81. package/server/services/router/index.ts +50 -47
  82. package/server/services/router/request/api.ts +160 -19
  83. package/server/services/router/request/index.ts +8 -0
  84. package/server/services/router/response/index.ts +24 -1
  85. package/server/services/router/response/page/document.tsx +5 -0
  86. package/server/services/router/response/page/index.tsx +10 -0
  87. package/agents/framework/AGENTS.md +0 -177
  88. package/server/services/auth/router/service.json +0 -6
  89. package/server/services/auth/service.json +0 -6
  90. package/server/services/cron/service.json +0 -6
  91. package/server/services/disks/drivers/local/service.json +0 -6
  92. package/server/services/disks/drivers/s3/service.json +0 -6
  93. package/server/services/disks/service.json +0 -6
  94. package/server/services/fetch/service.json +0 -7
  95. package/server/services/prisma/service.json +0 -6
  96. package/server/services/router/service.json +0 -6
  97. package/server/services/schema/router/service.json +0 -6
  98. package/server/services/schema/service.json +0 -6
  99. package/server/services/security/encrypt/aes/service.json +0 -6
@@ -2,70 +2,212 @@ import path from 'path';
2
2
 
3
3
  import app from '../../app';
4
4
  import cli from '../..';
5
- import { generateControllerClientTree, indexControllers, printControllerTree } from '../common/controllers';
5
+ import { indexControllers, printControllerTree, type TControllerFileMeta } from '../common/controllers';
6
6
  import { TProteumManifestController } from '../common/proteumManifest';
7
7
  import writeIfChanged from '../writeIfChanged';
8
+ import { resolveConnectedProjectContracts, writeConnectedProjectContract } from './connectedProjects';
8
9
  import { normalizeAbsolutePath } from './shared';
9
10
 
11
+ const reservedConnectedContextKeys = new Set(['app', 'context', 'request', 'response', 'route', 'api', 'Router']);
12
+
10
13
  const getManifestScopeFromImportPath = (importPath: string) =>
11
14
  importPath.startsWith('@server/controllers/') ? 'framework' : 'app';
12
15
 
13
- export const generateControllerArtifacts = () => {
14
- const controllers = indexControllers([
16
+ const insertTreeLeaf = (tree: Record<string, any>, accessor: string, value: string) => {
17
+ const segments = accessor.split('.').filter(Boolean);
18
+ let cursor = tree;
19
+
20
+ for (let index = 0; index < segments.length; index += 1) {
21
+ const segment = segments[index];
22
+ const isLeaf = index === segments.length - 1;
23
+
24
+ if (isLeaf) {
25
+ cursor[segment] = value;
26
+ return;
27
+ }
28
+
29
+ cursor[segment] = cursor[segment] || {};
30
+ cursor = cursor[segment];
31
+ }
32
+ };
33
+
34
+ const buildLocalControllers = (): TControllerFileMeta[] =>
35
+ indexControllers([
15
36
  { importPrefix: '@server/controllers/', root: path.join(cli.paths.core.root, 'server', 'controllers') },
16
37
  { importPrefix: '@/server/controllers/', root: path.join(app.paths.root, 'server', 'controllers') },
17
38
  ]);
18
- const manifestControllers = controllers.flatMap<TProteumManifestController>((controller) =>
19
- controller.methods.map((method) => ({
20
- className: controller.className,
21
- importPath: controller.importPath,
22
- filepath: normalizeAbsolutePath(controller.filepath),
23
- sourceLocation: method.sourceLocation,
24
- routeBasePath: controller.routeBasePath,
25
- methodName: method.name,
26
- inputCallsCount: method.inputCallsCount,
27
- hasInput: method.inputCallsCount > 0,
28
- routePath: method.routePath,
29
- httpPath: '/api/' + method.routePath,
30
- clientAccessor: method.routePath.split('/').join('.'),
31
- scope: getManifestScopeFromImportPath(controller.importPath),
32
- })),
33
- );
34
- const clientTree = generateControllerClientTree(controllers);
35
39
 
36
- const getControllerLeafMeta = (leaf: string) => {
37
- const meta = JSON.parse(leaf) as {
38
- routePath: string;
39
- importPath: string;
40
- className: string;
41
- methodName: string;
42
- hasInput: boolean;
43
- };
44
- const controllerIndex = controllers.findIndex((controller) => controller.importPath === meta.importPath);
40
+ const assertConnectedProjectNamespaces = (localControllers: TControllerFileMeta[]) => {
41
+ const localTopLevelKeys = new Set<string>();
45
42
 
46
- if (controllerIndex === -1) {
47
- throw new Error(`Unable to find controller import ${meta.importPath} while generating controller types.`);
43
+ for (const controller of localControllers) {
44
+ for (const method of controller.methods) {
45
+ const topLevelKey = method.routePath.split('/')[0];
46
+ if (topLevelKey) localTopLevelKeys.add(topLevelKey);
48
47
  }
48
+ }
49
49
 
50
- return { ...meta, controllerIndex };
51
- };
50
+ for (const namespace of Object.keys(app.connectedProjects)) {
51
+ if (reservedConnectedContextKeys.has(namespace)) {
52
+ throw new Error(`Connected project namespace "${namespace}" collides with a reserved route context key.`);
53
+ }
54
+
55
+ if (localTopLevelKeys.has(namespace)) {
56
+ throw new Error(`Connected project namespace "${namespace}" collides with an existing local controller root.`);
57
+ }
58
+ }
59
+ };
60
+
61
+ export const generateControllerArtifacts = async () => {
62
+ const localControllers = buildLocalControllers();
63
+ assertConnectedProjectNamespaces(localControllers);
64
+ writeConnectedProjectContract(localControllers);
65
+
66
+ const connectedProjectContracts = await resolveConnectedProjectContracts(app.connectedProjects);
67
+ const manifestControllers: TProteumManifestController[] = [];
68
+ const runtimeTree: Record<string, any> = {};
69
+ const typeTree: Record<string, any> = {};
70
+ const typeImports: string[] = [];
71
+
72
+ localControllers.forEach((controller, index) => {
73
+ typeImports.push(`import type Controller${index} from ${JSON.stringify(controller.importPath)};`);
74
+
75
+ controller.methods.forEach((method) => {
76
+ const resultType = `TControllerResult<Controller${index}, ${JSON.stringify(method.name)}>`;
77
+ const clientAccessor = method.routePath.split('/').join('.');
78
+
79
+ manifestControllers.push({
80
+ className: controller.className,
81
+ importPath: controller.importPath,
82
+ filepath: normalizeAbsolutePath(controller.filepath),
83
+ sourceLocation: method.sourceLocation,
84
+ routeBasePath: controller.routeBasePath,
85
+ methodName: method.name,
86
+ inputCallsCount: method.inputCallsCount,
87
+ hasInput: method.inputCallsCount > 0,
88
+ routePath: method.routePath,
89
+ httpPath: '/api/' + method.routePath,
90
+ clientAccessor,
91
+ scope: getManifestScopeFromImportPath(controller.importPath),
92
+ });
93
+
94
+ insertTreeLeaf(
95
+ runtimeTree,
96
+ clientAccessor,
97
+ JSON.stringify({
98
+ connected: undefined,
99
+ hasInput: method.inputCallsCount > 0,
100
+ httpPath: '/api/' + method.routePath,
101
+ methodName: method.name,
102
+ resultType,
103
+ typeName: `Controller${index}`,
104
+ }),
105
+ );
106
+
107
+ insertTreeLeaf(
108
+ typeTree,
109
+ clientAccessor,
110
+ JSON.stringify({
111
+ hasInput: method.inputCallsCount > 0,
112
+ methodName: method.name,
113
+ typeName: `Controller${index}`,
114
+ }),
115
+ );
116
+ });
117
+ });
118
+
119
+ const connectedControllerTypeImports: string[] = [];
120
+ const connectedManifestControllers: TProteumManifestController[] = [];
121
+
122
+ connectedProjectContracts.forEach(({ namespace, cachedContractFilepath, contract, sourceKind, sourceValue, typeImportModuleSpecifier, typingMode }) => {
123
+ if (typingMode === 'local-typed' && typeImportModuleSpecifier) {
124
+ const typeName = `ConnectedControllers_${namespace.replace(/[^A-Za-z0-9_$]+/g, '_')}`;
125
+ connectedControllerTypeImports.push(
126
+ `import type { TConnectedControllers as ${typeName} } from ${JSON.stringify(typeImportModuleSpecifier)};`,
127
+ );
128
+ typeTree[namespace] = JSON.stringify({ rawType: typeName });
129
+ } else {
130
+ typeTree[namespace] = JSON.stringify({ runtimeOnly: true });
131
+ }
132
+
133
+ contract.controllers.forEach((controller) => {
134
+ const clientAccessor = `${namespace}.${controller.clientAccessor}`;
135
+ connectedManifestControllers.push({
136
+ className: controller.className,
137
+ importPath: `connected:${namespace}/${controller.importPath}`,
138
+ filepath:
139
+ sourceKind === 'file'
140
+ ? normalizeAbsolutePath(path.join(sourceValue, controller.relativeFilepath))
141
+ : cachedContractFilepath,
142
+ sourceLocation: controller.sourceLocation,
143
+ routeBasePath: controller.routeBasePath,
144
+ methodName: controller.methodName,
145
+ inputCallsCount: controller.inputCallsCount,
146
+ hasInput: controller.hasInput,
147
+ routePath: controller.routePath,
148
+ httpPath: controller.httpPath,
149
+ clientAccessor,
150
+ scope: 'connected',
151
+ connectedProjectNamespace: namespace,
152
+ connectedProjectIdentifier: contract.identity.identifier,
153
+ });
154
+
155
+ insertTreeLeaf(
156
+ runtimeTree,
157
+ clientAccessor,
158
+ JSON.stringify({
159
+ connected: {
160
+ controllerAccessor: controller.clientAccessor,
161
+ httpPath: controller.httpPath,
162
+ namespace,
163
+ },
164
+ hasInput: controller.hasInput,
165
+ httpPath: controller.httpPath,
166
+ methodName: controller.methodName,
167
+ resultType: 'unknown',
168
+ }),
169
+ );
170
+ });
171
+ });
52
172
 
53
173
  const runtimeLeaf = (leaf: string) => {
54
- const meta = getControllerLeafMeta(leaf);
55
- const resultType = `TControllerResult<Controller${meta.controllerIndex}, ${JSON.stringify(meta.methodName)}>`;
174
+ const meta = JSON.parse(leaf) as {
175
+ connected?: {
176
+ controllerAccessor: string;
177
+ httpPath: string;
178
+ namespace: string;
179
+ };
180
+ hasInput: boolean;
181
+ httpPath: string;
182
+ resultType: string;
183
+ };
184
+
185
+ const connectedOptions = meta.connected
186
+ ? `, { connected: ${JSON.stringify(meta.connected)} }`
187
+ : '';
56
188
 
57
189
  return meta.hasInput
58
- ? `(data) => api.createFetcher<${resultType}>('POST', ${JSON.stringify(meta.routePath)}, data)`
59
- : `() => api.createFetcher<${resultType}>('POST', ${JSON.stringify(meta.routePath)})`;
190
+ ? `(data) => api.createFetcher<${meta.resultType}>('POST', ${JSON.stringify(meta.httpPath)}, data${connectedOptions})`
191
+ : `() => api.createFetcher<${meta.resultType}>('POST', ${JSON.stringify(meta.httpPath)}, undefined${connectedOptions})`;
60
192
  };
61
193
 
62
- const typeImports = controllers
63
- .map((controller, index) => `import type Controller${index} from ${JSON.stringify(controller.importPath)};`)
64
- .join('\n');
65
-
66
194
  const typeLeaf = (leaf: string) => {
67
- const meta = getControllerLeafMeta(leaf);
68
- const fetcherType = `TControllerFetcher<Controller${meta.controllerIndex}, ${JSON.stringify(meta.methodName)}>`;
195
+ const meta = JSON.parse(leaf) as
196
+ | {
197
+ rawType: string;
198
+ }
199
+ | {
200
+ runtimeOnly: true;
201
+ }
202
+ | {
203
+ hasInput: boolean;
204
+ methodName: string;
205
+ typeName: string;
206
+ };
207
+
208
+ if ('rawType' in meta) return meta.rawType;
209
+ if ('runtimeOnly' in meta) return 'any';
210
+ const fetcherType = `TControllerFetcher<${meta.typeName}, ${JSON.stringify(meta.methodName)}>`;
69
211
 
70
212
  return meta.hasInput ? `(data: any) => ${fetcherType}` : `() => ${fetcherType}`;
71
213
  };
@@ -79,19 +221,19 @@ export const generateControllerArtifacts = () => {
79
221
 
80
222
  import type ApiClient from '@common/router/request/api';
81
223
  import type { TFetcher } from '@common/router/request/api';
82
- ${typeImports ? '\n' + typeImports : ''}
224
+ ${[...typeImports, ...connectedControllerTypeImports].join('\n') ? '\n' + [...typeImports, ...connectedControllerTypeImports].join('\n') : ''}
83
225
 
84
226
  type TControllerResult<TController, TMethod extends keyof TController> =
85
227
  TController[TMethod] extends (...args: any[]) => infer TResult ? Awaited<TResult> : never;
86
228
 
87
229
  type TControllerFetcher<TController, TMethod extends keyof TController> = TFetcher<TControllerResult<TController, TMethod>>;
88
230
 
89
- export type TControllers = ${printControllerTree(clientTree, typeLeaf)};
231
+ export type TControllers = ${printControllerTree(typeTree, typeLeaf)};
90
232
 
91
233
  export const createControllers = (
92
234
  api: Pick<ApiClient, 'createFetcher'>
93
235
  ): TControllers => (
94
- ${printControllerTree(clientTree, runtimeLeaf)}
236
+ ${printControllerTree(runtimeTree, runtimeLeaf)}
95
237
  );
96
238
 
97
239
  export default createControllers;
@@ -106,14 +248,16 @@ export type { TControllers } from '@generated/common/controllers';
106
248
  `,
107
249
  );
108
250
 
109
- const controllerImports = controllers
251
+ const controllerImports = localControllers
110
252
  .map((controller, index) => `import Controller${index} from ${JSON.stringify(controller.importPath)};`)
111
253
  .join('\n');
112
254
 
113
- const controllerEntries = controllers.flatMap((controller, controllerIndex) =>
255
+ const controllerEntries = localControllers.flatMap((controller, controllerIndex) =>
114
256
  controller.methods.map(
115
257
  (method) => ` {
116
258
  path: ${JSON.stringify('/api/' + method.routePath)},
259
+ filepath: ${JSON.stringify(normalizeAbsolutePath(controller.filepath))},
260
+ sourceLocation: { line: ${method.sourceLocation.line}, column: ${method.sourceLocation.column} },
117
261
  Controller: Controller${controllerIndex},
118
262
  method: ${JSON.stringify(method.name)},
119
263
  },`,
@@ -134,6 +278,8 @@ ${controllerImports ? '\n' + controllerImports : ''}
134
278
 
135
279
  export type TGeneratedControllerDefinition = {
136
280
  path: string,
281
+ filepath: string,
282
+ sourceLocation: { line: number, column: number },
137
283
  Controller: new (request: any) => Controller,
138
284
  method: string,
139
285
  }
@@ -146,5 +292,8 @@ export default controllers;
146
292
  `,
147
293
  );
148
294
 
149
- return manifestControllers;
295
+ return {
296
+ connectedProjects: connectedProjectContracts,
297
+ controllers: [...manifestControllers, ...connectedManifestControllers],
298
+ };
150
299
  };
@@ -5,40 +5,6 @@ import ts from 'typescript';
5
5
  import app from '../../app';
6
6
  import { normalizePath } from './shared';
7
7
 
8
- const ignoredServiceDirectories = new Set(['node_modules', 'proteum']);
9
-
10
- export const findServiceDirectories = (dir: string): string[] => {
11
- if (!fs.existsSync(dir)) return [];
12
-
13
- const directories: string[] = [];
14
-
15
- for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
16
- const filePath = path.resolve(dir, dirent.name);
17
-
18
- if (ignoredServiceDirectories.has(dirent.name)) continue;
19
-
20
- let shouldTraverse = false;
21
-
22
- if (dirent.isSymbolicLink()) {
23
- const realPath = path.resolve(dir, fs.readlinkSync(filePath));
24
- shouldTraverse = fs.lstatSync(realPath).isDirectory();
25
- } else if (dirent.isDirectory()) {
26
- shouldTraverse = true;
27
- }
28
-
29
- if (shouldTraverse) {
30
- directories.push(...findServiceDirectories(filePath));
31
- continue;
32
- }
33
-
34
- if (dirent.name === 'service.json') {
35
- directories.push(path.dirname(filePath));
36
- }
37
- }
38
-
39
- return directories;
40
- };
41
-
42
8
  const hasRegisteredRouteDefinitions = (filepath: string, content: string) => {
43
9
  const sourceFile = ts.createSourceFile(
44
10
  filepath,
@@ -1,14 +1,14 @@
1
+ import fs from 'fs';
1
2
  import path from 'path';
2
3
 
3
4
  import app from '../../app';
4
5
  import cli from '../..';
5
- import {
6
- inspectProteumEnv,
7
- } from '../../../common/env/proteumEnv';
6
+ import { inspectProteumEnv } from '../../../common/env/proteumEnv';
8
7
  import { reservedRouteSetupKeys, routeSetupOptionKeys } from '../../../common/router/pageSetup';
9
8
  import {
10
9
  TProteumManifest,
11
10
  TProteumManifestCommand,
11
+ TProteumManifestConnectedProject,
12
12
  TProteumManifestController,
13
13
  TProteumManifestDiagnostic,
14
14
  TProteumManifestLayout,
@@ -16,6 +16,18 @@ import {
16
16
  } from '../common/proteumManifest';
17
17
  import { writeProteumManifest } from '../common/proteumManifest';
18
18
  import { normalizeAbsolutePath, normalizePath } from './shared';
19
+ import type { TResolvedConnectedProjectContract } from './connectedProjects';
20
+
21
+ const requiredGitignoreEntries = [
22
+ '/.proteum',
23
+ '/bin',
24
+ '/dev',
25
+ '/.cache',
26
+ '/var',
27
+ '/proteum.connected.json',
28
+ ] as const;
29
+
30
+ const normalizeGitignoreEntry = (value: string) => value.trim().replace(/\\/g, '/').replace(/^\/+/, '').replace(/\/+$/, '');
19
31
 
20
32
  const collectManifestDiagnostics = ({
21
33
  commands,
@@ -206,6 +218,37 @@ const collectManifestDiagnostics = ({
206
218
  });
207
219
  }
208
220
 
221
+ const gitignoreFilepath = path.join(app.paths.root, '.gitignore');
222
+
223
+ if (!fs.existsSync(gitignoreFilepath)) {
224
+ pushDiagnostic({
225
+ level: 'warning',
226
+ code: 'app.gitignore-missing',
227
+ message: `Missing .gitignore. Proteum generated output should ignore ${requiredGitignoreEntries.join(', ')}.`,
228
+ filepath: gitignoreFilepath,
229
+ });
230
+ } else {
231
+ const entries = new Set(
232
+ fs.readFileSync(gitignoreFilepath, 'utf8')
233
+ .split(/\r?\n/)
234
+ .map((line) => line.replace(/#.*/, '').trim())
235
+ .filter(Boolean)
236
+ .map(normalizeGitignoreEntry),
237
+ );
238
+
239
+ for (const requiredEntry of requiredGitignoreEntries) {
240
+ const normalizedRequiredEntry = normalizeGitignoreEntry(requiredEntry);
241
+ if (entries.has(normalizedRequiredEntry)) continue;
242
+
243
+ pushDiagnostic({
244
+ level: 'warning',
245
+ code: 'app.gitignore-generated-entry-missing',
246
+ message: `Add "${requiredEntry}" to .gitignore so Proteum generated output stays untracked.`,
247
+ filepath: gitignoreFilepath,
248
+ });
249
+ }
250
+ }
251
+
209
252
  return diagnostics.sort((a, b) => {
210
253
  if (a.level !== b.level) return a.level === 'error' ? -1 : 1;
211
254
  if (a.filepath !== b.filepath) return a.filepath.localeCompare(b.filepath);
@@ -218,25 +261,49 @@ const collectManifestDiagnostics = ({
218
261
 
219
262
  export const writeCurrentProteumManifest = ({
220
263
  services,
264
+ connectedProjects: resolvedConnectedProjects,
221
265
  controllers,
222
266
  commands,
223
267
  routes,
224
268
  layouts,
225
269
  }: {
226
270
  services: TProteumManifest['services'];
271
+ connectedProjects: TResolvedConnectedProjectContract[];
227
272
  controllers: TProteumManifestController[];
228
273
  commands: TProteumManifestCommand[];
229
274
  routes: TProteumManifest['routes'];
230
275
  layouts: TProteumManifestLayout[];
231
276
  }) => {
232
- const envInspection = inspectProteumEnv(app.paths.root);
277
+ const envInspection = inspectProteumEnv(app.paths.root, app.connectedProjects);
278
+ const connectedProjects: TProteumManifestConnectedProject[] = Object.entries(app.connectedProjects)
279
+ .sort(([left], [right]) => left.localeCompare(right))
280
+ .map(([namespace, config]) => {
281
+ const connectedControllers = controllers.filter((controller) => controller.connectedProjectNamespace === namespace);
282
+ const connectedEnv = app.env.connectedProjects[namespace];
283
+ const resolvedConnectedProject = resolvedConnectedProjects.find((connectedProject) => connectedProject.namespace === namespace);
284
+ const contract = resolvedConnectedProject?.contract;
285
+
286
+ return {
287
+ namespace,
288
+ packageName: contract?.packageName,
289
+ identityIdentifier: contract?.identity.identifier || connectedControllers[0]?.connectedProjectIdentifier,
290
+ identityName: contract?.identity.name,
291
+ sourceKind: resolvedConnectedProject?.sourceKind,
292
+ sourceValue: resolvedConnectedProject?.sourceValue || config.source,
293
+ cachedContractFilepath: resolvedConnectedProject?.cachedContractFilepath,
294
+ typingMode: resolvedConnectedProject?.typingMode,
295
+ urlInternal: connectedEnv?.urlInternal || config.urlInternal,
296
+ controllerCount: connectedControllers.length,
297
+ };
298
+ });
233
299
 
234
300
  const manifest: TProteumManifest = {
235
- version: 2,
301
+ version: 9,
236
302
  app: {
237
303
  root: normalizeAbsolutePath(app.paths.root),
238
304
  coreRoot: normalizeAbsolutePath(cli.paths.core.root),
239
- identityFilepath: normalizeAbsolutePath(path.join(app.paths.root, 'identity.yaml')),
305
+ identityFilepath: normalizeAbsolutePath(path.join(app.paths.root, 'identity.config.ts')),
306
+ setupFilepath: normalizeAbsolutePath(path.join(app.paths.root, 'proteum.config.ts')),
240
307
  identity: {
241
308
  name: app.identity.name,
242
309
  identifier: app.identity.identifier,
@@ -249,6 +316,21 @@ export const writeCurrentProteumManifest = ({
249
316
  webDescription: app.identity.web?.description,
250
317
  version: app.identity.web?.version,
251
318
  },
319
+ setup: {
320
+ transpile: app.transpile.length > 0 ? [...app.transpile] : undefined,
321
+ connect:
322
+ Object.keys(app.connectedProjects).length > 0
323
+ ? Object.fromEntries(
324
+ Object.entries(app.connectedProjects).map(([namespace, config]) => [
325
+ namespace,
326
+ {
327
+ ...(config.source ? { source: config.source } : {}),
328
+ ...(config.urlInternal ? { urlInternal: config.urlInternal } : {}),
329
+ },
330
+ ]),
331
+ )
332
+ : undefined,
333
+ },
252
334
  },
253
335
  conventions: {
254
336
  routeSetupOptionKeys: [...routeSetupOptionKeys],
@@ -267,8 +349,10 @@ export const writeCurrentProteumManifest = ({
267
349
  profile: app.env.profile,
268
350
  routerPort: app.env.router.port,
269
351
  routerCurrentDomain: app.env.router.currentDomain,
352
+ routerInternalUrl: app.env.router.internalUrl,
270
353
  },
271
354
  },
355
+ connectedProjects,
272
356
  services,
273
357
  controllers,
274
358
  commands,
@@ -100,7 +100,7 @@ const generateClientRouteWrapperModules = () => {
100
100
  runtime: 'client',
101
101
  side: 'client',
102
102
  sourceFilepath: filepath,
103
- clientRoute: { chunkId: pageChunk.chunkId, filepath: pageChunk.filepath },
103
+ clientRoute: { chunkId: pageChunk.chunkId },
104
104
  routeSourceFilepaths,
105
105
  });
106
106
 
@@ -109,7 +109,7 @@ const generateClientRouteWrapperModules = () => {
109
109
  runtime: 'server',
110
110
  side: 'client',
111
111
  sourceFilepath: filepath,
112
- clientRoute: { chunkId: pageChunk.chunkId, filepath: pageChunk.filepath },
112
+ clientRoute: { chunkId: pageChunk.chunkId },
113
113
  routeSourceFilepaths,
114
114
  });
115
115
  }