@codeyam/codeyam-cli 0.1.0-staging.415103 → 0.1.0-staging.44b55c1
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/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +9 -9
- package/analyzer-template/packages/ai/package.json +1 -1
- package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +0 -33
- package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +13 -7
- package/analyzer-template/packages/analyze/src/lib/asts/index.ts +7 -2
- package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +0 -98
- package/analyzer-template/packages/aws/package.json +2 -2
- package/analyzer-template/packages/database/package.json +3 -3
- package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +20 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +4 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js +20 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.d.ts +2 -0
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.js +2 -0
- package/analyzer-template/packages/github/dist/types/src/enums/ProjectFramework.js.map +1 -1
- package/analyzer-template/packages/github/package.json +1 -1
- package/analyzer-template/packages/types/src/enums/ProjectFramework.ts +2 -0
- package/analyzer-template/packages/ui-components/package.json +1 -1
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.d.ts +2 -0
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.d.ts.map +1 -1
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.js +2 -0
- package/analyzer-template/packages/utils/dist/types/src/enums/ProjectFramework.js.map +1 -1
- package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js +196 -0
- package/codeyam-cli/src/__tests__/memory-scripts/filter-session.test.js.map +1 -0
- package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js +114 -0
- package/codeyam-cli/src/__tests__/memory-scripts/read-json-field.test.js.map +1 -0
- package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js +149 -0
- package/codeyam-cli/src/__tests__/memory-scripts/ripgrep-fallback.test.js.map +1 -0
- package/codeyam-cli/src/commands/default.js +3 -46
- package/codeyam-cli/src/commands/default.js.map +1 -1
- package/codeyam-cli/src/commands/editor.js +2354 -284
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/init.js +6 -1
- package/codeyam-cli/src/commands/init.js.map +1 -1
- package/codeyam-cli/src/data/techStacks.js +77 -0
- package/codeyam-cli/src/data/techStacks.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/devServerState.test.js +134 -0
- package/codeyam-cli/src/utils/__tests__/devServerState.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js +127 -0
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js +635 -0
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorCapture.test.js +93 -0
- package/codeyam-cli/src/utils/__tests__/editorCapture.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js +304 -0
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js +121 -0
- package/codeyam-cli/src/utils/__tests__/editorEntityChangeStatus.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js +294 -0
- package/codeyam-cli/src/utils/__tests__/editorImageVerifier.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js +542 -0
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +393 -0
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js +270 -0
- package/codeyam-cli/src/utils/__tests__/editorMockState.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +217 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +266 -0
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +107 -0
- package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioLookup.test.js +139 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioLookup.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js +221 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarioSwitch.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js +221 -0
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js +213 -0
- package/codeyam-cli/src/utils/__tests__/editorSeedAdapter.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js +1737 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js +134 -0
- package/codeyam-cli/src/utils/__tests__/git.editor.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js +107 -0
- package/codeyam-cli/src/utils/__tests__/journalCaptureStabilization.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js +101 -0
- package/codeyam-cli/src/utils/__tests__/parseRegisterArg.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/project.test.js +65 -0
- package/codeyam-cli/src/utils/__tests__/project.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js +121 -0
- package/codeyam-cli/src/utils/__tests__/scenarioMarkers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +246 -0
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +25 -5
- package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/templateConsistency.test.js +51 -0
- package/codeyam-cli/src/utils/__tests__/templateConsistency.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +142 -0
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -0
- package/codeyam-cli/src/utils/backgroundServer.js +2 -2
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/buildFlags.js +4 -0
- package/codeyam-cli/src/utils/buildFlags.js.map +1 -0
- package/codeyam-cli/src/utils/devServerState.js +71 -0
- package/codeyam-cli/src/utils/devServerState.js.map +1 -0
- package/codeyam-cli/src/utils/editorApi.js +73 -0
- package/codeyam-cli/src/utils/editorApi.js.map +1 -0
- package/codeyam-cli/src/utils/editorAudit.js +159 -0
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -0
- package/codeyam-cli/src/utils/editorCapture.js +102 -0
- package/codeyam-cli/src/utils/editorCapture.js.map +1 -0
- package/codeyam-cli/src/utils/editorDevServer.js +197 -0
- package/codeyam-cli/src/utils/editorDevServer.js.map +1 -0
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js +44 -0
- package/codeyam-cli/src/utils/editorEntityChangeStatus.js.map +1 -0
- package/codeyam-cli/src/utils/editorImageVerifier.js +155 -0
- package/codeyam-cli/src/utils/editorImageVerifier.js.map +1 -0
- package/codeyam-cli/src/utils/editorJournal.js +225 -0
- package/codeyam-cli/src/utils/editorJournal.js.map +1 -0
- package/codeyam-cli/src/utils/editorLoaderHelpers.js +81 -0
- package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorMockState.js +248 -0
- package/codeyam-cli/src/utils/editorMockState.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js +135 -0
- package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorPreview.js +106 -0
- package/codeyam-cli/src/utils/editorPreview.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarioSwitch.js +112 -0
- package/codeyam-cli/src/utils/editorScenarioSwitch.js.map +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js +96 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -0
- package/codeyam-cli/src/utils/editorSeedAdapter.js +173 -0
- package/codeyam-cli/src/utils/editorSeedAdapter.js.map +1 -0
- package/codeyam-cli/src/utils/entityChangeStatus.js +347 -0
- package/codeyam-cli/src/utils/entityChangeStatus.js.map +1 -0
- package/codeyam-cli/src/utils/entityChangeStatus.server.js +158 -0
- package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -0
- package/codeyam-cli/src/utils/git.js +51 -0
- package/codeyam-cli/src/utils/git.js.map +1 -1
- package/codeyam-cli/src/utils/install-skills.js +28 -17
- package/codeyam-cli/src/utils/install-skills.js.map +1 -1
- package/codeyam-cli/src/utils/parseRegisterArg.js +31 -0
- package/codeyam-cli/src/utils/parseRegisterArg.js.map +1 -0
- package/codeyam-cli/src/utils/project.js +15 -5
- package/codeyam-cli/src/utils/project.js.map +1 -1
- package/codeyam-cli/src/utils/scenarioMarkers.js +134 -0
- package/codeyam-cli/src/utils/scenarioMarkers.js.map +1 -0
- package/codeyam-cli/src/utils/scenariosManifest.js +112 -0
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -0
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +46 -16
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
- package/codeyam-cli/src/utils/testRunner.js +1 -1
- package/codeyam-cli/src/utils/testRunner.js.map +1 -1
- package/codeyam-cli/src/utils/webappDetection.js +21 -0
- package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +410 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/database.js +41 -27
- package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
- package/codeyam-cli/src/webserver/app/lib/git.js +396 -0
- package/codeyam-cli/src/webserver/app/lib/git.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{CopyButton-DmJveP3T.js → CopyButton-BPXZwM4t.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-C76mRRiF.js → EntityItem-BcgbViKV.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-CobE682z.js → EntityTypeIcon-CQIG2qda.js} +9 -9
- package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-djPLI-WV.js → ReportIssueModal-BzHcG7SE.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-B76aig_2.js → ScenarioViewer-0DY_NKil.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/ViewportInspectBar-oAf2Kqsf.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{_index-C96V0n15.js → _index-DLxKhri3.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BpKzcsJz.js → activity.(_tab)-BcY3q6nt.js} +6 -6
- package/codeyam-cli/src/webserver/build/client/assets/addon-canvas-DpzMmAy5.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/addon-fit-YJmn1quW.js +12 -0
- package/codeyam-cli/src/webserver/build/client/assets/addon-webgl-DI8QOUvO.js +58 -0
- package/codeyam-cli/src/webserver/build/client/assets/{agent-transcripts-D9hemwl6.js → agent-transcripts-Bni3iiUj.js} +5 -5
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-audit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-diff-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-file-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-load-commit-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-project-info-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenarios-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{book-open-D_nMCFmP.js → book-open-BYOypzCa.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-BH2h1Ea2.js → chevron-down-C_Pmso5S.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{circle-check-DyIKORY6.js → circle-check-BVMi9VA5.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{copy-NDbZjXao.js → copy-n2FB0_Sw.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-CC6AbExI.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Csi0_PMl.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor-pyT-NAuu.js +10 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-BLQMSKZa.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-CrjR3zZW.js → entity._sha._-BF4oLwaE.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-C7YX6r3H.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-CF164ouH.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/{files-DO4CZ16O.js → files-BZrlFE1F.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/git-DdZcvjGh.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/globals-BkWJ_UNc.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/index-yHOVb4rc.js +15 -0
- package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BAXYRVEO.js → loader-circle-DaAZ_H2w.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/manifest-753dd058.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{memory-FweZHj5U.js → memory-Bl2rpw8u.js} +13 -10
- package/codeyam-cli/src/webserver/build/client/assets/{pause-DTAcYxBt.js → pause-f5-1lKBt.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{root-Dzn8nIkU.js → root-B_X8HS1x.js} +15 -15
- package/codeyam-cli/src/webserver/build/client/assets/{search-fKo7v0Zo.js → search-Di64LWVb.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/{settings-DfuTtcJP.js → settings-0OrEMU6J.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{simulations-B3aOzpCZ.js → simulations-DWT-CvLy.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{terminal-BG4heKCG.js → terminal-Br7MOqts.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-DtSmdtM4.js → triangle-alert-BLdiCuG-.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-CrAK28Bc.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/xterm-BqvuqXEL.js +27 -0
- package/codeyam-cli/src/webserver/build/server/assets/{index-DeSuKbSF.js → index-Dp5Hp9wV.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-DpDPLKrh.js +416 -0
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/editorProxy.js +557 -50
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +414 -0
- package/codeyam-cli/src/webserver/scripts/journalCapture.ts +118 -9
- package/codeyam-cli/src/webserver/server.js +93 -12
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +131 -11
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/chrome-extension-react/EXTENSION_SETUP.md +75 -0
- package/codeyam-cli/templates/chrome-extension-react/gitignore +15 -0
- package/codeyam-cli/templates/chrome-extension-react/index.html +12 -0
- package/codeyam-cli/templates/chrome-extension-react/package.json +26 -0
- package/codeyam-cli/templates/chrome-extension-react/popup.html +12 -0
- package/codeyam-cli/templates/chrome-extension-react/public/manifest.json +15 -0
- package/codeyam-cli/templates/chrome-extension-react/src/background/service-worker.ts +7 -0
- package/codeyam-cli/templates/chrome-extension-react/src/globals.css +6 -0
- package/codeyam-cli/templates/chrome-extension-react/src/lib/storage.ts +37 -0
- package/codeyam-cli/templates/chrome-extension-react/src/popup/App.tsx +12 -0
- package/codeyam-cli/templates/chrome-extension-react/src/popup/main.tsx +10 -0
- package/codeyam-cli/templates/chrome-extension-react/tsconfig.json +24 -0
- package/codeyam-cli/templates/chrome-extension-react/vite.config.ts +41 -0
- package/codeyam-cli/templates/editor-step-hook.py +108 -20
- package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +89 -0
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/_layout.tsx +33 -0
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/index.tsx +12 -0
- package/codeyam-cli/templates/expo-react-native/app/(tabs)/settings.tsx +12 -0
- package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +12 -0
- package/codeyam-cli/templates/expo-react-native/app.json +18 -0
- package/codeyam-cli/templates/expo-react-native/babel.config.js +9 -0
- package/codeyam-cli/templates/expo-react-native/gitignore +12 -0
- package/codeyam-cli/templates/expo-react-native/global.css +3 -0
- package/codeyam-cli/templates/expo-react-native/lib/storage.ts +32 -0
- package/codeyam-cli/templates/expo-react-native/metro.config.js +6 -0
- package/codeyam-cli/templates/expo-react-native/nativewind-env.d.ts +1 -0
- package/codeyam-cli/templates/expo-react-native/package.json +37 -0
- package/codeyam-cli/templates/expo-react-native/tailwind.config.js +10 -0
- package/codeyam-cli/templates/expo-react-native/tsconfig.json +10 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/AUTH_PATTERNS.md +308 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/AUTH_UPGRADE.md +304 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/DATABASE.md +112 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/FEATURE_PATTERNS.md +37 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/codeyam-isolate/layout.tsx +12 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/app/lib/prisma.ts +9 -4
- package/codeyam-cli/templates/nextjs-prisma-sqlite/env +4 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/gitignore +64 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +4 -1
- package/codeyam-cli/templates/nextjs-prisma-sqlite/prisma/seed.ts +4 -1
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +92 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/vitest.config.ts +13 -0
- package/codeyam-cli/templates/{nextjs-prisma-sqlite/PRISMA_SETUP.md → nextjs-prisma-supabase/SUPABASE_SETUP.md} +37 -17
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/api/todos/route.ts +17 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/globals.css +26 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/layout.tsx +34 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/lib/prisma.ts +20 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/lib/supabase.ts +12 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/app/page.tsx +10 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/env +9 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/eslint.config.mjs +11 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/gitignore +40 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/next.config.ts +11 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/package.json +36 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/postcss.config.mjs +7 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/prisma/schema.prisma +27 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/prisma/seed.ts +39 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/prisma.config.ts +12 -0
- package/codeyam-cli/templates/nextjs-prisma-supabase/tsconfig.json +34 -0
- package/codeyam-cli/templates/{codeyam-dev-mode.md → skills/codeyam-dev-mode/SKILL.md} +2 -2
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +145 -0
- package/codeyam-cli/templates/{codeyam-memory.md → skills/codeyam-memory/SKILL.md} +215 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/deprecated-prompt.md +100 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.mjs +139 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.mjs +52 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/misleading-api-prompt.md +117 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/read-json-field.mjs +61 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/lib/ripgrep-fallback.mjs +155 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/analyze-prompt.md +46 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.mjs +13 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter-session.mjs +95 -0
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.mjs +160 -0
- package/package.json +15 -10
- package/packages/ai/src/lib/generateExecutionFlows.js +0 -11
- package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
- package/packages/analyze/src/lib/ProjectAnalyzer.js +10 -4
- package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
- package/packages/analyze/src/lib/asts/index.js +4 -2
- package/packages/analyze/src/lib/asts/index.js.map +1 -1
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +0 -40
- package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js +20 -0
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/packages/types/src/enums/ProjectFramework.js +2 -0
- package/packages/types/src/enums/ProjectFramework.js.map +1 -1
- package/scripts/npm-post-install.cjs +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-BaIiqg_w.js +0 -41
- package/codeyam-cli/src/webserver/build/client/assets/addon-fit-CUXOrorO.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/createLucideIcon-CMT1jU2q.js +0 -21
- package/codeyam-cli/src/webserver/build/client/assets/dev.empty-BiM6z3Do.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor-BaC8lHDG.js +0 -7
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-DloHYjtt.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-C28BiQzt.js +0 -6
- package/codeyam-cli/src/webserver/build/client/assets/git-CFCTYk9I.js +0 -15
- package/codeyam-cli/src/webserver/build/client/assets/globals-BH6uYxPM.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-fb3128c3.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-ByhSyh0W.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/xterm-DMSzMhqy.js +0 -9
- package/codeyam-cli/src/webserver/build/server/assets/server-build-BbQ8YBP-.js +0 -362
- package/codeyam-cli/templates/codeyam-editor.md +0 -67
- package/scripts/finalize-analyzer.cjs +0 -13
- /package/codeyam-cli/templates/{codeyam-diagnose.md → commands/codeyam-diagnose.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-debug.md → skills/codeyam-debug/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-new-rule.md → skills/codeyam-new-rule/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-setup.md → skills/codeyam-setup/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-sim.md → skills/codeyam-sim/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-test.md → skills/codeyam-test/SKILL.md} +0 -0
- /package/codeyam-cli/templates/{codeyam-verify.md → skills/codeyam-verify/SKILL.md} +0 -0
|
@@ -3,16 +3,157 @@ import net from 'net';
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { getProjectRoot } from "../state.js";
|
|
6
|
+
import { createMockStateManager, } from "../utils/editorMockState.js";
|
|
7
|
+
import { computeEditorPorts } from "../utils/editorDevServer.js";
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a target URL by stripping trailing slashes for consistency.
|
|
10
|
+
*
|
|
11
|
+
* Previously this also replaced `localhost` with `127.0.0.1`, but that broke
|
|
12
|
+
* forwarding to dev servers that bind to IPv6 only (e.g. Vite 6 on macOS
|
|
13
|
+
* binds to `[::1]`). The hostname is now left as-is — `resolveLoopbackAddress`
|
|
14
|
+
* probes the actual target at startup to pick the right address.
|
|
15
|
+
*/
|
|
16
|
+
export function normalizeTargetUrl(url) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = new URL(url);
|
|
19
|
+
return parsed.toString().replace(/\/$/, '');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return url;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Probe a localhost port to determine the correct loopback address.
|
|
27
|
+
* Dev servers may bind to IPv4 (127.0.0.1), IPv6 (::1), or both.
|
|
28
|
+
* Returns the first address that accepts a TCP connection.
|
|
29
|
+
*/
|
|
30
|
+
export async function resolveLoopbackAddress(port) {
|
|
31
|
+
const candidates = ['127.0.0.1', '::1'];
|
|
32
|
+
for (const host of candidates) {
|
|
33
|
+
try {
|
|
34
|
+
const connected = await new Promise((resolve) => {
|
|
35
|
+
const socket = new net.Socket();
|
|
36
|
+
socket.setTimeout(1000);
|
|
37
|
+
socket.once('connect', () => {
|
|
38
|
+
socket.destroy();
|
|
39
|
+
resolve(true);
|
|
40
|
+
});
|
|
41
|
+
socket.once('error', () => {
|
|
42
|
+
socket.destroy();
|
|
43
|
+
resolve(false);
|
|
44
|
+
});
|
|
45
|
+
socket.once('timeout', () => {
|
|
46
|
+
socket.destroy();
|
|
47
|
+
resolve(false);
|
|
48
|
+
});
|
|
49
|
+
socket.connect(port, host);
|
|
50
|
+
});
|
|
51
|
+
if (connected) {
|
|
52
|
+
return host;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Try next candidate
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
6
61
|
// Global key so the proxy survives HMR
|
|
7
62
|
const GLOBAL_KEY = '__codeyam_editor_proxy__';
|
|
63
|
+
// ─── Live Preview Health ─────────────────────────────────────────────
|
|
64
|
+
const PREVIEW_HEALTH_KEY = '__codeyam_preview_health__';
|
|
65
|
+
function getPreviewHealth() {
|
|
66
|
+
return globalThis[PREVIEW_HEALTH_KEY] ?? null;
|
|
67
|
+
}
|
|
68
|
+
function setPreviewHealth(report) {
|
|
69
|
+
globalThis[PREVIEW_HEALTH_KEY] = report;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the current live preview health report (read by API endpoint).
|
|
73
|
+
*/
|
|
74
|
+
export function getPreviewHealthReport() {
|
|
75
|
+
return getPreviewHealth();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Reset preview health state (called when a new HTML page is served).
|
|
79
|
+
*/
|
|
80
|
+
export function resetPreviewHealth() {
|
|
81
|
+
setPreviewHealth(null);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Error-catching script injected into HTML responses.
|
|
85
|
+
* Uses vanilla JS for maximum compatibility.
|
|
86
|
+
*/
|
|
87
|
+
export const PREVIEW_HEALTH_SCRIPT = `<script data-codeyam-health>
|
|
88
|
+
(function() {
|
|
89
|
+
var errors = [];
|
|
90
|
+
var reported = false;
|
|
91
|
+
function report(type, msg, stack) {
|
|
92
|
+
errors.push({ type: type, message: msg, stack: stack, timestamp: Date.now() });
|
|
93
|
+
if (!reported) {
|
|
94
|
+
reported = true;
|
|
95
|
+
setTimeout(function() { flush(); }, 500);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function flush() {
|
|
99
|
+
fetch('/__codeyam__/preview-health', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: { 'Content-Type': 'application/json' },
|
|
102
|
+
body: JSON.stringify({ errors: errors, url: location.href })
|
|
103
|
+
}).catch(function(){});
|
|
104
|
+
reported = false;
|
|
105
|
+
errors = [];
|
|
106
|
+
}
|
|
107
|
+
window.addEventListener('error', function(e) {
|
|
108
|
+
report('error', e.message, e.error && e.error.stack);
|
|
109
|
+
});
|
|
110
|
+
window.addEventListener('unhandledrejection', function(e) {
|
|
111
|
+
report('unhandledrejection', String(e.reason), e.reason && e.reason.stack);
|
|
112
|
+
});
|
|
113
|
+
var origError = console.error;
|
|
114
|
+
console.error = function() {
|
|
115
|
+
report('console.error', Array.prototype.join.call(arguments, ' '));
|
|
116
|
+
origError.apply(console, arguments);
|
|
117
|
+
};
|
|
118
|
+
window.addEventListener('load', function() {
|
|
119
|
+
setTimeout(function() {
|
|
120
|
+
var hasContent = document.body && document.body.innerText.trim().length > 0;
|
|
121
|
+
fetch('/__codeyam__/preview-health', {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers: { 'Content-Type': 'application/json' },
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
loaded: true,
|
|
126
|
+
hasContent: hasContent,
|
|
127
|
+
url: location.href,
|
|
128
|
+
errorCount: errors.length
|
|
129
|
+
})
|
|
130
|
+
}).catch(function(){});
|
|
131
|
+
}, 1000);
|
|
132
|
+
});
|
|
133
|
+
})();
|
|
134
|
+
</script>`;
|
|
8
135
|
const CACHE_TTL_MS = 500;
|
|
9
136
|
let scenarioCache = { data: null, timestamp: 0 };
|
|
137
|
+
// Session config extracted from the active scenario — drives cookie injection
|
|
138
|
+
let sessionConfig = undefined;
|
|
139
|
+
// Max body size to buffer for mock matching (10MB)
|
|
140
|
+
const MAX_BODY_SIZE = 10 * 1024 * 1024;
|
|
10
141
|
function getProxyState() {
|
|
11
142
|
return globalThis[GLOBAL_KEY] ?? null;
|
|
12
143
|
}
|
|
13
144
|
function setProxyState(state) {
|
|
14
145
|
globalThis[GLOBAL_KEY] = state;
|
|
15
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Get or create the mock state manager (survives HMR via globalThis).
|
|
149
|
+
*/
|
|
150
|
+
function getMockStateManager() {
|
|
151
|
+
const key = '__codeyam_mock_state__';
|
|
152
|
+
if (!globalThis[key]) {
|
|
153
|
+
globalThis[key] = createMockStateManager();
|
|
154
|
+
}
|
|
155
|
+
return globalThis[key];
|
|
156
|
+
}
|
|
16
157
|
/**
|
|
17
158
|
* Get the proxy URL if the proxy is running.
|
|
18
159
|
*/
|
|
@@ -24,6 +165,13 @@ export function getProxyUrl() {
|
|
|
24
165
|
}
|
|
25
166
|
/**
|
|
26
167
|
* Read the active scenario's mock data from disk, with brief caching.
|
|
168
|
+
* Feeds the data into the MockStateManager.
|
|
169
|
+
*
|
|
170
|
+
* For application/user scenarios (type-aware): only loads `externalApis`
|
|
171
|
+
* into the mock state manager. All DB-backed routes flow through to the
|
|
172
|
+
* real app (database is seeded with real data).
|
|
173
|
+
*
|
|
174
|
+
* For component scenarios (or legacy): loads all routes as before.
|
|
27
175
|
*/
|
|
28
176
|
function readScenarioData() {
|
|
29
177
|
const now = Date.now();
|
|
@@ -50,9 +198,31 @@ function readScenarioData() {
|
|
|
50
198
|
scenarioCache = { data: null, timestamp: now };
|
|
51
199
|
return null;
|
|
52
200
|
}
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
201
|
+
const rawData = JSON.parse(fs.readFileSync(dataFilePath, 'utf-8'));
|
|
202
|
+
// Extract session config for cookie injection
|
|
203
|
+
sessionConfig = rawData.session || null;
|
|
204
|
+
// Type-aware: for seed-based scenarios, only serve externalApis via proxy
|
|
205
|
+
const scenarioType = active.type || rawData.type || null;
|
|
206
|
+
let mockData;
|
|
207
|
+
if ((scenarioType === 'application' || scenarioType === 'user') &&
|
|
208
|
+
rawData.seed) {
|
|
209
|
+
// Seed-based scenario: only load externalApis as routes for the proxy
|
|
210
|
+
if (rawData.externalApis && typeof rawData.externalApis === 'object') {
|
|
211
|
+
mockData = { routes: rawData.externalApis };
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
// No external APIs — proxy passes everything through
|
|
215
|
+
mockData = {};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
// Component/legacy scenario: load all data
|
|
220
|
+
mockData = rawData;
|
|
221
|
+
}
|
|
222
|
+
scenarioCache = { data: mockData, timestamp: now };
|
|
223
|
+
// Feed into mock state manager (smart reload handles dedup)
|
|
224
|
+
getMockStateManager().loadScenario(mockData);
|
|
225
|
+
return mockData;
|
|
56
226
|
}
|
|
57
227
|
catch (err) {
|
|
58
228
|
console.warn('[editorProxy] Error reading scenario data:', err);
|
|
@@ -61,50 +231,153 @@ function readScenarioData() {
|
|
|
61
231
|
}
|
|
62
232
|
}
|
|
63
233
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* Supports two formats:
|
|
67
|
-
* - New route-keyed: { "routes": { "/api/drinks": { "body": [...] } } }
|
|
68
|
-
* - Legacy key-based: { "drinks": [...] } → matches /api/drinks
|
|
234
|
+
* Buffer the request body, up to MAX_BODY_SIZE.
|
|
235
|
+
* Returns null if the body exceeds the limit.
|
|
69
236
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
237
|
+
function bufferRequestBody(req) {
|
|
238
|
+
return new Promise((resolve) => {
|
|
239
|
+
const chunks = [];
|
|
240
|
+
let totalSize = 0;
|
|
241
|
+
req.on('data', (chunk) => {
|
|
242
|
+
totalSize += chunk.length;
|
|
243
|
+
if (totalSize > MAX_BODY_SIZE) {
|
|
244
|
+
resolve(null); // Too large — skip mock matching
|
|
245
|
+
req.resume(); // Drain remaining
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
chunks.push(chunk);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
req.on('end', () => {
|
|
252
|
+
if (totalSize > MAX_BODY_SIZE)
|
|
253
|
+
return; // Already resolved
|
|
254
|
+
resolve(Buffer.concat(chunks));
|
|
255
|
+
});
|
|
256
|
+
req.on('error', () => {
|
|
257
|
+
resolve(null);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Strip IPv6 bracket notation for use with http.request hostname.
|
|
263
|
+
* URL.hostname returns `[::1]` for IPv6 but http.request needs `::1`.
|
|
264
|
+
*/
|
|
265
|
+
function stripIPv6Brackets(hostname) {
|
|
266
|
+
if (hostname.startsWith('[') && hostname.endsWith(']')) {
|
|
267
|
+
return hostname.slice(1, -1);
|
|
268
|
+
}
|
|
269
|
+
return hostname;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Forward a buffered request to the target dev server.
|
|
273
|
+
* Unlike the streaming forwardRequest, this replays a buffered body.
|
|
274
|
+
*/
|
|
275
|
+
function forwardBufferedRequest(req, res, targetUrl, bodyBuffer) {
|
|
276
|
+
const target = new URL(targetUrl);
|
|
277
|
+
const hostname = stripIPv6Brackets(target.hostname);
|
|
278
|
+
const headers = { ...req.headers, host: `${target.hostname}:${target.port}` };
|
|
279
|
+
// Remove accept-encoding so the dev server returns uncompressed responses.
|
|
280
|
+
// The proxy injects a health script into HTML — this fails on compressed bodies.
|
|
281
|
+
delete headers['accept-encoding'];
|
|
282
|
+
// Update content-length if we have the body buffer
|
|
283
|
+
if (bodyBuffer) {
|
|
284
|
+
headers['content-length'] = String(bodyBuffer.length);
|
|
82
285
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
286
|
+
const options = {
|
|
287
|
+
hostname,
|
|
288
|
+
port: target.port,
|
|
289
|
+
path: req.url,
|
|
290
|
+
method: req.method,
|
|
291
|
+
headers,
|
|
292
|
+
};
|
|
293
|
+
const proxyReq = http.request(options, (proxyRes) => {
|
|
294
|
+
const status = proxyRes.statusCode || 200;
|
|
295
|
+
if (status >= 400) {
|
|
296
|
+
console.warn(`[editorProxy] Target returned ${status} for ${req.method} ${req.url}`);
|
|
297
|
+
}
|
|
298
|
+
const headers = { ...proxyRes.headers };
|
|
299
|
+
injectSessionCookie(headers);
|
|
300
|
+
// Check if response is HTML — if so, buffer and inject health script
|
|
301
|
+
const contentType = proxyRes.headers['content-type'] || '';
|
|
302
|
+
if (contentType.includes('text/html')) {
|
|
303
|
+
resetPreviewHealth();
|
|
304
|
+
const chunks = [];
|
|
305
|
+
proxyRes.on('data', (chunk) => chunks.push(chunk));
|
|
306
|
+
proxyRes.on('end', () => {
|
|
307
|
+
const body = Buffer.concat(chunks).toString('utf-8');
|
|
308
|
+
const injected = injectHealthScript(body);
|
|
309
|
+
delete headers['content-length'];
|
|
310
|
+
delete headers['content-encoding'];
|
|
311
|
+
res.writeHead(status, headers);
|
|
312
|
+
res.end(injected);
|
|
313
|
+
});
|
|
314
|
+
return;
|
|
90
315
|
}
|
|
316
|
+
res.writeHead(status, headers);
|
|
317
|
+
proxyRes.pipe(res, { end: true });
|
|
318
|
+
});
|
|
319
|
+
proxyReq.on('error', (err) => {
|
|
320
|
+
console.warn(`[editorProxy] Forward error for ${req.method} ${req.url}: ${err.message}`);
|
|
321
|
+
if (!res.headersSent) {
|
|
322
|
+
res.writeHead(502, { 'Content-Type': 'text/plain' });
|
|
323
|
+
res.end('Bad Gateway — dev server unreachable');
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
if (bodyBuffer && bodyBuffer.length > 0) {
|
|
327
|
+
proxyReq.end(bodyBuffer);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
proxyReq.end();
|
|
91
331
|
}
|
|
92
|
-
return null;
|
|
93
332
|
}
|
|
94
333
|
/**
|
|
95
334
|
* Forward an HTTP request to the target dev server.
|
|
335
|
+
* For HTML responses: buffers body to inject health-check script.
|
|
336
|
+
* For non-HTML responses: pipes directly (no buffering).
|
|
96
337
|
*/
|
|
97
338
|
function forwardRequest(req, res, targetUrl) {
|
|
98
339
|
const target = new URL(targetUrl);
|
|
340
|
+
const hostname = stripIPv6Brackets(target.hostname);
|
|
341
|
+
// Build headers, stripping accept-encoding so the dev server returns uncompressed
|
|
342
|
+
// responses. The proxy injects a health script into HTML — this fails on compressed bodies.
|
|
343
|
+
const { 'accept-encoding': _ae, ...forwardHeaders } = req.headers;
|
|
99
344
|
const options = {
|
|
100
|
-
hostname
|
|
345
|
+
hostname,
|
|
101
346
|
port: target.port,
|
|
102
347
|
path: req.url,
|
|
103
348
|
method: req.method,
|
|
104
|
-
headers: {
|
|
349
|
+
headers: {
|
|
350
|
+
...forwardHeaders,
|
|
351
|
+
host: `${target.hostname}:${target.port}`,
|
|
352
|
+
},
|
|
105
353
|
};
|
|
106
354
|
const proxyReq = http.request(options, (proxyRes) => {
|
|
107
|
-
|
|
355
|
+
const status = proxyRes.statusCode || 200;
|
|
356
|
+
if (status >= 400) {
|
|
357
|
+
console.warn(`[editorProxy] Target returned ${status} for ${req.method} ${req.url}`);
|
|
358
|
+
}
|
|
359
|
+
const headers = { ...proxyRes.headers };
|
|
360
|
+
injectSessionCookie(headers);
|
|
361
|
+
// Check if response is HTML — if so, buffer and inject health script
|
|
362
|
+
const contentType = proxyRes.headers['content-type'] || '';
|
|
363
|
+
if (contentType.includes('text/html')) {
|
|
364
|
+
// Reset health state for new page loads
|
|
365
|
+
resetPreviewHealth();
|
|
366
|
+
const chunks = [];
|
|
367
|
+
proxyRes.on('data', (chunk) => chunks.push(chunk));
|
|
368
|
+
proxyRes.on('end', () => {
|
|
369
|
+
const body = Buffer.concat(chunks).toString('utf-8');
|
|
370
|
+
const injected = injectHealthScript(body);
|
|
371
|
+
// Remove content-length since body size changed; use chunked transfer
|
|
372
|
+
delete headers['content-length'];
|
|
373
|
+
// Remove content-encoding since we're serving uncompressed
|
|
374
|
+
delete headers['content-encoding'];
|
|
375
|
+
res.writeHead(status, headers);
|
|
376
|
+
res.end(injected);
|
|
377
|
+
});
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
res.writeHead(status, headers);
|
|
108
381
|
proxyRes.pipe(res, { end: true });
|
|
109
382
|
});
|
|
110
383
|
proxyReq.on('error', (err) => {
|
|
@@ -116,14 +389,100 @@ function forwardRequest(req, res, targetUrl) {
|
|
|
116
389
|
});
|
|
117
390
|
req.pipe(proxyReq, { end: true });
|
|
118
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* Inject or clear the session-token cookie on proxied responses.
|
|
394
|
+
* When a scenario has session.cookieValue, sets the cookie to auto-log the user in.
|
|
395
|
+
* When a scenario has no session field (null), clears any existing session cookie.
|
|
396
|
+
* When sessionConfig is undefined (no scenario loaded yet), does nothing.
|
|
397
|
+
*/
|
|
398
|
+
function injectSessionCookie(headers) {
|
|
399
|
+
if (sessionConfig === undefined)
|
|
400
|
+
return;
|
|
401
|
+
let cookie;
|
|
402
|
+
if (sessionConfig?.cookieValue) {
|
|
403
|
+
cookie = `session-token=${sessionConfig.cookieValue}; Path=/; SameSite=Lax`;
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
// No session config — clear any existing session cookie
|
|
407
|
+
cookie = `session-token=; Path=/; Max-Age=0`;
|
|
408
|
+
}
|
|
409
|
+
const existing = headers['set-cookie'];
|
|
410
|
+
if (existing) {
|
|
411
|
+
headers['set-cookie'] = [
|
|
412
|
+
...(Array.isArray(existing) ? existing : [existing]),
|
|
413
|
+
cookie,
|
|
414
|
+
];
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
headers['set-cookie'] = [cookie];
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Get the current session config (for testing).
|
|
422
|
+
*/
|
|
423
|
+
export function getSessionConfig() {
|
|
424
|
+
return sessionConfig;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Inject the health-check script into an HTML response body.
|
|
428
|
+
* Inserts before </head> if present, otherwise before </body>, otherwise appends.
|
|
429
|
+
*/
|
|
430
|
+
export function injectHealthScript(html) {
|
|
431
|
+
if (html.includes('</head>')) {
|
|
432
|
+
return html.replace('</head>', PREVIEW_HEALTH_SCRIPT + '</head>');
|
|
433
|
+
}
|
|
434
|
+
if (html.includes('</body>')) {
|
|
435
|
+
return html.replace('</body>', PREVIEW_HEALTH_SCRIPT + '</body>');
|
|
436
|
+
}
|
|
437
|
+
return html + PREVIEW_HEALTH_SCRIPT;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Handle POST /__codeyam__/preview-health — store health data in globalThis.
|
|
441
|
+
*/
|
|
442
|
+
function handlePreviewHealthPost(req, res) {
|
|
443
|
+
const chunks = [];
|
|
444
|
+
req.on('data', (chunk) => chunks.push(chunk));
|
|
445
|
+
req.on('end', () => {
|
|
446
|
+
try {
|
|
447
|
+
const body = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
|
|
448
|
+
const current = getPreviewHealth() || {
|
|
449
|
+
errors: [],
|
|
450
|
+
loaded: false,
|
|
451
|
+
hasContent: false,
|
|
452
|
+
url: '',
|
|
453
|
+
lastUpdated: 0,
|
|
454
|
+
};
|
|
455
|
+
if (body.errors && Array.isArray(body.errors)) {
|
|
456
|
+
current.errors = current.errors.concat(body.errors);
|
|
457
|
+
}
|
|
458
|
+
if (body.loaded !== undefined) {
|
|
459
|
+
current.loaded = body.loaded;
|
|
460
|
+
}
|
|
461
|
+
if (body.hasContent !== undefined) {
|
|
462
|
+
current.hasContent = body.hasContent;
|
|
463
|
+
}
|
|
464
|
+
if (body.url) {
|
|
465
|
+
current.url = body.url;
|
|
466
|
+
}
|
|
467
|
+
current.lastUpdated = Date.now();
|
|
468
|
+
setPreviewHealth(current);
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
// Ignore malformed JSON
|
|
472
|
+
}
|
|
473
|
+
res.writeHead(204);
|
|
474
|
+
res.end();
|
|
475
|
+
});
|
|
476
|
+
}
|
|
119
477
|
/**
|
|
120
478
|
* Handle WebSocket upgrade by piping to the target dev server.
|
|
121
479
|
*/
|
|
122
480
|
function handleUpgrade(req, socket, head, targetUrl) {
|
|
123
481
|
const target = new URL(targetUrl);
|
|
482
|
+
const hostname = stripIPv6Brackets(target.hostname);
|
|
124
483
|
const port = parseInt(target.port, 10) || 80;
|
|
125
|
-
console.log(`[editorProxy] WebSocket upgrade: ${req.url} → ${
|
|
126
|
-
const proxySocket = net.connect(port,
|
|
484
|
+
console.log(`[editorProxy] WebSocket upgrade: ${req.url} → ${hostname}:${port}`);
|
|
485
|
+
const proxySocket = net.connect(port, hostname, () => {
|
|
127
486
|
// Reconstruct the HTTP upgrade request
|
|
128
487
|
const requestLine = `${req.method} ${req.url} HTTP/${req.httpVersion}\r\n`;
|
|
129
488
|
const headers = Object.entries(req.headers)
|
|
@@ -146,38 +505,144 @@ function handleUpgrade(req, socket, head, targetUrl) {
|
|
|
146
505
|
proxySocket.destroy();
|
|
147
506
|
});
|
|
148
507
|
}
|
|
508
|
+
/**
|
|
509
|
+
* Write proxy-config.json so the preload module can discover the proxy.
|
|
510
|
+
*/
|
|
511
|
+
function writeProxyConfig(proxyPort, targetUrl) {
|
|
512
|
+
const projectRoot = getProjectRoot() || process.env.CODEYAM_ROOT_PATH || process.cwd();
|
|
513
|
+
const configPath = path.join(projectRoot, '.codeyam', 'proxy-config.json');
|
|
514
|
+
try {
|
|
515
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
516
|
+
fs.writeFileSync(configPath, JSON.stringify({
|
|
517
|
+
proxyUrl: `http://localhost:${proxyPort}`,
|
|
518
|
+
devServerUrl: targetUrl,
|
|
519
|
+
}), 'utf-8');
|
|
520
|
+
console.log(`[editorProxy] Wrote proxy config to ${configPath}`);
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
console.warn('[editorProxy] Failed to write proxy-config.json:', err);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Remove proxy-config.json on proxy stop.
|
|
528
|
+
*/
|
|
529
|
+
function removeProxyConfig() {
|
|
530
|
+
const projectRoot = getProjectRoot() || process.env.CODEYAM_ROOT_PATH || process.cwd();
|
|
531
|
+
const configPath = path.join(projectRoot, '.codeyam', 'proxy-config.json');
|
|
532
|
+
try {
|
|
533
|
+
if (fs.existsSync(configPath)) {
|
|
534
|
+
fs.unlinkSync(configPath);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
catch {
|
|
538
|
+
// Best effort
|
|
539
|
+
}
|
|
540
|
+
}
|
|
149
541
|
/**
|
|
150
542
|
* Start the editor proxy server.
|
|
151
|
-
* Intercepts
|
|
543
|
+
* Intercepts requests to API routes matching the active scenario, forwards everything else.
|
|
544
|
+
* Supports all HTTP methods (GET, POST, PUT, DELETE, PATCH) with body buffering.
|
|
152
545
|
*/
|
|
153
546
|
export async function startEditorProxy(options) {
|
|
154
547
|
// Stop existing proxy first
|
|
155
548
|
await stopEditorProxy();
|
|
156
|
-
|
|
549
|
+
let targetUrl = normalizeTargetUrl(options.targetUrl);
|
|
157
550
|
let port = options.port;
|
|
551
|
+
// When the target is localhost, probe to find the correct loopback address.
|
|
552
|
+
// Dev servers may bind to IPv4 (127.0.0.1) or IPv6 (::1) — Vite 6 on macOS
|
|
553
|
+
// binds to ::1 by default. We need to match the actual binding.
|
|
554
|
+
try {
|
|
555
|
+
const parsed = new URL(targetUrl);
|
|
556
|
+
if (parsed.hostname === 'localhost') {
|
|
557
|
+
const targetPort = parseInt(parsed.port || '80', 10);
|
|
558
|
+
const resolvedHost = await resolveLoopbackAddress(targetPort);
|
|
559
|
+
if (resolvedHost) {
|
|
560
|
+
parsed.hostname = resolvedHost;
|
|
561
|
+
targetUrl = parsed.toString().replace(/\/$/, '');
|
|
562
|
+
console.log(`[editorProxy] Resolved localhost to ${resolvedHost} for port ${targetPort}`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
// Keep original targetUrl
|
|
568
|
+
}
|
|
158
569
|
console.log(`[editorProxy] Starting proxy (requested port ${port}, target ${targetUrl})`);
|
|
570
|
+
const mockState = getMockStateManager();
|
|
159
571
|
const server = http.createServer((req, res) => {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
572
|
+
void (async () => {
|
|
573
|
+
const parsedUrl = new URL(req.url || '/', `http://localhost:${port}`);
|
|
574
|
+
const pathname = parsedUrl.pathname;
|
|
575
|
+
const method = req.method || 'GET';
|
|
576
|
+
// CORS preflight — permissive 204 for browser cross-origin requests
|
|
577
|
+
if (method === 'OPTIONS') {
|
|
578
|
+
res.writeHead(204, {
|
|
579
|
+
'Access-Control-Allow-Origin': '*',
|
|
580
|
+
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
|
581
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With',
|
|
582
|
+
'Access-Control-Max-Age': '86400',
|
|
583
|
+
});
|
|
584
|
+
res.end();
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
// Intercept preview health reports from the injected script
|
|
588
|
+
if (method === 'POST' && pathname === '/__codeyam__/preview-health') {
|
|
589
|
+
handlePreviewHealthPost(req, res);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
// Load scenario data (also feeds MockStateManager)
|
|
593
|
+
readScenarioData();
|
|
594
|
+
// For methods that may carry a body, buffer it first
|
|
595
|
+
const needsBody = method === 'POST' ||
|
|
596
|
+
method === 'PUT' ||
|
|
597
|
+
method === 'DELETE' ||
|
|
598
|
+
method === 'PATCH';
|
|
599
|
+
if (needsBody) {
|
|
600
|
+
const bodyBuffer = await bufferRequestBody(req);
|
|
601
|
+
if (bodyBuffer === null) {
|
|
602
|
+
// Body too large — forward without mock matching
|
|
603
|
+
forwardBufferedRequest(req, res, targetUrl, null);
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
// Try to parse body as JSON for mock matching
|
|
607
|
+
let parsedBody = undefined;
|
|
608
|
+
if (bodyBuffer.length > 0) {
|
|
609
|
+
try {
|
|
610
|
+
parsedBody = JSON.parse(bodyBuffer.toString('utf-8'));
|
|
611
|
+
}
|
|
612
|
+
catch {
|
|
613
|
+
// Not JSON — that's fine, still try mock matching without parsed body
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const match = mockState.matchRequest(method, pathname, parsedBody);
|
|
167
617
|
if (match) {
|
|
168
|
-
console.log(`[editorProxy] Intercepted ${
|
|
618
|
+
console.log(`[editorProxy] Intercepted ${method} ${pathname} → mock response (status ${match.status})`);
|
|
169
619
|
res.writeHead(match.status, {
|
|
170
620
|
'Content-Type': 'application/json',
|
|
171
621
|
'Access-Control-Allow-Origin': '*',
|
|
172
622
|
'X-CodeYam-Proxy': 'scenario-data',
|
|
173
623
|
});
|
|
174
|
-
res.end(JSON.stringify(match.body));
|
|
624
|
+
res.end(match.body != null ? JSON.stringify(match.body) : '');
|
|
175
625
|
return;
|
|
176
626
|
}
|
|
627
|
+
// No mock match — forward with buffered body
|
|
628
|
+
forwardBufferedRequest(req, res, targetUrl, bodyBuffer);
|
|
629
|
+
return;
|
|
177
630
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
631
|
+
// GET, HEAD, etc. — try mock matching (no body)
|
|
632
|
+
const match = mockState.matchRequest(method, pathname);
|
|
633
|
+
if (match) {
|
|
634
|
+
console.log(`[editorProxy] Intercepted ${method} ${pathname} → mock response (status ${match.status})`);
|
|
635
|
+
res.writeHead(match.status, {
|
|
636
|
+
'Content-Type': 'application/json',
|
|
637
|
+
'Access-Control-Allow-Origin': '*',
|
|
638
|
+
'X-CodeYam-Proxy': 'scenario-data',
|
|
639
|
+
});
|
|
640
|
+
res.end(match.body != null ? JSON.stringify(match.body) : '');
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
// Forward everything else to the dev server
|
|
644
|
+
forwardRequest(req, res, targetUrl);
|
|
645
|
+
})();
|
|
181
646
|
});
|
|
182
647
|
// Handle WebSocket upgrades (for HMR)
|
|
183
648
|
server.on('upgrade', (req, socket, head) => {
|
|
@@ -195,9 +660,14 @@ export async function startEditorProxy(options) {
|
|
|
195
660
|
resolve();
|
|
196
661
|
});
|
|
197
662
|
});
|
|
198
|
-
port
|
|
663
|
+
// When port 0 is requested, the OS assigns an ephemeral port
|
|
664
|
+
const addr = server.address();
|
|
665
|
+
port =
|
|
666
|
+
typeof addr === 'object' && addr !== null ? addr.port : currentPort;
|
|
199
667
|
const state = { server, port, targetUrl };
|
|
200
668
|
setProxyState(state);
|
|
669
|
+
// Write proxy-config.json for the preload module
|
|
670
|
+
writeProxyConfig(port, targetUrl);
|
|
201
671
|
console.log(`[editorProxy] Proxy started on port ${port}, forwarding to ${targetUrl}`);
|
|
202
672
|
return { port };
|
|
203
673
|
}
|
|
@@ -220,6 +690,7 @@ export async function stopEditorProxy() {
|
|
|
220
690
|
if (!state)
|
|
221
691
|
return;
|
|
222
692
|
console.log(`[editorProxy] Stopping proxy on port ${state.port}`);
|
|
693
|
+
removeProxyConfig();
|
|
223
694
|
return new Promise((resolve) => {
|
|
224
695
|
state.server.close(() => {
|
|
225
696
|
console.log('[editorProxy] Proxy stopped');
|
|
@@ -235,6 +706,37 @@ export async function stopEditorProxy() {
|
|
|
235
706
|
*/
|
|
236
707
|
export function invalidateScenarioCache() {
|
|
237
708
|
scenarioCache = { data: null, timestamp: 0 };
|
|
709
|
+
sessionConfig = undefined;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Verify that the proxy can successfully forward a request to the target dev server.
|
|
713
|
+
* Makes a HEAD request through the proxy and checks that it gets a response (any status).
|
|
714
|
+
* Returns false if the proxy isn't running or if the request fails entirely.
|
|
715
|
+
*/
|
|
716
|
+
export async function verifyProxyForwarding() {
|
|
717
|
+
const state = getProxyState();
|
|
718
|
+
if (!state) {
|
|
719
|
+
console.warn('[editorProxy] Cannot verify — proxy is not running');
|
|
720
|
+
return false;
|
|
721
|
+
}
|
|
722
|
+
try {
|
|
723
|
+
const response = await fetch(`http://127.0.0.1:${state.port}/`, {
|
|
724
|
+
method: 'HEAD',
|
|
725
|
+
signal: AbortSignal.timeout(5000),
|
|
726
|
+
});
|
|
727
|
+
// Any response from the target (even 404) means forwarding works.
|
|
728
|
+
// Only 502 (our own Bad Gateway) means the target is unreachable.
|
|
729
|
+
if (response.status === 502) {
|
|
730
|
+
console.warn(`[editorProxy] Verification failed — proxy returned 502 (target unreachable)`);
|
|
731
|
+
return false;
|
|
732
|
+
}
|
|
733
|
+
console.log(`[editorProxy] Verification passed — proxy forwarding to ${state.targetUrl} (status ${response.status})`);
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
catch (err) {
|
|
737
|
+
console.warn(`[editorProxy] Verification failed — could not reach proxy on port ${state.port}`);
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
238
740
|
}
|
|
239
741
|
/**
|
|
240
742
|
* Ensure the proxy is running. If it's not, start it using the current dev server URL.
|
|
@@ -256,9 +758,10 @@ export async function ensureProxyRunning() {
|
|
|
256
758
|
return null;
|
|
257
759
|
}
|
|
258
760
|
const codeyamPort = parseInt(process.env.CODEYAM_PORT || '3111', 10);
|
|
259
|
-
|
|
761
|
+
const { proxyPort } = computeEditorPorts(codeyamPort);
|
|
762
|
+
console.log(`[editorProxy] Proxy not running, starting on-demand (port ${proxyPort}, target ${devServer.url})`);
|
|
260
763
|
const result = await startEditorProxy({
|
|
261
|
-
port:
|
|
764
|
+
port: proxyPort,
|
|
262
765
|
targetUrl: devServer.url,
|
|
263
766
|
});
|
|
264
767
|
if (result) {
|
|
@@ -269,4 +772,8 @@ export async function ensureProxyRunning() {
|
|
|
269
772
|
console.error('[editorProxy] Failed to start on-demand proxy');
|
|
270
773
|
return null;
|
|
271
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Test-only export: trigger readScenarioData so tests can verify session config extraction.
|
|
777
|
+
*/
|
|
778
|
+
export const _readScenarioDataForTest = readScenarioData;
|
|
272
779
|
//# sourceMappingURL=editorProxy.js.map
|