proteum 2.2.6 → 2.2.8

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 (42) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +4 -4
  3. package/agents/project/AGENTS.md +2 -1
  4. package/agents/project/app-root/AGENTS.md +1 -1
  5. package/agents/project/diagnostics.md +1 -1
  6. package/agents/project/root/AGENTS.md +2 -1
  7. package/cli/commands/configure.ts +14 -35
  8. package/cli/commands/dev.ts +105 -52
  9. package/cli/compiler/artifacts/controllers.ts +30 -4
  10. package/cli/compiler/artifacts/manifest.ts +1 -5
  11. package/cli/compiler/artifacts/services.ts +67 -29
  12. package/cli/presentation/commands.ts +9 -9
  13. package/cli/presentation/help.ts +1 -1
  14. package/cli/scaffold/index.ts +2 -5
  15. package/cli/scaffold/templates.ts +3 -10
  16. package/cli/utils/agents.ts +281 -199
  17. package/client/dev/profiler/ApexChart.tsx +4 -3
  18. package/common/dev/serverHotReload.ts +26 -25
  19. package/package.json +1 -1
  20. package/server/app/commands.ts +11 -16
  21. package/server/app/commandsManager.ts +5 -1
  22. package/server/app/controller/index.ts +68 -16
  23. package/server/app/devCommands.ts +3 -3
  24. package/server/app/devDiagnostics.ts +2 -2
  25. package/server/app/index.ts +19 -8
  26. package/server/app/service/container.ts +22 -19
  27. package/server/app/service/index.ts +33 -13
  28. package/server/app.tsconfig.json +0 -1
  29. package/server/services/auth/index.ts +12 -6
  30. package/server/services/auth/router/index.ts +12 -14
  31. package/server/services/auth/router/request.ts +34 -13
  32. package/server/services/disks/driver.ts +1 -1
  33. package/server/services/disks/index.ts +11 -8
  34. package/server/services/email/index.ts +1 -1
  35. package/server/services/prisma/Facet.ts +6 -5
  36. package/server/services/router/index.ts +8 -7
  37. package/server/services/router/request/validation/zod.ts +2 -0
  38. package/server/services/router/response/index.ts +9 -9
  39. package/server/services/router/service.ts +12 -8
  40. package/tests/agents-utils.test.cjs +207 -0
  41. package/tests/dev-transpile-watch.test.cjs +513 -0
  42. package/types/global/vendors.d.ts +70 -0
@@ -711,6 +711,68 @@ ${classMembers.join('\n')}
711
711
  };
712
712
  };
713
713
 
