@aws/nx-plugin 1.0.0-rc.75 → 1.0.0-rc.76
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/migrations.json
CHANGED
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"implementation": "./src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration"
|
|
144
144
|
},
|
|
145
145
|
"sync-vended-versions": {
|
|
146
|
-
"version": "1.0.0-rc.
|
|
146
|
+
"version": "1.0.0-rc.76",
|
|
147
147
|
"description": "Sync vended dependency versions and the tracked plugin version to those vended by this release",
|
|
148
148
|
"implementation": "./src/utils/version-upgrade-migration/migration"
|
|
149
149
|
}
|
package/package.json
CHANGED
|
@@ -27,8 +27,20 @@ import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
|
27
27
|
* generated shape and report them via `nextSteps`, rather than clobbering
|
|
28
28
|
* the user's changes.
|
|
29
29
|
* - Idempotent: re-running is a no-op once migrated.
|
|
30
|
-
*/
|
|
31
|
-
|
|
30
|
+
*/ // The consuming repo's formatter/linter (e.g. Biome, ESLint import/order) may
|
|
31
|
+
// alphabetize these three named type imports, so match/rewrite any of their
|
|
32
|
+
// orderings rather than only the literal order the generator emits. Fixed-
|
|
33
|
+
// arity destructuring (`[$a, $b, $c]`) pins the specifier count to exactly
|
|
34
|
+
// three - so a 4th merged-in specifier fails to match - while constraining
|
|
35
|
+
// each slot independently makes the check order-independent.
|
|
36
|
+
const EXPRESS_IMPORT_OLD_WHERE = `{
|
|
37
|
+
$clause <: import_clause(default=\`express\`, name=named_imports(imports=[$a, $b, $c])),
|
|
38
|
+
$a <: or { \`type Request\`, \`type Response\`, \`type NextFunction\` },
|
|
39
|
+
$b <: or { \`type Request\`, \`type Response\`, \`type NextFunction\` },
|
|
40
|
+
$c <: or { \`type Request\`, \`type Response\`, \`type NextFunction\` }
|
|
41
|
+
}`;
|
|
42
|
+
const EXPRESS_IMPORT_OLD_MATCH = `\`import $clause from 'express';\` where ${EXPRESS_IMPORT_OLD_WHERE}`;
|
|
43
|
+
const EXPRESS_IMPORT_OLD_REWRITE = `\`import $clause from 'express';\` => \`import express from 'express';\` where ${EXPRESS_IMPORT_OLD_WHERE}`;
|
|
32
44
|
const RANDOM_UUID_IMPORT = "import { randomUUID } from 'node:crypto';";
|
|
33
45
|
const SESSION_ID_HEADER_LINE = "const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';";
|
|
34
46
|
// The leading comment sits directly above both shapes below. GritQL can only
|
|
@@ -76,7 +88,7 @@ const migrateIndex = async (tree, indexPath, protocol, nextSteps)=>{
|
|
|
76
88
|
const diverged = ()=>{
|
|
77
89
|
nextSteps.push(`${indexPath}: diverged from the generated ts#agent ${protocol} shape - left untouched. Manually move the inline session-id middleware into a sibling \`middleware/session-id-middleware.ts\` module and import \`sessionIdMiddleware\` from there (see the ts#agent generator's template).`);
|
|
78
90
|
};
|
|
79
|
-
const ready = await matchGritQL(tree, indexPath,
|
|
91
|
+
const ready = await matchGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_MATCH) && await matchGritQL(tree, indexPath, `\`${RANDOM_UUID_IMPORT}\``) && await matchGritQL(tree, indexPath, `\`${SESSION_ID_HEADER_LINE}\``) && await matchGritQL(tree, indexPath, `\`${MIDDLEWARE_LEADING_COMMENT}\``) && await matchGritQL(tree, indexPath, `\`${middlewarePattern}\``) && await matchGritQL(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE);
|
|
80
92
|
if (!ready) {
|
|
81
93
|
diverged();
|
|
82
94
|
return;
|
|
@@ -91,7 +103,7 @@ const migrateIndex = async (tree, indexPath, protocol, nextSteps)=>{
|
|
|
91
103
|
if (!tree.exists(middlewarePath)) {
|
|
92
104
|
tree.write(middlewarePath, sessionIdMiddlewareContent(mod));
|
|
93
105
|
}
|
|
94
|
-
await applyGritQL(tree, indexPath,
|
|
106
|
+
await applyGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_REWRITE);
|
|
95
107
|
await applyGritQL(tree, indexPath, `\`${RANDOM_UUID_IMPORT}\` => .`);
|
|
96
108
|
await applyGritQL(tree, indexPath, `\`${SESSION_ID_HEADER_LINE}\` => .`);
|
|
97
109
|
await applyGritQL(tree, indexPath, REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { TS_AGENT_GENERATOR_INFO } from '../../../ts/agent/generator';\nimport {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n matchGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport type { ComponentMetadata } from '../../../utils/nx';\n\n/**\n * Extract the inline session-id Express middleware - previously duplicated\n * verbatim in every ts#agent AG-UI/A2A `index.ts` - into a shared\n * `middleware/session-id-middleware.ts` module sitting beside `index.ts`,\n * mirroring how `agent.ts`/`session.ts` are already shared siblings.\n *\n * HTTP is untouched: its one procedure is a tRPC subscription served over\n * WebSocket, which bypasses Express entirely, so it never had this middleware\n * to begin with (see `enterSessionContext` in its `router.ts` instead).\n *\n * AG-UI and A2A wire the same logic up differently - AG-UI binds it to a\n * named `sessionIdMiddleware` const before use, while A2A inlines it directly\n * at the `app.use` call site - so each gets its own old-shape pattern below,\n * even though both resolve to the same shared middleware content.\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the\n * generated shape and report them via `nextSteps`, rather than clobbering\n * the user's changes.\n * - Idempotent: re-running is a no-op once migrated.\n */\n\nconst EXPRESS_IMPORT_OLD =\n \"import express, { type Request, type Response, type NextFunction } from 'express';\";\nconst EXPRESS_IMPORT_NEW = \"import express from 'express';\";\n\nconst RANDOM_UUID_IMPORT = \"import { randomUUID } from 'node:crypto';\";\n\nconst SESSION_ID_HEADER_LINE =\n \"const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';\";\n\n// The leading comment sits directly above both shapes below. GritQL can only\n// match it as its own comment node - folding it into the same backtick\n// snippet as the statement that follows fails to parse as one construct - so\n// it's matched/deleted as a separate step from the statement/expression.\nconst MIDDLEWARE_LEADING_COMMENT =\n '// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.';\n\n// AG-UI binds the middleware to a named const, used later via `app.use(sessionIdMiddleware)`.\nconst AGUI_MIDDLEWARE_DEFINITION_OLD = `const sessionIdMiddleware = (req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n};`;\n\n// A2A inlines the same logic directly at the `app.use` call site instead.\nconst A2A_MIDDLEWARE_USE_OLD = `app.use((req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n });`;\nconst A2A_MIDDLEWARE_USE_NEW = 'app.use(sessionIdMiddleware);';\n\n// Captures the agent-connection package specifier so the new middleware\n// module targets the same one. Generic over `$names` since a connection\n// generator may have merged its own import into this same statement.\nconst RUNWITHSESSIONID_IMPORT_CAPTURE =\n \"`import { $names } from '$mod';` where { $names <: contains `runWithSessionId` }\";\n\n// Removes `runWithSessionId` from the named import, preserving any other\n// merged specifiers (or the whole statement if it was the only one).\n// Decomposed via `import_clause(name=named_imports($imports))` since a naive\n// `$rest`-based rewrite silently fails once other specifiers are involved.\nconst REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN = `\\`import $clause from '$mod';\\` as $import where {\n $clause <: import_clause(name=named_imports($imports)),\n $imports <: contains \\`runWithSessionId\\`,\n if ($imports <: [\\`runWithSessionId\\`]) { $import => . } else { $imports <: some import_specifier(name=\\`runWithSessionId\\`) => . }\n}`;\n\n// Both protocols' `common-express/` dir vends byte-identical middleware\n// content; read as the single source of truth rather than hand-duplicating\n// it here.\nconst SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(\n join(\n import.meta.dirname,\n '../../../ts/agent/files/common-express/middleware/session-id-middleware.ts.template',\n ),\n 'utf-8',\n);\n\nconst sessionIdMiddlewareContent = (agentConnectionImport: string): string =>\n SESSION_ID_MIDDLEWARE_TEMPLATE.replace(\n '<%- agentConnectionImport %>',\n agentConnectionImport,\n );\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) =>\n component.generator === TS_AGENT_GENERATOR_INFO.id &&\n (component.protocol === 'ag-ui' || component.protocol === 'a2a'),\n );\n\nconst migrateIndex = async (\n tree: Tree,\n indexPath: string,\n protocol: 'ag-ui' | 'a2a',\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(indexPath)) return;\n\n const contents = tree.read(indexPath, 'utf-8') ?? '';\n if (!contents.includes(SESSION_ID_HEADER_LINE)) return;\n\n const middlewarePattern =\n protocol === 'ag-ui'\n ? AGUI_MIDDLEWARE_DEFINITION_OLD\n : A2A_MIDDLEWARE_USE_OLD;\n\n const diverged = () => {\n nextSteps.push(\n `${indexPath}: diverged from the generated ts#agent ${protocol} shape - left untouched. Manually move the inline session-id middleware into a sibling \\`middleware/session-id-middleware.ts\\` module and import \\`sessionIdMiddleware\\` from there (see the ts#agent generator's template).`,\n );\n };\n\n const ready =\n (await matchGritQL(tree, indexPath, `\\`${EXPRESS_IMPORT_OLD}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${middlewarePattern}\\``)) &&\n (await matchGritQL(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE));\n\n if (!ready) {\n diverged();\n return;\n }\n\n const mod = await captureGritQLVariable(\n tree,\n indexPath,\n RUNWITHSESSIONID_IMPORT_CAPTURE,\n 'mod',\n );\n if (!mod) {\n diverged();\n return;\n }\n\n const dir = indexPath.split('/').slice(0, -1).join('/');\n const middlewarePath = joinPathFragments(\n dir,\n 'middleware',\n 'session-id-middleware.ts',\n );\n\n if (!tree.exists(middlewarePath)) {\n tree.write(middlewarePath, sessionIdMiddlewareContent(mod));\n }\n\n await applyGritQL(\n tree,\n indexPath,\n `\\`${EXPRESS_IMPORT_OLD}\\` => \\`${EXPRESS_IMPORT_NEW}\\``,\n );\n await applyGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\` => .`);\n await applyGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\` => .`);\n await applyGritQL(tree, indexPath, REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN);\n await applyGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\` => .`);\n\n if (protocol === 'ag-ui') {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${AGUI_MIDDLEWARE_DEFINITION_OLD}\\` => .`,\n );\n } else {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${A2A_MIDDLEWARE_USE_OLD}\\` => \\`${A2A_MIDDLEWARE_USE_NEW}\\``,\n );\n }\n\n await addDestructuredImport(\n tree,\n indexPath,\n ['sessionIdMiddleware'],\n './middleware/session-id-middleware.js',\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n const indexPath = joinPathFragments(\n project.root,\n component.path,\n 'index.ts',\n );\n\n await migrateIndex(\n tree,\n indexPath,\n component.protocol as 'ag-ui' | 'a2a',\n nextSteps,\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["readFileSync","join","getProjects","joinPathFragments","TS_AGENT_GENERATOR_INFO","addDestructuredImport","applyGritQL","captureGritQLVariable","matchGritQL","formatFilesInSubtree","EXPRESS_IMPORT_OLD","EXPRESS_IMPORT_NEW","RANDOM_UUID_IMPORT","SESSION_ID_HEADER_LINE","MIDDLEWARE_LEADING_COMMENT","AGUI_MIDDLEWARE_DEFINITION_OLD","A2A_MIDDLEWARE_USE_OLD","A2A_MIDDLEWARE_USE_NEW","RUNWITHSESSIONID_IMPORT_CAPTURE","REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN","SESSION_ID_MIDDLEWARE_TEMPLATE","dirname","sessionIdMiddlewareContent","agentConnectionImport","replace","findAgentComponents","components","filter","component","generator","id","protocol","migrateIndex","tree","indexPath","nextSteps","exists","contents","read","includes","middlewarePattern","diverged","push","ready","mod","dir","split","slice","middlewarePath","write","migration","project","values","metadata","path","root"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { TS_AGENT_GENERATOR_INFO } from '../../../ts/agent/generator';\nimport {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n matchGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport type { ComponentMetadata } from '../../../utils/nx';\n\n/**\n * Extract the inline session-id Express middleware - previously duplicated\n * verbatim in every ts#agent AG-UI/A2A `index.ts` - into a shared\n * `middleware/session-id-middleware.ts` module sitting beside `index.ts`,\n * mirroring how `agent.ts`/`session.ts` are already shared siblings.\n *\n * HTTP is untouched: its one procedure is a tRPC subscription served over\n * WebSocket, which bypasses Express entirely, so it never had this middleware\n * to begin with (see `enterSessionContext` in its `router.ts` instead).\n *\n * AG-UI and A2A wire the same logic up differently - AG-UI binds it to a\n * named `sessionIdMiddleware` const before use, while A2A inlines it directly\n * at the `app.use` call site - so each gets its own old-shape pattern below,\n * even though both resolve to the same shared middleware content.\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the\n * generated shape and report them via `nextSteps`, rather than clobbering\n * the user's changes.\n * - Idempotent: re-running is a no-op once migrated.\n */\n\n// The consuming repo's formatter/linter (e.g. Biome, ESLint import/order) may\n// alphabetize these three named type imports, so match/rewrite any of their\n// orderings rather than only the literal order the generator emits. Fixed-\n// arity destructuring (`[$a, $b, $c]`) pins the specifier count to exactly\n// three - so a 4th merged-in specifier fails to match - while constraining\n// each slot independently makes the check order-independent.\nconst EXPRESS_IMPORT_OLD_WHERE = `{\n $clause <: import_clause(default=\\`express\\`, name=named_imports(imports=[$a, $b, $c])),\n $a <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` },\n $b <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` },\n $c <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` }\n}`;\nconst EXPRESS_IMPORT_OLD_MATCH = `\\`import $clause from 'express';\\` where ${EXPRESS_IMPORT_OLD_WHERE}`;\nconst EXPRESS_IMPORT_OLD_REWRITE = `\\`import $clause from 'express';\\` => \\`import express from 'express';\\` where ${EXPRESS_IMPORT_OLD_WHERE}`;\n\nconst RANDOM_UUID_IMPORT = \"import { randomUUID } from 'node:crypto';\";\n\nconst SESSION_ID_HEADER_LINE =\n \"const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';\";\n\n// The leading comment sits directly above both shapes below. GritQL can only\n// match it as its own comment node - folding it into the same backtick\n// snippet as the statement that follows fails to parse as one construct - so\n// it's matched/deleted as a separate step from the statement/expression.\nconst MIDDLEWARE_LEADING_COMMENT =\n '// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.';\n\n// AG-UI binds the middleware to a named const, used later via `app.use(sessionIdMiddleware)`.\nconst AGUI_MIDDLEWARE_DEFINITION_OLD = `const sessionIdMiddleware = (req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n};`;\n\n// A2A inlines the same logic directly at the `app.use` call site instead.\nconst A2A_MIDDLEWARE_USE_OLD = `app.use((req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n });`;\nconst A2A_MIDDLEWARE_USE_NEW = 'app.use(sessionIdMiddleware);';\n\n// Captures the agent-connection package specifier so the new middleware\n// module targets the same one. Generic over `$names` since a connection\n// generator may have merged its own import into this same statement.\nconst RUNWITHSESSIONID_IMPORT_CAPTURE =\n \"`import { $names } from '$mod';` where { $names <: contains `runWithSessionId` }\";\n\n// Removes `runWithSessionId` from the named import, preserving any other\n// merged specifiers (or the whole statement if it was the only one).\n// Decomposed via `import_clause(name=named_imports($imports))` since a naive\n// `$rest`-based rewrite silently fails once other specifiers are involved.\nconst REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN = `\\`import $clause from '$mod';\\` as $import where {\n $clause <: import_clause(name=named_imports($imports)),\n $imports <: contains \\`runWithSessionId\\`,\n if ($imports <: [\\`runWithSessionId\\`]) { $import => . } else { $imports <: some import_specifier(name=\\`runWithSessionId\\`) => . }\n}`;\n\n// Both protocols' `common-express/` dir vends byte-identical middleware\n// content; read as the single source of truth rather than hand-duplicating\n// it here.\nconst SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(\n join(\n import.meta.dirname,\n '../../../ts/agent/files/common-express/middleware/session-id-middleware.ts.template',\n ),\n 'utf-8',\n);\n\nconst sessionIdMiddlewareContent = (agentConnectionImport: string): string =>\n SESSION_ID_MIDDLEWARE_TEMPLATE.replace(\n '<%- agentConnectionImport %>',\n agentConnectionImport,\n );\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) =>\n component.generator === TS_AGENT_GENERATOR_INFO.id &&\n (component.protocol === 'ag-ui' || component.protocol === 'a2a'),\n );\n\nconst migrateIndex = async (\n tree: Tree,\n indexPath: string,\n protocol: 'ag-ui' | 'a2a',\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(indexPath)) return;\n\n const contents = tree.read(indexPath, 'utf-8') ?? '';\n if (!contents.includes(SESSION_ID_HEADER_LINE)) return;\n\n const middlewarePattern =\n protocol === 'ag-ui'\n ? AGUI_MIDDLEWARE_DEFINITION_OLD\n : A2A_MIDDLEWARE_USE_OLD;\n\n const diverged = () => {\n nextSteps.push(\n `${indexPath}: diverged from the generated ts#agent ${protocol} shape - left untouched. Manually move the inline session-id middleware into a sibling \\`middleware/session-id-middleware.ts\\` module and import \\`sessionIdMiddleware\\` from there (see the ts#agent generator's template).`,\n );\n };\n\n const ready =\n (await matchGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_MATCH)) &&\n (await matchGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${middlewarePattern}\\``)) &&\n (await matchGritQL(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE));\n\n if (!ready) {\n diverged();\n return;\n }\n\n const mod = await captureGritQLVariable(\n tree,\n indexPath,\n RUNWITHSESSIONID_IMPORT_CAPTURE,\n 'mod',\n );\n if (!mod) {\n diverged();\n return;\n }\n\n const dir = indexPath.split('/').slice(0, -1).join('/');\n const middlewarePath = joinPathFragments(\n dir,\n 'middleware',\n 'session-id-middleware.ts',\n );\n\n if (!tree.exists(middlewarePath)) {\n tree.write(middlewarePath, sessionIdMiddlewareContent(mod));\n }\n\n await applyGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_REWRITE);\n await applyGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\` => .`);\n await applyGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\` => .`);\n await applyGritQL(tree, indexPath, REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN);\n await applyGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\` => .`);\n\n if (protocol === 'ag-ui') {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${AGUI_MIDDLEWARE_DEFINITION_OLD}\\` => .`,\n );\n } else {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${A2A_MIDDLEWARE_USE_OLD}\\` => \\`${A2A_MIDDLEWARE_USE_NEW}\\``,\n );\n }\n\n await addDestructuredImport(\n tree,\n indexPath,\n ['sessionIdMiddleware'],\n './middleware/session-id-middleware.js',\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n const indexPath = joinPathFragments(\n project.root,\n component.path,\n 'index.ts',\n );\n\n await migrateIndex(\n tree,\n indexPath,\n component.protocol as 'ag-ui' | 'a2a',\n nextSteps,\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["readFileSync","join","getProjects","joinPathFragments","TS_AGENT_GENERATOR_INFO","addDestructuredImport","applyGritQL","captureGritQLVariable","matchGritQL","formatFilesInSubtree","EXPRESS_IMPORT_OLD_WHERE","EXPRESS_IMPORT_OLD_MATCH","EXPRESS_IMPORT_OLD_REWRITE","RANDOM_UUID_IMPORT","SESSION_ID_HEADER_LINE","MIDDLEWARE_LEADING_COMMENT","AGUI_MIDDLEWARE_DEFINITION_OLD","A2A_MIDDLEWARE_USE_OLD","A2A_MIDDLEWARE_USE_NEW","RUNWITHSESSIONID_IMPORT_CAPTURE","REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN","SESSION_ID_MIDDLEWARE_TEMPLATE","dirname","sessionIdMiddlewareContent","agentConnectionImport","replace","findAgentComponents","components","filter","component","generator","id","protocol","migrateIndex","tree","indexPath","nextSteps","exists","contents","read","includes","middlewarePattern","diverged","push","ready","mod","dir","split","slice","middlewarePath","write","migration","project","values","metadata","path","root"],"mappings":"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"}
|