proteum 2.4.4 → 2.5.0
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/README.md +60 -55
- package/agents/project/AGENTS.md +112 -31
- package/agents/project/CODING_STYLE.md +2 -2
- package/agents/project/app-root/AGENTS.md +1 -3
- package/agents/project/client/AGENTS.md +1 -1
- package/agents/project/client/pages/AGENTS.md +21 -9
- package/agents/project/diagnostics.md +2 -2
- package/agents/project/optimizations.md +1 -1
- package/agents/project/root/AGENTS.md +105 -22
- package/agents/project/server/routes/AGENTS.md +30 -1
- package/agents/project/tests/AGENTS.md +1 -1
- package/cli/commands/doctor.ts +54 -3
- package/cli/commands/runtime.ts +6 -0
- package/cli/commands/worktree.ts +116 -0
- package/cli/compiler/artifacts/controllers.ts +16 -15
- package/cli/compiler/artifacts/discovery.ts +129 -17
- package/cli/compiler/artifacts/routing.ts +0 -5
- package/cli/compiler/artifacts/services.ts +253 -76
- package/cli/compiler/common/controllers.ts +159 -57
- package/cli/compiler/common/generatedRouteModules.ts +457 -363
- package/cli/mcp/router.ts +47 -3
- package/cli/presentation/commands.ts +25 -15
- package/cli/runtime/commands.ts +39 -12
- package/cli/runtime/worktreeBootstrap.ts +608 -0
- package/cli/scaffold/index.ts +28 -18
- package/cli/scaffold/templates.ts +44 -33
- package/cli/utils/agents.ts +14 -1
- package/client/services/router/index.tsx +23 -3
- package/client/services/router/request/api.ts +14 -4
- package/common/dev/contractsDoctor.ts +1 -1
- package/common/dev/mcpPayloads.ts +8 -1
- package/common/env/proteumEnv.ts +14 -2
- package/common/router/contracts.ts +1 -1
- package/common/router/definitions.ts +177 -0
- package/common/router/index.ts +23 -12
- package/common/router/pageData.ts +5 -5
- package/common/router/register.ts +2 -2
- package/common/router/request/api.ts +12 -2
- package/docs/agent-routing.md +5 -2
- package/docs/diagnostics.md +2 -0
- package/docs/mcp.md +6 -3
- package/eslint.js +36 -1
- package/package.json +1 -1
- package/server/app/commands.ts +5 -1
- package/server/app/container/console/index.ts +1 -1
- package/server/app/controller/index.ts +98 -40
- package/server/app/index.ts +92 -1
- package/server/app/service/index.ts +5 -1
- package/server/index.ts +6 -2
- package/server/services/router/index.ts +47 -38
- package/server/services/router/response/index.ts +2 -2
- package/tests/agents-utils.test.cjs +14 -1
- package/tests/cli-mcp-command.test.cjs +84 -0
- package/tests/definition-contracts.test.cjs +453 -0
- package/tests/dev-transpile-watch.test.cjs +37 -28
- package/tests/eslint-rules.test.cjs +39 -1
- package/tests/mcp.test.cjs +90 -0
- package/tests/worktree-bootstrap.test.cjs +206 -0
- package/types/aliases.d.ts +0 -5
- package/types/controller-input.test.ts +23 -17
- package/types/controller-request-context.test.ts +10 -11
- package/cli/commands/migrate.ts +0 -51
- package/cli/migrate/pageContract.ts +0 -516
- package/docs/migrate-from-2.1.3.md +0 -396
- package/scripts/cleanup-generated-controllers.ts +0 -62
- package/scripts/fix-reference-app-typing.ts +0 -490
- package/scripts/format-router-registrations.ts +0 -119
- package/scripts/migrate-explicit-controllers-and-request.ts +0 -423
- package/scripts/refactor-client-app-imports.ts +0 -244
- package/scripts/refactor-client-pages.ts +0 -587
- package/scripts/refactor-server-controllers.ts +0 -471
- package/scripts/refactor-server-runtime-aliases.ts +0 -360
- package/scripts/restore-client-app-import-files.ts +0 -41
|
@@ -50,6 +50,11 @@ type TResolvedImportSource = {
|
|
|
50
50
|
sourceFilepath?: string;
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
type TObjectLiteralFactoryDetails = {
|
|
54
|
+
object: ts.ObjectLiteralExpression;
|
|
55
|
+
localInitializers: Map<string, ts.Expression>;
|
|
56
|
+
};
|
|
57
|
+
|
|
53
58
|
const coreServicesRoot = normalizeAbsolutePath(path.join(cli.paths.core.root, 'server', 'services'));
|
|
54
59
|
const appServicesRoot = normalizeAbsolutePath(path.join(app.paths.root, 'server', 'services'));
|
|
55
60
|
const coreServerRoot = normalizeAbsolutePath(path.join(cli.paths.core.root, 'server'));
|
|
@@ -111,7 +116,7 @@ const getDefaultExportClassDeclaration = (sourceFile: ts.SourceFile) => {
|
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
if (!defaultExportIdentifier) {
|
|
114
|
-
throw new Error(`Expected ${sourceFile.fileName} to default-export
|
|
119
|
+
throw new Error(`Expected ${sourceFile.fileName} to default-export a class.`);
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
const declaration = sourceFile.statements.find(
|
|
@@ -121,7 +126,7 @@ const getDefaultExportClassDeclaration = (sourceFile: ts.SourceFile) => {
|
|
|
121
126
|
|
|
122
127
|
if (!declaration) {
|
|
123
128
|
throw new Error(
|
|
124
|
-
`Unable to resolve the default-exported
|
|
129
|
+
`Unable to resolve the default-exported class "${defaultExportIdentifier}" in ${sourceFile.fileName}.`,
|
|
125
130
|
);
|
|
126
131
|
}
|
|
127
132
|
|
|
@@ -251,6 +256,125 @@ const getObjectLiteralProperty = (expression: ts.ObjectLiteralExpression, proper
|
|
|
251
256
|
return getPropertyNameText(property.name) === propertyName;
|
|
252
257
|
});
|
|
253
258
|
|
|
259
|
+
const collectConstInitializers = (sourceFile: ts.SourceFile) => {
|
|
260
|
+
const initializers = new Map<string, ts.Expression>();
|
|
261
|
+
|
|
262
|
+
for (const statement of sourceFile.statements) {
|
|
263
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
264
|
+
if (!(statement.declarationList.flags & ts.NodeFlags.Const)) continue;
|
|
265
|
+
|
|
266
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
267
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
268
|
+
|
|
269
|
+
initializers.set(declaration.name.text, declaration.initializer);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return initializers;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const resolveLocalIdentifierExpression = (
|
|
277
|
+
expression: ts.Expression,
|
|
278
|
+
constInitializers: Map<string, ts.Expression>,
|
|
279
|
+
) => {
|
|
280
|
+
const unwrappedExpression = unwrapExpression(expression);
|
|
281
|
+
if (!ts.isIdentifier(unwrappedExpression)) return unwrappedExpression;
|
|
282
|
+
|
|
283
|
+
return constInitializers.get(unwrappedExpression.text) || unwrappedExpression;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const getDefaultExportExpression = (sourceFile: ts.SourceFile) => {
|
|
287
|
+
const constInitializers = collectConstInitializers(sourceFile);
|
|
288
|
+
|
|
289
|
+
for (const statement of sourceFile.statements) {
|
|
290
|
+
if (!ts.isExportAssignment(statement) || statement.isExportEquals) continue;
|
|
291
|
+
|
|
292
|
+
return resolveLocalIdentifierExpression(statement.expression, constInitializers);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return undefined;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const getCallExpressionName = (expression: ts.Expression) => {
|
|
299
|
+
if (ts.isIdentifier(expression)) return expression.text;
|
|
300
|
+
if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
|
|
301
|
+
return undefined;
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const getObjectLiteralFromFactory = (expression: ts.Expression): ts.ObjectLiteralExpression | undefined => {
|
|
305
|
+
return getObjectLiteralFactoryDetails(expression)?.object;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const getExpressionFromFactory = (expression: ts.Expression): ts.Expression => {
|
|
309
|
+
const unwrappedExpression = unwrapExpression(expression);
|
|
310
|
+
|
|
311
|
+
if (ts.isArrowFunction(unwrappedExpression) || ts.isFunctionExpression(unwrappedExpression)) {
|
|
312
|
+
const body = unwrappedExpression.body;
|
|
313
|
+
if (!ts.isBlock(body)) return unwrapExpression(body);
|
|
314
|
+
|
|
315
|
+
for (const statement of body.statements) {
|
|
316
|
+
if (!ts.isReturnStatement(statement) || !statement.expression) continue;
|
|
317
|
+
|
|
318
|
+
return unwrapExpression(statement.expression);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return unwrappedExpression;
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const getObjectLiteralFactoryDetails = (expression: ts.Expression): TObjectLiteralFactoryDetails | undefined => {
|
|
326
|
+
const unwrappedExpression = unwrapExpression(expression);
|
|
327
|
+
|
|
328
|
+
if (ts.isObjectLiteralExpression(unwrappedExpression)) {
|
|
329
|
+
return { object: unwrappedExpression, localInitializers: new Map() };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (ts.isArrowFunction(unwrappedExpression) || ts.isFunctionExpression(unwrappedExpression)) {
|
|
333
|
+
const body = unwrapExpression(unwrappedExpression.body as ts.Expression);
|
|
334
|
+
|
|
335
|
+
if (ts.isObjectLiteralExpression(body)) {
|
|
336
|
+
return { object: body, localInitializers: new Map() };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (ts.isBlock(unwrappedExpression.body)) {
|
|
340
|
+
const localInitializers = new Map<string, ts.Expression>();
|
|
341
|
+
|
|
342
|
+
for (const statement of unwrappedExpression.body.statements) {
|
|
343
|
+
if (ts.isVariableStatement(statement) && statement.declarationList.flags & ts.NodeFlags.Const) {
|
|
344
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
345
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
346
|
+
|
|
347
|
+
localInitializers.set(declaration.name.text, declaration.initializer);
|
|
348
|
+
}
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (!ts.isReturnStatement(statement) || !statement.expression) continue;
|
|
353
|
+
|
|
354
|
+
const returnExpression = unwrapExpression(statement.expression);
|
|
355
|
+
if (ts.isObjectLiteralExpression(returnExpression)) {
|
|
356
|
+
return { object: returnExpression, localInitializers };
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return undefined;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const getDefaultDefineApplicationObject = (sourceFile: ts.SourceFile) => {
|
|
366
|
+
const expression = getDefaultExportExpression(sourceFile);
|
|
367
|
+
if (!expression || !ts.isCallExpression(expression)) return undefined;
|
|
368
|
+
if (getCallExpressionName(expression.expression) !== 'defineApplication') return undefined;
|
|
369
|
+
|
|
370
|
+
const [definitionArg] = [...expression.arguments];
|
|
371
|
+
if (!definitionArg || !ts.isObjectLiteralExpression(definitionArg)) {
|
|
372
|
+
throw new Error(`defineApplication(...) in ${sourceFile.fileName} must receive an object literal.`);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return definitionArg;
|
|
376
|
+
};
|
|
377
|
+
|
|
254
378
|
const readNumericLiteral = (expression: ts.Expression) => {
|
|
255
379
|
const unwrappedExpression = unwrapExpression(expression);
|
|
256
380
|
|
|
@@ -398,8 +522,13 @@ const parseServiceInstantiation = (
|
|
|
398
522
|
imports: Map<string, TImportedBinding>,
|
|
399
523
|
sourceFilepath: string,
|
|
400
524
|
configArgIndex: number,
|
|
525
|
+
localInitializers = new Map<string, ts.Expression>(),
|
|
401
526
|
): TParsedService | undefined => {
|
|
402
|
-
const
|
|
527
|
+
const factoryExpression = getExpressionFromFactory(expression);
|
|
528
|
+
const unwrappedExpression =
|
|
529
|
+
ts.isIdentifier(factoryExpression) && localInitializers.has(factoryExpression.text)
|
|
530
|
+
? getExpressionFromFactory(localInitializers.get(factoryExpression.text)!)
|
|
531
|
+
: factoryExpression;
|
|
403
532
|
if (!ts.isNewExpression(unwrappedExpression)) return undefined;
|
|
404
533
|
|
|
405
534
|
const resolvedService = resolveImportedService(unwrappedExpression.expression, imports, sourceFilepath);
|
|
@@ -447,40 +576,122 @@ const extractRouterPlugins = (
|
|
|
447
576
|
return routerPlugins;
|
|
448
577
|
};
|
|
449
578
|
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
579
|
+
const parseRootServiceObject = (
|
|
580
|
+
servicesObject: ts.ObjectLiteralExpression | undefined,
|
|
581
|
+
imports: Map<string, TImportedBinding>,
|
|
582
|
+
sourceFilepath: string,
|
|
583
|
+
localInitializers = new Map<string, ts.Expression>(),
|
|
584
|
+
) => {
|
|
585
|
+
if (!servicesObject) return [];
|
|
454
586
|
|
|
455
587
|
const rootServices: TParsedService[] = [];
|
|
456
|
-
let routerPlugins: TParsedService[] = [];
|
|
457
588
|
|
|
458
|
-
for (const
|
|
459
|
-
if (!ts.
|
|
589
|
+
for (const property of servicesObject.properties) {
|
|
590
|
+
if (!ts.isPropertyAssignment(property)) continue;
|
|
460
591
|
|
|
461
|
-
const registeredName = getPropertyNameText(
|
|
592
|
+
const registeredName = getPropertyNameText(property.name);
|
|
462
593
|
if (!registeredName) continue;
|
|
463
594
|
|
|
464
|
-
const
|
|
595
|
+
const initializer =
|
|
596
|
+
ts.isIdentifier(unwrapExpression(property.initializer)) &&
|
|
597
|
+
localInitializers.has((unwrapExpression(property.initializer) as ts.Identifier).text)
|
|
598
|
+
? localInitializers.get((unwrapExpression(property.initializer) as ts.Identifier).text)!
|
|
599
|
+
: property.initializer;
|
|
600
|
+
const rootService = parseServiceInstantiation(registeredName, initializer, imports, sourceFilepath, 1);
|
|
465
601
|
if (!rootService) continue;
|
|
466
602
|
|
|
467
603
|
rootServices.push(rootService);
|
|
604
|
+
}
|
|
468
605
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (!ts.isNewExpression(initializer)) continue;
|
|
606
|
+
return rootServices;
|
|
607
|
+
};
|
|
472
608
|
|
|
609
|
+
const parseDefineApplicationBootstrap = (
|
|
610
|
+
sourceFile: ts.SourceFile,
|
|
611
|
+
imports: Map<string, TImportedBinding>,
|
|
612
|
+
): TParsedAppBootstrap | undefined => {
|
|
613
|
+
const topLevelInitializers = collectConstInitializers(sourceFile);
|
|
614
|
+
const definitionArg = getDefaultDefineApplicationObject(sourceFile);
|
|
615
|
+
if (!definitionArg) return undefined;
|
|
616
|
+
|
|
617
|
+
const servicesProperty = getObjectLiteralProperty(definitionArg, 'services');
|
|
618
|
+
const servicesDetails =
|
|
619
|
+
servicesProperty && ts.isPropertyAssignment(servicesProperty)
|
|
620
|
+
? getObjectLiteralFactoryDetails(servicesProperty.initializer)
|
|
621
|
+
: undefined;
|
|
622
|
+
const servicesObject = servicesDetails?.object;
|
|
623
|
+
const localServiceInitializers = servicesDetails?.localInitializers || new Map<string, ts.Expression>();
|
|
624
|
+
const rootServices = parseRootServiceObject(servicesObject, imports, sourceFile.fileName, localServiceInitializers);
|
|
625
|
+
const routerProperty = getObjectLiteralProperty(definitionArg, 'router');
|
|
626
|
+
|
|
627
|
+
if (routerProperty && ts.isPropertyAssignment(routerProperty)) {
|
|
628
|
+
const routerService = parseServiceInstantiation(
|
|
629
|
+
'Router',
|
|
630
|
+
routerProperty.initializer,
|
|
631
|
+
imports,
|
|
632
|
+
sourceFile.fileName,
|
|
633
|
+
1,
|
|
634
|
+
topLevelInitializers,
|
|
635
|
+
);
|
|
636
|
+
if (routerService && !rootServices.some((service) => service.registeredName === 'Router')) {
|
|
637
|
+
rootServices.push(routerService);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const routerService = rootServices.find((rootService) => rootService.importPath === '@server/services/router');
|
|
642
|
+
let routerPlugins: TParsedService[] = [];
|
|
643
|
+
|
|
644
|
+
if (routerService?.registeredName === 'Router') {
|
|
645
|
+
const routerExpression =
|
|
646
|
+
servicesObject &&
|
|
647
|
+
servicesObject.properties
|
|
648
|
+
.filter((property): property is ts.PropertyAssignment => ts.isPropertyAssignment(property))
|
|
649
|
+
.find((property) => getPropertyNameText(property.name) === 'Router')?.initializer;
|
|
650
|
+
|
|
651
|
+
const routerInitializer =
|
|
652
|
+
routerExpression ||
|
|
653
|
+
(routerProperty && ts.isPropertyAssignment(routerProperty) ? routerProperty.initializer : undefined);
|
|
654
|
+
const unwrappedRouterInitializer = routerInitializer ? getExpressionFromFactory(routerInitializer) : undefined;
|
|
655
|
+
const initializer =
|
|
656
|
+
unwrappedRouterInitializer &&
|
|
657
|
+
ts.isIdentifier(unwrappedRouterInitializer) &&
|
|
658
|
+
(localServiceInitializers.has(unwrappedRouterInitializer.text) ||
|
|
659
|
+
topLevelInitializers.has(unwrappedRouterInitializer.text))
|
|
660
|
+
? getExpressionFromFactory(
|
|
661
|
+
(localServiceInitializers.get(unwrappedRouterInitializer.text) ||
|
|
662
|
+
topLevelInitializers.get(unwrappedRouterInitializer.text))!,
|
|
663
|
+
)
|
|
664
|
+
: unwrappedRouterInitializer;
|
|
665
|
+
|
|
666
|
+
if (initializer && ts.isNewExpression(initializer)) {
|
|
473
667
|
routerPlugins = extractRouterPlugins(initializer.arguments?.[1], imports, sourceFile.fileName);
|
|
474
668
|
}
|
|
475
669
|
}
|
|
476
670
|
|
|
477
|
-
|
|
671
|
+
return { rootServices, routerPlugins };
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
export const parseAppBootstrapSource = (sourceFilepath: string): TParsedAppBootstrap => {
|
|
675
|
+
const sourceFile = createSourceFile(sourceFilepath);
|
|
676
|
+
const imports = buildImportIndex(sourceFile);
|
|
677
|
+
const definitionBootstrap = parseDefineApplicationBootstrap(sourceFile, imports);
|
|
678
|
+
|
|
679
|
+
if (!definitionBootstrap) {
|
|
680
|
+
throw new Error(
|
|
681
|
+
`${sourceFile.fileName} must default-export defineApplication({ services, router, models, commands }). ` +
|
|
682
|
+
'Application subclasses are no longer supported.',
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (definitionBootstrap.rootServices.length === 0) {
|
|
478
687
|
throw new Error(`No root services were found in ${sourceFile.fileName}.`);
|
|
479
688
|
}
|
|
480
689
|
|
|
481
|
-
return
|
|
690
|
+
return definitionBootstrap;
|
|
482
691
|
};
|
|
483
692
|
|
|
693
|
+
const parseAppBootstrap = (): TParsedAppBootstrap => parseAppBootstrapSource(getAppServerEntryFilepath());
|
|
694
|
+
|
|
484
695
|
const commandServiceSearchRoots = [
|
|
485
696
|
{ root: coreServicesRoot, prefix: '@server/services/' },
|
|
486
697
|
{ root: appServicesRoot, prefix: '@/server/services/' },
|
|
@@ -652,7 +863,7 @@ const createCommandServiceStubDeclarations = (rootServices: TParsedService[]): T
|
|
|
652
863
|
stubs.set(
|
|
653
864
|
source.aliasImportPath,
|
|
654
865
|
`declare class ${getStubTypeName(source)} {
|
|
655
|
-
app: import("@/server/index").default
|
|
866
|
+
app: InstanceType<typeof import("@/server/index").default>;
|
|
656
867
|
[key: string]: any;
|
|
657
868
|
}`,
|
|
658
869
|
);
|
|
@@ -660,7 +871,7 @@ const createCommandServiceStubDeclarations = (rootServices: TParsedService[]): T
|
|
|
660
871
|
}
|
|
661
872
|
|
|
662
873
|
const className = getStubTypeName(source);
|
|
663
|
-
const classMembers = [` app: import("@/server/index").default
|
|
874
|
+
const classMembers = [` app: InstanceType<typeof import("@/server/index").default>;`];
|
|
664
875
|
|
|
665
876
|
for (const member of defaultClass.members) {
|
|
666
877
|
if (isPrivateOrProtectedInstanceMember(member)) continue;
|
|
@@ -714,12 +925,12 @@ ${classMembers.join('\n')}
|
|
|
714
925
|
const isNamespaceImportDeclaration = (statement: ts.ImportDeclaration) =>
|
|
715
926
|
statement.importClause?.namedBindings !== undefined && ts.isNamespaceImport(statement.importClause.namedBindings);
|
|
716
927
|
|
|
717
|
-
const isClientServerStubSupportStatement = (statement: ts.Statement,
|
|
928
|
+
const isClientServerStubSupportStatement = (statement: ts.Statement, appIdentifier: string) => {
|
|
718
929
|
if (ts.isTypeAliasDeclaration(statement)) {
|
|
719
930
|
return !new Set([
|
|
720
|
-
`${
|
|
721
|
-
`${
|
|
722
|
-
`${
|
|
931
|
+
`${appIdentifier}Disks`,
|
|
932
|
+
`${appIdentifier}Router`,
|
|
933
|
+
`${appIdentifier}RouterPlugins`,
|
|
723
934
|
]).has(statement.name.text);
|
|
724
935
|
}
|
|
725
936
|
|
|
@@ -728,7 +939,6 @@ const isClientServerStubSupportStatement = (statement: ts.Statement, appClassIde
|
|
|
728
939
|
|
|
729
940
|
const createClientServerIndexDeclaration = (rootServices: TParsedService[]) => {
|
|
730
941
|
const sourceFile = createSourceFile(getAppServerEntryFilepath());
|
|
731
|
-
const appClass = getDefaultExportClassDeclaration(sourceFile);
|
|
732
942
|
const serviceTypesByRegisteredName = new Map(
|
|
733
943
|
rootServices.map((service) => [service.registeredName, `import("${service.importPath}").default`] as const),
|
|
734
944
|
);
|
|
@@ -738,26 +948,19 @@ const createClientServerIndexDeclaration = (rootServices: TParsedService[]) => {
|
|
|
738
948
|
.filter((statement) => !isNamespaceImportDeclaration(statement))
|
|
739
949
|
.map((statement) => statement.getText(sourceFile));
|
|
740
950
|
const supportStatements = sourceFile.statements
|
|
741
|
-
.slice(0, sourceFile.statements.indexOf(appClass))
|
|
742
951
|
.filter((statement) => isClientServerStubSupportStatement(statement, app.identity.identifier))
|
|
743
952
|
.map((statement) => statement.getText(sourceFile));
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
.filter((member): member is ts.PropertyDeclaration => ts.isPropertyDeclaration(member))
|
|
747
|
-
.filter((member) => !isPrivateOrProtectedInstanceMember(member))
|
|
748
|
-
.map((property) => {
|
|
749
|
-
const propertyName = getPropertyNameText(property.name);
|
|
750
|
-
if (!propertyName) return undefined;
|
|
751
|
-
|
|
953
|
+
const properties = rootServices
|
|
954
|
+
.map((service) => {
|
|
752
955
|
const propertyType =
|
|
753
|
-
|
|
956
|
+
service.registeredName === 'Disks'
|
|
754
957
|
? 'object'
|
|
755
|
-
:
|
|
958
|
+
: service.registeredName === 'Router'
|
|
756
959
|
? `${app.identity.identifier}Router`
|
|
757
|
-
:
|
|
960
|
+
: serviceTypesByRegisteredName.get(service.registeredName);
|
|
758
961
|
if (!propertyType) return undefined;
|
|
759
962
|
|
|
760
|
-
return ` public ${
|
|
963
|
+
return ` public ${service.registeredName}: ${propertyType};`;
|
|
761
964
|
})
|
|
762
965
|
.filter((property): property is string => property !== undefined);
|
|
763
966
|
|
|
@@ -767,7 +970,7 @@ ${supportStatements.join('\n\n')}
|
|
|
767
970
|
|
|
768
971
|
type ${app.identity.identifier}Router = Router<${app.identity.identifier}>;
|
|
769
972
|
|
|
770
|
-
export default class ${app.identity.identifier}
|
|
973
|
+
export default class ${app.identity.identifier} {
|
|
771
974
|
${properties.join('\n')}
|
|
772
975
|
}
|
|
773
976
|
`;
|
|
@@ -786,7 +989,7 @@ const resolveManifestService = (service: TParsedService, parent: string): TProte
|
|
|
786
989
|
|
|
787
990
|
export const generateServiceArtifacts = () => {
|
|
788
991
|
const { rootServices, routerPlugins } = parseAppBootstrap();
|
|
789
|
-
const
|
|
992
|
+
const appIdentifier = app.identity.identifier;
|
|
790
993
|
const containerServices = app.containerServices.map((serviceName) => "'" + serviceName + "'").join('|');
|
|
791
994
|
const appServices = rootServices.map((service) => resolveManifestService(service, 'app'));
|
|
792
995
|
const routerPluginServices = routerPlugins.map((service) => resolveManifestService(service, 'Router.plugins'));
|
|
@@ -799,15 +1002,7 @@ export const generateServiceArtifacts = () => {
|
|
|
799
1002
|
|
|
800
1003
|
writeIfChanged(
|
|
801
1004
|
path.join(app.paths.client.generated, 'services.d.ts'),
|
|
802
|
-
`declare type ${
|
|
803
|
-
|
|
804
|
-
declare module "@app" {
|
|
805
|
-
|
|
806
|
-
import { ${appClassIdentifier} as ${appClassIdentifier}Client } from "@/client";
|
|
807
|
-
|
|
808
|
-
export const Router: ${appClassIdentifier}Client['Router'];
|
|
809
|
-
|
|
810
|
-
}
|
|
1005
|
+
`declare type ${appIdentifier} = InstanceType<typeof import("@/server/index").default>;
|
|
811
1006
|
|
|
812
1007
|
declare module '@models/types' {
|
|
813
1008
|
export * from '@generated/client/models';
|
|
@@ -841,12 +1036,12 @@ declare namespace preact.JSX {
|
|
|
841
1036
|
|
|
842
1037
|
writeIfChanged(
|
|
843
1038
|
path.join(app.paths.client.generated, 'context.ts'),
|
|
844
|
-
`// TODO: move it into core (but how to make sure usecontext returns ${
|
|
1039
|
+
`// TODO: move it into core (but how to make sure usecontext returns ${appIdentifier}'s context ?)
|
|
845
1040
|
import React from 'react';
|
|
846
1041
|
|
|
847
|
-
import type ${
|
|
1042
|
+
import type ${appIdentifier}Client from '@/client/index';
|
|
848
1043
|
|
|
849
|
-
export type ClientContext = ${
|
|
1044
|
+
export type ClientContext = ${appIdentifier}Client["Router"]["context"];
|
|
850
1045
|
|
|
851
1046
|
type GlobalClientContextStore = typeof globalThis & {
|
|
852
1047
|
__proteumClientContexts?: Record<string, React.Context<ClientContext | undefined>>;
|
|
@@ -856,14 +1051,14 @@ const globalClientContextStore = globalThis as GlobalClientContextStore;
|
|
|
856
1051
|
const clientContexts = (globalClientContextStore.__proteumClientContexts ??= {});
|
|
857
1052
|
|
|
858
1053
|
export const ReactClientContext =
|
|
859
|
-
clientContexts['${
|
|
1054
|
+
clientContexts['${appIdentifier}'] ?? (clientContexts['${appIdentifier}'] = React.createContext<ClientContext | undefined>(undefined));
|
|
860
1055
|
export default (): ClientContext => {
|
|
861
1056
|
const context = React.useContext<ClientContext | undefined>(ReactClientContext);
|
|
862
1057
|
if (context) return context;
|
|
863
1058
|
|
|
864
1059
|
throw new Error(
|
|
865
1060
|
'Proteum router context hook was called outside the App provider. This is a framework contract failure. ' +
|
|
866
|
-
'Likely fix: move the hook back under
|
|
1061
|
+
'Likely fix: move the hook back under definePageRoute render ownership or pass the required values explicitly. ' +
|
|
867
1062
|
'Re-check both SSR and client navigation after the fix.',
|
|
868
1063
|
);
|
|
869
1064
|
};`,
|
|
@@ -871,7 +1066,7 @@ export default (): ClientContext => {
|
|
|
871
1066
|
|
|
872
1067
|
writeIfChanged(
|
|
873
1068
|
path.join(app.paths.common.generated, 'services.d.ts'),
|
|
874
|
-
`declare type ${
|
|
1069
|
+
`declare type ${appIdentifier} = InstanceType<typeof import("@/server/index").default>;
|
|
875
1070
|
|
|
876
1071
|
declare module '@models/types' {
|
|
877
1072
|
export * from '@generated/common/models';
|
|
@@ -888,7 +1083,7 @@ declare module '@models/types' {
|
|
|
888
1083
|
|
|
889
1084
|
writeIfChanged(
|
|
890
1085
|
path.join(app.paths.server.generated, 'commands.d.ts'),
|
|
891
|
-
`declare type ${
|
|
1086
|
+
`declare type ${appIdentifier} = InstanceType<typeof import("@/server/index").default>;
|
|
892
1087
|
|
|
893
1088
|
declare module "@models/types" {
|
|
894
1089
|
const Models: any;
|
|
@@ -903,7 +1098,7 @@ export {};
|
|
|
903
1098
|
path.join(app.paths.server.generated, 'commands.app.d.ts'),
|
|
904
1099
|
`${commandServiceStubs.declarations}
|
|
905
1100
|
|
|
906
|
-
declare class ${
|
|
1101
|
+
declare class ${appIdentifier} implements import("@server/app/commands").TCommandApplication {
|
|
907
1102
|
env: import("@server/app/commands").TCommandApplication["env"];
|
|
908
1103
|
identity: import("@server/app/commands").TCommandApplication["identity"];
|
|
909
1104
|
getRootServices: import("@server/app/commands").TCommandApplication["getRootServices"];
|
|
@@ -920,7 +1115,7 @@ ${rootServices
|
|
|
920
1115
|
.join('\n')}
|
|
921
1116
|
}
|
|
922
1117
|
|
|
923
|
-
export default ${
|
|
1118
|
+
export default ${appIdentifier};
|
|
924
1119
|
`,
|
|
925
1120
|
);
|
|
926
1121
|
|
|
@@ -932,25 +1127,7 @@ export default ${appClassIdentifier};
|
|
|
932
1127
|
|
|
933
1128
|
writeIfChanged(
|
|
934
1129
|
path.join(app.paths.server.generated, 'services.d.ts'),
|
|
935
|
-
`type
|
|
936
|
-
|
|
937
|
-
declare type ${appClassIdentifier} = import("@/server/index").default;
|
|
938
|
-
|
|
939
|
-
declare module "@app" {
|
|
940
|
-
|
|
941
|
-
import { ApplicationContainer } from '@server/app/container';
|
|
942
|
-
|
|
943
|
-
const ServerServices: (
|
|
944
|
-
Pick<
|
|
945
|
-
ApplicationContainer<InstalledServices>,
|
|
946
|
-
${containerServices}
|
|
947
|
-
>
|
|
948
|
-
&
|
|
949
|
-
${appClassIdentifier}
|
|
950
|
-
)
|
|
951
|
-
|
|
952
|
-
export = ServerServices
|
|
953
|
-
}
|
|
1130
|
+
`declare type ${appIdentifier} = InstanceType<typeof import("@/server/index").default>;
|
|
954
1131
|
|
|
955
1132
|
declare module '@common/errors' {
|
|
956
1133
|
|