714
+ const isNamespaceImportDeclaration = (statement: ts.ImportDeclaration) =>
715
+ statement.importClause?.namedBindings !== undefined && ts.isNamespaceImport(statement.importClause.namedBindings);
716
+
717
+ const isClientServerStubSupportStatement = (statement: ts.Statement, appClassIdentifier: string) => {
718
+ if (ts.isTypeAliasDeclaration(statement)) {
719
+ return !new Set([
720
+ `${appClassIdentifier}Disks`,
721
+ `${appClassIdentifier}Router`,
722
+ `${appClassIdentifier}RouterPlugins`,
723
+ ]).has(statement.name.text);
724
+ }
725
+
726
+ return ts.isInterfaceDeclaration(statement) || ts.isModuleDeclaration(statement);
727
+ };
728
+
729
+ const createClientServerIndexDeclaration = (rootServices: TParsedService[]) => {
730
+ const sourceFile = createSourceFile(getAppServerEntryFilepath());
731
+ const appClass = getDefaultExportClassDeclaration(sourceFile);
732
+ const serviceTypesByRegisteredName = new Map(
733
+ rootServices.map((service) => [service.registeredName, `import("${service.importPath}").default`] as const),
734
+ );
735
+
736
+ const imports = sourceFile.statements
737
+ .filter((statement): statement is ts.ImportDeclaration => ts.isImportDeclaration(statement))
738
+ .filter((statement) => !isNamespaceImportDeclaration(statement))
739
+ .map((statement) => statement.getText(sourceFile));
740
+ const supportStatements = sourceFile.statements
741
+ .slice(0, sourceFile.statements.indexOf(appClass))
742
+ .filter((statement) => isClientServerStubSupportStatement(statement, app.identity.identifier))
743
+ .map((statement) => statement.getText(sourceFile));
744
+ const heritageClauses = appClass.heritageClauses?.map((clause) => clause.getText(sourceFile)).join(' ');
745
+ const properties = appClass.members
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
+
752
+ const propertyType =
753
+ propertyName === 'Disks'
754
+ ? 'object'
755
+ : propertyName === 'Router'
756
+ ? `${app.identity.identifier}Router`
757
+ : property.type?.getText(sourceFile) || serviceTypesByRegisteredName.get(propertyName);
758
+ if (!propertyType) return undefined;
759
+
760
+ return ` public ${propertyName}: ${propertyType};`;
761
+ })
762
+ .filter((property): property is string => property !== undefined);
763
+
764
+ return `${imports.join('\n')}
765
+
766
+ ${supportStatements.join('\n\n')}
767
+
768
+ type ${app.identity.identifier}Router = Router<${app.identity.identifier}>;
769
+
770
+ export default class ${app.identity.identifier}${heritageClauses ? ` ${heritageClauses}` : ''} {
771
+ ${properties.join('\n')}
772
+ }
773
+ `;
774
+ };
775
+
714
776
  const resolveManifestService = (service: TParsedService, parent: string): TProteumManifestService => ({
715
777
  kind: 'service',
716
778
  registeredName: service.registeredName,
@@ -730,6 +792,11 @@ export const generateServiceArtifacts = () => {
730
792
  const routerPluginServices = routerPlugins.map((service) => resolveManifestService(service, 'Router.plugins'));
731
793
  const commandServiceStubs = createCommandServiceStubDeclarations(rootServices);
732
794
 
795
+ writeIfChanged(
796
+ path.join(app.paths.client.generated, 'server-index.d.ts'),
797
+ createClientServerIndexDeclaration(rootServices),
798
+ );
799
+
733
800
  writeIfChanged(
734
801
  path.join(app.paths.client.generated, 'services.d.ts'),
735
802
  `declare type ${appClassIdentifier} = import("@/server/index").default;
@@ -737,18 +804,9 @@ export const generateServiceArtifacts = () => {
737
804
  declare module "@app" {
738
805
 
739
806
  import { ${appClassIdentifier} as ${appClassIdentifier}Client } from "@/client";
740
- import ${appClassIdentifier}Server from "@/server/index";
741
807
 
742
808
  export const Router: ${appClassIdentifier}Client['Router'];
743
809
 
744
- ${rootServices
745
- .map((service) =>
746
- service.registeredName !== 'Router'
747
- ? `export const ${service.registeredName}: ${appClassIdentifier}Server["${service.registeredName}"];`
748
- : '',
749
- )
750
- .join('\n')}
751
-
752
810
  }
753
811
 
754
812
  declare module '@models/types' {
@@ -894,26 +952,6 @@ declare module "@app" {
894
952
  export = ServerServices
895
953
  }
896
954
 
897
- declare module '@server/app' {
898
-
899
- import { Application } from "@server/app";
900
- import { Environment } from "@server/app";
901
- import { ServicesContainer } from "@server/app/service/container";
902
-
903
- abstract class ApplicationWithServices extends Application<
904
- ServicesContainer<InstalledServices>
905
- > {}
906
-
907
- export interface Exported {
908
- Application: typeof ApplicationWithServices,
909
- Environment: Environment,
910
- }
911
-
912
- const foo: Exported;
913
-
914
- export = foo;
915
- }
916
-
917
955
  declare module '@common/errors' {
918
956
 
919
957
  export * from '@common/errors/index';
@@ -113,22 +113,22 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
113
113
  configure: {
114
114
  name: 'configure',
115
115
  category: 'Project scaffolding',
116
- summary: 'Interactively configure Proteum-managed instruction stubs for a standalone app or monorepo app root.',
116
+ summary: 'Interactively configure tracked Proteum instruction files for a standalone app or monorepo app root.',
117
117
  usage: 'proteum configure agents',
118
118
  bestFor:
119
- 'Creating or switching the managed `AGENTS.md` instruction layout intentionally instead of having `init` or `dev` write instruction files implicitly.',
119
+ 'Creating or switching the tracked instruction layout intentionally while keeping Proteum-owned instructions embedded in managed sections.',
120
120
  examples: [
121
121
  {
122
- description: 'Configure instruction stubs for the current standalone app',
122
+ description: 'Configure instruction files for the current standalone app',
123
123
  command: 'proteum configure agents',
124
124
  },
125
125
  ],
126
126
  notes: [
127
- 'This command is interactive. It asks whether the current Proteum app belongs to a monorepo and, if so, which ancestor path should receive the reusable root `AGENTS.md` stub.',
128
- 'Standalone mode writes the full app-root instruction set into the current Proteum app root.',
129
- 'Monorepo mode writes the reusable root `AGENTS.md` into the chosen monorepo root and switches the current app root `AGENTS.md` to the app-root addendum.',
130
- 'If a target path already contains a non-managed file or foreign symlink, the interactive flow asks whether to overwrite it with the Proteum-managed stub.',
131
- 'Declined non-managed paths are left untouched; Proteum still creates missing stubs and updates stubs or symlinks it already manages.',
127
+ 'This command is interactive. It asks whether the current Proteum app belongs to a monorepo and, if so, which ancestor path should receive the reusable root `AGENTS.md` file.',
128
+ 'Standalone mode writes tracked instruction files into the current Proteum app root.',
129
+ 'Monorepo mode writes the reusable root `AGENTS.md` into the chosen monorepo root and the app-root instruction files into the current Proteum app root.',
130
+ 'Every managed instruction file contains a `# Proteum Instructions` section with the full embedded Proteum project instruction corpus.',
131
+ 'Existing content outside `# Proteum Instructions` is preserved. Directories and foreign symlinks are replaced only after confirmation.',
132
132
  ],
133
133
  status: 'experimental',
134
134
  },
@@ -191,7 +191,7 @@ export const proteumCommands: Record<TProteumCommandName, TProteumCommandDoc> =
191
191
  notes: [
192
192
  'Use `--cwd` when the target Proteum app lives in another worktree or checkout and you do not want to `cd` first.',
193
193
  'Proteum writes a machine-readable dev session file under `var/run/proteum/dev/<port>.json` by default; override it with `--session-file` when an agent needs a stable path.',
194
- 'Before the interactive dev loop starts, Proteum offers to launch `proteum configure agents` when the app root is missing `AGENTS.md`.',
194
+ 'Before the dev loop starts, Proteum ensures tracked instruction files contain the current managed `# Proteum Instructions` section.',
195
195
  'Use `--replace-existing` when retries should stop the previously tracked matching session before starting a new one.',
196
196
  '`proteum dev list` inspects tracked sessions for the current app root. Add `--stale` to show only orphaned or dead sessions.',
197
197
  '`proteum dev stop` targets the current session file by default. Add `--all` to stop every tracked session for the current app root.',
@@ -139,7 +139,7 @@ export const renderCliOverview = async ({
139
139
  indent: ' ',
140
140
  nextIndent: ' ',
141
141
  }),
142
- wrapText('When the app root is missing `AGENTS.md`, the interactive `proteum dev` start offers to launch `proteum configure agents` before the dev loop begins.', {
142
+ wrapText('Before the dev loop starts, `proteum dev` ensures tracked instruction files contain the current managed `# Proteum Instructions` section.', {
143
143
  indent: ' ',
144
144
  nextIndent: ' ',
145
145
  }),
@@ -5,7 +5,6 @@ import { UsageError } from 'clipanion';
5
5
 
6
6
  import cli from '..';
7
7
  import { loadApplicationIdentityConfig } from '../../common/applicationConfigLoader';
8
- import { renderProjectInstructionGitignoreBlock } from '../utils/agents';
9
8
  import { runProcess } from '../utils/runProcess';
10
9
  import {
11
10
  createClientTsconfigTemplate,
@@ -656,9 +655,7 @@ const createInitFilePlans = ({ appRoot, config }: { appRoot: string; config: TSc
656
655
  },
657
656
  {
658
657
  relativePath: '.gitignore',
659
- content: createGitignoreTemplate({
660
- projectInstructionGitignoreBlock: renderProjectInstructionGitignoreBlock({ coreRoot: cli.paths.core.root }),
661
- }),
658
+ content: createGitignoreTemplate(),
662
659
  },
663
660
  {
664
661
  relativePath: 'eslint.config.mjs',
@@ -727,7 +724,7 @@ export const runInitScaffold = async () => {
727
724
  ? 'Run `npm run dev` in the new app directory.'
728
725
  : 'Run `npm install`, then `npm run dev` in the new app directory.',
729
726
  );
730
- result.nextSteps.push('Run `proteum configure agents` when you want Proteum-managed instruction stubs.');
727
+ result.nextSteps.push('Run `proteum configure agents` when you want tracked Proteum instruction files.');
731
728
  result.nextSteps.push('Use `proteum create page|controller|command|route|service ...` to add app artifacts.');
732
729
 
733
730
  printResult(result, createInitSummary(result, config));
@@ -191,6 +191,7 @@ export const createClientTsconfigTemplate = (paths: TTsconfigTemplatePaths) => `
191
191
  "@server/*": [${JSON.stringify(paths.frameworkServer)}],
192
192
 
193
193
  "@/client/context": ["./.proteum/client/context.ts"],
194
+ "@/server/index": ["./.proteum/client/server-index.d.ts"],
194
195
  "@generated/client/*": ["./.proteum/client/*"],
195
196
  "@generated/common/*": ["./.proteum/common/*"],
196
197
  "@generated/server/*": ["./.proteum/server/*"],
@@ -207,8 +208,7 @@ export const createClientTsconfigTemplate = (paths: TTsconfigTemplatePaths) => `
207
208
  ".",
208
209
  "../var/typings",
209
210
  ${JSON.stringify(paths.frameworkTypesGlobal)},
210
- "../.proteum/client/services.d.ts",
211
- "../server/index.ts"
211
+ "../.proteum/client/services.d.ts"
212
212
  ]
213
213
  }
214
214
  `;
@@ -228,7 +228,6 @@ export const createServerTsconfigTemplate = (paths: TTsconfigTemplatePaths) => `
228
228
  "@common/*": [${JSON.stringify(paths.frameworkCommon)}],
229
229
  "@server/*": [${JSON.stringify(paths.frameworkServer)}],
230
230
 
231
- "@/client/context": ["./.proteum/client/context.ts"],
232
231
  "@generated/client/*": ["./.proteum/client/*"],
233
232
  "@generated/common/*": ["./.proteum/common/*"],
234
233
  "@generated/server/*": ["./.proteum/server/*"],
@@ -253,11 +252,7 @@ export const createServerTsconfigTemplate = (paths: TTsconfigTemplatePaths) => `
253
252
  }
254
253
  `;
255
254
 
256
- export const createGitignoreTemplate = ({
257
- projectInstructionGitignoreBlock,
258
- }: {
259
- projectInstructionGitignoreBlock: string;
260
- }) => `node_modules
255
+ export const createGitignoreTemplate = () => `node_modules
261
256
  /.proteum
262
257
  /.cache
263
258
  /bin
@@ -265,8 +260,6 @@ export const createGitignoreTemplate = ({
265
260
  /var
266
261
  /proteum.connected.json
267
262
  .env
268
-
269
- ${projectInstructionGitignoreBlock}
270
263
  `;
271
264
 
272
265
  export const createEnvTemplate = ({ port, url }: { port: number; url: string }) => `ENV_NAME=local