@codeyam/codeyam-cli 0.1.8 → 0.1.9
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 +8 -8
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/package.json +4 -4
- 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 +1 -1
- package/analyzer-template/packages/database/src/lib/kysely/tables/editorScenariosTable.ts +31 -0
- package/analyzer-template/packages/database/src/lib/loadEntities.ts +0 -6
- package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +0 -65
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.d.ts +5 -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 +31 -0
- package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +0 -6
- package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +0 -25
- package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.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/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/__tests__/editor.stepDispatch.test.js +45 -0
- package/codeyam-cli/src/commands/__tests__/editor.stepDispatch.test.js.map +1 -0
- package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js +101 -47
- package/codeyam-cli/src/commands/__tests__/init.gitignore.test.js.map +1 -1
- 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 +1619 -243
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/commands/init.js +67 -34
- 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__/analyzerFinalization.test.js +144 -0
- package/codeyam-cli/src/utils/__tests__/analyzerFinalization.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/backgroundServer.test.js +46 -0
- package/codeyam-cli/src/utils/__tests__/backgroundServer.test.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 +610 -1
- package/codeyam-cli/src/utils/__tests__/editorAudit.test.js.map +1 -1
- 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 +181 -3
- package/codeyam-cli/src/utils/__tests__/editorDevServer.test.js.map +1 -1
- 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 +249 -2
- package/codeyam-cli/src/utils/__tests__/editorJournal.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js +520 -0
- package/codeyam-cli/src/utils/__tests__/editorLoaderHelpers.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js +118 -1
- package/codeyam-cli/src/utils/__tests__/editorPreloadHelpers.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js +195 -3
- package/codeyam-cli/src/utils/__tests__/editorPreview.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/editorProxySession.test.js +153 -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 +781 -2
- package/codeyam-cli/src/utils/__tests__/editorScenarios.test.js.map +1 -1
- 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 +1742 -0
- package/codeyam-cli/src/utils/__tests__/entityChangeStatus.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__/scenarioCoverage.test.js +227 -0
- package/codeyam-cli/src/utils/__tests__/scenarioCoverage.test.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/scenariosManifest.test.js +300 -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/analyzer.js +9 -0
- package/codeyam-cli/src/utils/analyzer.js.map +1 -1
- package/codeyam-cli/src/utils/analyzerFinalization.js +96 -0
- package/codeyam-cli/src/utils/analyzerFinalization.js.map +1 -0
- package/codeyam-cli/src/utils/backgroundServer.js +94 -18
- package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
- package/codeyam-cli/src/utils/database.js +37 -2
- package/codeyam-cli/src/utils/database.js.map +1 -1
- 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 +101 -7
- package/codeyam-cli/src/utils/editorAudit.js.map +1 -1
- 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 +100 -1
- package/codeyam-cli/src/utils/editorDevServer.js.map +1 -1
- 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 +92 -4
- package/codeyam-cli/src/utils/editorJournal.js.map +1 -1
- package/codeyam-cli/src/utils/editorLoaderHelpers.js +113 -0
- package/codeyam-cli/src/utils/editorLoaderHelpers.js.map +1 -0
- package/codeyam-cli/src/utils/editorMockState.js +1 -1
- package/codeyam-cli/src/utils/editorPreloadHelpers.js +72 -1
- package/codeyam-cli/src/utils/editorPreloadHelpers.js.map +1 -1
- package/codeyam-cli/src/utils/editorPreview.js +67 -1
- package/codeyam-cli/src/utils/editorPreview.js.map +1 -1
- 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 +276 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- 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 +349 -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/install-skills.js +1 -1
- 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/scenarioCoverage.js +75 -0
- package/codeyam-cli/src/utils/scenarioCoverage.js.map +1 -0
- package/codeyam-cli/src/utils/scenariosManifest.js +159 -0
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -0
- package/codeyam-cli/src/utils/serverState.js +30 -0
- package/codeyam-cli/src/utils/serverState.js.map +1 -1
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +46 -16
- package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
- package/codeyam-cli/src/utils/simulationGateMiddleware.js +8 -1
- package/codeyam-cli/src/utils/simulationGateMiddleware.js.map +1 -1
- package/codeyam-cli/src/utils/slugUtils.js +25 -0
- package/codeyam-cli/src/utils/slugUtils.js.map +1 -0
- package/codeyam-cli/src/utils/syncMocksMiddleware.js +2 -2
- package/codeyam-cli/src/utils/syncMocksMiddleware.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__/clientErrors.test.js +40 -0
- package/codeyam-cli/src/webserver/__tests__/clientErrors.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js +567 -0
- package/codeyam-cli/src/webserver/__tests__/editorProxy.test.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js +65 -0
- package/codeyam-cli/src/webserver/app/lib/clientErrors.js.map +1 -0
- package/codeyam-cli/src/webserver/app/lib/git.js +397 -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-Bd-hxofb.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-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-project-info-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-scenario-coverage-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-BsDh6TSF.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/editor-PBc_6L9R.js +10 -0
- package/codeyam-cli/src/webserver/build/client/assets/editorPreview-4FzHlcNn.js +41 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-CrjR3zZW.js → entity._sha._-BsDXNp45.js} +3 -3
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-BgAqUtTZ.js +6 -0
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.fullscreen-Bmshgrij.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-B8vTTNy2.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-65850841.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/memory-9gnxSZlb.js +101 -0
- 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-BwX8YgFb.js +67 -0
- 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-BE43Hjti.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/index-DEEQf4pi.js +1 -0
- package/codeyam-cli/src/webserver/build/server/assets/init-CkWmyFY2.js +10 -0
- package/codeyam-cli/src/webserver/build/server/assets/server-build-BHi-9O8W.js +439 -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 +487 -50
- package/codeyam-cli/src/webserver/editorProxy.js.map +1 -1
- package/codeyam-cli/src/webserver/scripts/codeyam-preload.mjs +242 -3
- package/codeyam-cli/src/webserver/scripts/journalCapture.ts +94 -4
- package/codeyam-cli/src/webserver/server.js +46 -14
- package/codeyam-cli/src/webserver/server.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +39 -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/README.md +46 -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 +27 -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/codeyam-editor-claude.md +84 -5
- package/codeyam-cli/templates/editor-step-hook.py +97 -8
- package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +89 -0
- package/codeyam-cli/templates/expo-react-native/README.md +41 -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 +38 -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 +126 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/FEATURE_PATTERNS.md +37 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/README.md +53 -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 +21 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/package.json +5 -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-supabase/README.md +52 -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 +37 -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/skills/codeyam-dev-mode/SKILL.md +2 -2
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +96 -17
- package/codeyam-cli/templates/skills/codeyam-memory/SKILL.md +10 -10
- 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/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/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 +14 -9
- 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 +31 -0
- package/packages/database/src/lib/kysely/tables/editorScenariosTable.js.map +1 -1
- package/packages/database/src/lib/loadEntities.js +0 -6
- package/packages/database/src/lib/loadEntities.js.map +1 -1
- package/packages/database/src/lib/updateCommitMetadata.js +0 -25
- package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
- package/packages/types/src/enums/ProjectFramework.js +2 -0
- package/packages/types/src/enums/ProjectFramework.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Terminal-Dnj5CY9R.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-D1DAKXtT.js +0 -8
- package/codeyam-cli/src/webserver/build/client/assets/entity._sha.scenarios._scenarioId.dev-DkzqFzFj.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-B17TBSS6.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-a632de18.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/memory-Dg0mvYrI.js +0 -96
- package/codeyam-cli/src/webserver/build/client/assets/root-DUKqhFlb.js +0 -67
- package/codeyam-cli/src/webserver/build/client/assets/useCustomSizes-ByhSyh0W.js +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/index-HfLydfDq.js +0 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-CUu_F-oo.js +0 -366
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/detect-deprecated-patterns.sh +0 -108
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/holistic-analysis/find-exports.sh +0 -69
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/cleanup.sh +0 -12
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/filter.jq +0 -45
- package/codeyam-cli/templates/skills/codeyam-memory/scripts/session-mining/preprocess.sh +0 -139
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { parseClientErrors } from "../app/lib/clientErrors.js";
|
|
2
|
+
describe('parseClientErrors', () => {
|
|
3
|
+
it('extracts console.error lines', () => {
|
|
4
|
+
const output = `[JournalCapture] Page console.error: Uncaught TypeError: x is not a function`;
|
|
5
|
+
expect(parseClientErrors(output)).toEqual([
|
|
6
|
+
'Uncaught TypeError: x is not a function',
|
|
7
|
+
]);
|
|
8
|
+
});
|
|
9
|
+
it('extracts network failure lines', () => {
|
|
10
|
+
const output = `[JournalCapture] Network failed: GET http://localhost:3113/api/products — net::ERR_CONNECTION_REFUSED`;
|
|
11
|
+
expect(parseClientErrors(output)).toEqual([
|
|
12
|
+
'Network failed: GET http://localhost:3113/api/products — net::ERR_CONNECTION_REFUSED',
|
|
13
|
+
]);
|
|
14
|
+
});
|
|
15
|
+
it('filters out /api/health network failures', () => {
|
|
16
|
+
const output = [
|
|
17
|
+
`[JournalCapture] Network failed: GET http://127.0.0.1:3111/api/health — net::ERR_CONNECTION_REFUSED`,
|
|
18
|
+
`[JournalCapture] Network failed: GET http://localhost:3113/api/products — net::ERR_CONNECTION_REFUSED`,
|
|
19
|
+
`[JournalCapture] Page console.error: Something broke`,
|
|
20
|
+
].join('\n');
|
|
21
|
+
expect(parseClientErrors(output)).toEqual([
|
|
22
|
+
'Network failed: GET http://localhost:3113/api/products — net::ERR_CONNECTION_REFUSED',
|
|
23
|
+
'Something broke',
|
|
24
|
+
]);
|
|
25
|
+
});
|
|
26
|
+
it('filters out /__codeyam__/preview-health network failures', () => {
|
|
27
|
+
const output = [
|
|
28
|
+
`[JournalCapture] Network failed: POST http://localhost:3112/__codeyam__/preview-health — net::ERR_ABORTED`,
|
|
29
|
+
`[JournalCapture] Network failed: GET http://localhost:3113/api/products — net::ERR_CONNECTION_REFUSED`,
|
|
30
|
+
].join('\n');
|
|
31
|
+
expect(parseClientErrors(output)).toEqual([
|
|
32
|
+
'Network failed: GET http://localhost:3113/api/products — net::ERR_CONNECTION_REFUSED',
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
it('returns empty array for clean output', () => {
|
|
36
|
+
const output = `[JournalCapture] Screenshot saved\n[JournalCapture] Done`;
|
|
37
|
+
expect(parseClientErrors(output)).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=clientErrors.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clientErrors.test.js","sourceRoot":"","sources":["../../../../../src/webserver/__tests__/clientErrors.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,MAAM,GAAG,8EAA8E,CAAC;QAC9F,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,yCAAyC;SAC1C,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,MAAM,GAAG,uGAAuG,CAAC;QACvH,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,sFAAsF;SACvF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,MAAM,GAAG;YACb,qGAAqG;YACrG,uGAAuG;YACvG,sDAAsD;SACvD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,sFAAsF;YACtF,iBAAiB;SAClB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,MAAM,GAAG;YACb,2GAA2G;YAC3G,uGAAuG;SACxG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACxC,sFAAsF;SACvF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,MAAM,GAAG,0DAA0D,CAAC;QAC1E,MAAM,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import zlib from 'zlib';
|
|
3
|
+
import { injectHealthScript, PREVIEW_HEALTH_SCRIPT, getPreviewHealthReport, resetPreviewHealth, } from "../editorProxy.js";
|
|
4
|
+
describe('editorProxy', () => {
|
|
5
|
+
describe('normalizeTargetUrl', () => {
|
|
6
|
+
it('should leave localhost as-is (no longer normalizes to 127.0.0.1)', () => {
|
|
7
|
+
const { normalizeTargetUrl } = require('../editorProxy');
|
|
8
|
+
expect(normalizeTargetUrl('http://localhost:3112')).toBe('http://localhost:3112');
|
|
9
|
+
});
|
|
10
|
+
it('should leave 127.0.0.1 unchanged', () => {
|
|
11
|
+
const { normalizeTargetUrl } = require('../editorProxy');
|
|
12
|
+
expect(normalizeTargetUrl('http://127.0.0.1:3112')).toBe('http://127.0.0.1:3112');
|
|
13
|
+
});
|
|
14
|
+
it('should leave other hostnames unchanged', () => {
|
|
15
|
+
const { normalizeTargetUrl } = require('../editorProxy');
|
|
16
|
+
expect(normalizeTargetUrl('http://myhost:3112')).toBe('http://myhost:3112');
|
|
17
|
+
});
|
|
18
|
+
it('should strip trailing slash', () => {
|
|
19
|
+
const { normalizeTargetUrl } = require('../editorProxy');
|
|
20
|
+
expect(normalizeTargetUrl('http://localhost:3112/')).toBe('http://localhost:3112');
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('resolveLoopbackAddress', () => {
|
|
24
|
+
let server;
|
|
25
|
+
afterEach((done) => {
|
|
26
|
+
if (server?.listening) {
|
|
27
|
+
server.close(done);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
done();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
it('should resolve to 127.0.0.1 when server binds to IPv4', async () => {
|
|
34
|
+
const { resolveLoopbackAddress } = require('../editorProxy');
|
|
35
|
+
server = http.createServer((_req, res) => res.end('ok'));
|
|
36
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', () => resolve()));
|
|
37
|
+
const port = server.address().port;
|
|
38
|
+
const host = await resolveLoopbackAddress(port);
|
|
39
|
+
expect(host).toBe('127.0.0.1');
|
|
40
|
+
});
|
|
41
|
+
it('should resolve to ::1 when server binds to IPv6 only', async () => {
|
|
42
|
+
const { resolveLoopbackAddress } = require('../editorProxy');
|
|
43
|
+
server = http.createServer((_req, res) => res.end('ok'));
|
|
44
|
+
await new Promise((resolve) => server.listen(0, '::1', () => resolve()));
|
|
45
|
+
const port = server.address().port;
|
|
46
|
+
const host = await resolveLoopbackAddress(port);
|
|
47
|
+
expect(host).toBe('::1');
|
|
48
|
+
});
|
|
49
|
+
it('should return null when no server is listening', async () => {
|
|
50
|
+
const { resolveLoopbackAddress } = require('../editorProxy');
|
|
51
|
+
const host = await resolveLoopbackAddress(19876);
|
|
52
|
+
expect(host).toBeNull();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe('proxy forwarding uses 127.0.0.1', () => {
|
|
56
|
+
let targetServer;
|
|
57
|
+
let targetPort;
|
|
58
|
+
afterEach((done) => {
|
|
59
|
+
if (targetServer?.listening) {
|
|
60
|
+
targetServer.close(done);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
done();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
it('should forward requests to target and return correct response', async () => {
|
|
67
|
+
const { startEditorProxy, stopEditorProxy } = require('../editorProxy');
|
|
68
|
+
// Start a target server that returns 200
|
|
69
|
+
targetServer = http.createServer((_req, res) => {
|
|
70
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
71
|
+
res.end('OK from target');
|
|
72
|
+
});
|
|
73
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
74
|
+
targetPort = targetServer.address().port;
|
|
75
|
+
// Start proxy pointing to the target using localhost URL
|
|
76
|
+
const result = await startEditorProxy({
|
|
77
|
+
port: 0, // let OS pick
|
|
78
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
79
|
+
});
|
|
80
|
+
expect(result).not.toBeNull();
|
|
81
|
+
// Make a request through the proxy
|
|
82
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/`);
|
|
83
|
+
expect(response.status).toBe(200);
|
|
84
|
+
const body = await response.text();
|
|
85
|
+
expect(body).toBe('OK from target');
|
|
86
|
+
await stopEditorProxy();
|
|
87
|
+
});
|
|
88
|
+
it('should log when target returns non-2xx status', async () => {
|
|
89
|
+
const { startEditorProxy, stopEditorProxy } = require('../editorProxy');
|
|
90
|
+
// Start a target that returns 404
|
|
91
|
+
targetServer = http.createServer((_req, res) => {
|
|
92
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
93
|
+
res.end('Not Found');
|
|
94
|
+
});
|
|
95
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
96
|
+
targetPort = targetServer.address().port;
|
|
97
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
98
|
+
const result = await startEditorProxy({
|
|
99
|
+
port: 0,
|
|
100
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
101
|
+
});
|
|
102
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/test-path`);
|
|
103
|
+
expect(response.status).toBe(404);
|
|
104
|
+
// Verify a warning was logged about the non-2xx response
|
|
105
|
+
expect(warnSpy.mock.calls.some((call) => String(call[0]).includes('[editorProxy]') &&
|
|
106
|
+
String(call[0]).includes('404') &&
|
|
107
|
+
String(call[0]).includes('/test-path'))).toBe(true);
|
|
108
|
+
warnSpy.mockRestore();
|
|
109
|
+
await stopEditorProxy();
|
|
110
|
+
});
|
|
111
|
+
it('should verify forwarding works after start via self-test', async () => {
|
|
112
|
+
const { startEditorProxy, stopEditorProxy, verifyProxyForwarding, } = require('../editorProxy');
|
|
113
|
+
// Start a target that returns 200
|
|
114
|
+
targetServer = http.createServer((_req, res) => {
|
|
115
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
116
|
+
res.end('healthy');
|
|
117
|
+
});
|
|
118
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
119
|
+
targetPort = targetServer.address().port;
|
|
120
|
+
const result = await startEditorProxy({
|
|
121
|
+
port: 0,
|
|
122
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
123
|
+
});
|
|
124
|
+
const verified = await verifyProxyForwarding();
|
|
125
|
+
expect(verified).toBe(true);
|
|
126
|
+
await stopEditorProxy();
|
|
127
|
+
});
|
|
128
|
+
it('should report verification failure when target is down', async () => {
|
|
129
|
+
const { startEditorProxy, stopEditorProxy, verifyProxyForwarding, } = require('../editorProxy');
|
|
130
|
+
const result = await startEditorProxy({
|
|
131
|
+
port: 0,
|
|
132
|
+
targetUrl: 'http://127.0.0.1:19876',
|
|
133
|
+
});
|
|
134
|
+
const verified = await verifyProxyForwarding();
|
|
135
|
+
expect(verified).toBe(false);
|
|
136
|
+
await stopEditorProxy();
|
|
137
|
+
});
|
|
138
|
+
it('should return false for verification when proxy is not running', async () => {
|
|
139
|
+
const { verifyProxyForwarding, stopEditorProxy, } = require('../editorProxy');
|
|
140
|
+
await stopEditorProxy();
|
|
141
|
+
const verified = await verifyProxyForwarding();
|
|
142
|
+
expect(verified).toBe(false);
|
|
143
|
+
});
|
|
144
|
+
it('should return 502 when target is unreachable', async () => {
|
|
145
|
+
const { startEditorProxy, stopEditorProxy } = require('../editorProxy');
|
|
146
|
+
// Start proxy pointing to a port with nothing listening
|
|
147
|
+
const result = await startEditorProxy({
|
|
148
|
+
port: 0,
|
|
149
|
+
targetUrl: 'http://localhost:19876',
|
|
150
|
+
});
|
|
151
|
+
expect(result).not.toBeNull();
|
|
152
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/`);
|
|
153
|
+
expect(response.status).toBe(502);
|
|
154
|
+
await stopEditorProxy();
|
|
155
|
+
});
|
|
156
|
+
it('should forward requests to an IPv6-only target server', async () => {
|
|
157
|
+
const { startEditorProxy, stopEditorProxy } = require('../editorProxy');
|
|
158
|
+
// Start a target server bound to IPv6 only (::1) — this is what Vite 6 does on macOS
|
|
159
|
+
targetServer = http.createServer((_req, res) => {
|
|
160
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
161
|
+
res.end('OK from IPv6 target');
|
|
162
|
+
});
|
|
163
|
+
await new Promise((resolve) => targetServer.listen(0, '::1', () => resolve()));
|
|
164
|
+
targetPort = targetServer.address().port;
|
|
165
|
+
// Start proxy with a localhost URL (simulating Vite's "Local: http://localhost:PORT")
|
|
166
|
+
const result = await startEditorProxy({
|
|
167
|
+
port: 0,
|
|
168
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
169
|
+
});
|
|
170
|
+
expect(result).not.toBeNull();
|
|
171
|
+
// The proxy should successfully forward to the IPv6-only target
|
|
172
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/`);
|
|
173
|
+
expect(response.status).toBe(200);
|
|
174
|
+
const body = await response.text();
|
|
175
|
+
expect(body).toBe('OK from IPv6 target');
|
|
176
|
+
await stopEditorProxy();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
describe('injectHealthScript', () => {
|
|
180
|
+
it('should inject before </head> when present', () => {
|
|
181
|
+
const html = '<html><head><title>Test</title></head><body>Hello</body></html>';
|
|
182
|
+
const result = injectHealthScript(html);
|
|
183
|
+
expect(result).toContain(PREVIEW_HEALTH_SCRIPT + '</head>');
|
|
184
|
+
expect(result).toContain('<title>Test</title>');
|
|
185
|
+
});
|
|
186
|
+
it('should inject before </body> when no </head>', () => {
|
|
187
|
+
const html = '<html><body>Hello</body></html>';
|
|
188
|
+
const result = injectHealthScript(html);
|
|
189
|
+
expect(result).toContain(PREVIEW_HEALTH_SCRIPT + '</body>');
|
|
190
|
+
});
|
|
191
|
+
it('should append when no </head> or </body>', () => {
|
|
192
|
+
const html = '<div>Hello</div>';
|
|
193
|
+
const result = injectHealthScript(html);
|
|
194
|
+
expect(result).toBe('<div>Hello</div>' + PREVIEW_HEALTH_SCRIPT);
|
|
195
|
+
});
|
|
196
|
+
it('should include the data-codeyam-health attribute', () => {
|
|
197
|
+
const html = '<html><head></head><body></body></html>';
|
|
198
|
+
const result = injectHealthScript(html);
|
|
199
|
+
expect(result).toContain('data-codeyam-health');
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
describe('preview health endpoint', () => {
|
|
203
|
+
let targetServer;
|
|
204
|
+
let targetPort;
|
|
205
|
+
afterEach(async () => {
|
|
206
|
+
const { stopEditorProxy } = require('../editorProxy');
|
|
207
|
+
await stopEditorProxy();
|
|
208
|
+
if (targetServer?.listening) {
|
|
209
|
+
await new Promise((resolve) => targetServer.close(() => resolve()));
|
|
210
|
+
}
|
|
211
|
+
resetPreviewHealth();
|
|
212
|
+
});
|
|
213
|
+
it('should intercept POST /__codeyam__/preview-health and store errors', async () => {
|
|
214
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
215
|
+
// Start a target that returns 200
|
|
216
|
+
targetServer = http.createServer((_req, res) => {
|
|
217
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
218
|
+
res.end('ok');
|
|
219
|
+
});
|
|
220
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
221
|
+
targetPort = targetServer.address().port;
|
|
222
|
+
const result = await startEditorProxy({
|
|
223
|
+
port: 0,
|
|
224
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
225
|
+
});
|
|
226
|
+
expect(result).not.toBeNull();
|
|
227
|
+
// Post error data to the health endpoint
|
|
228
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/__codeyam__/preview-health`, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
headers: { 'Content-Type': 'application/json' },
|
|
231
|
+
body: JSON.stringify({
|
|
232
|
+
errors: [
|
|
233
|
+
{
|
|
234
|
+
type: 'error',
|
|
235
|
+
message: 'Cannot read properties of undefined',
|
|
236
|
+
timestamp: 1710000000,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
url: 'http://localhost:3112/',
|
|
240
|
+
}),
|
|
241
|
+
});
|
|
242
|
+
expect(response.status).toBe(204);
|
|
243
|
+
// Check the stored health data
|
|
244
|
+
const health = getPreviewHealthReport();
|
|
245
|
+
expect(health).not.toBeNull();
|
|
246
|
+
expect(health.errors).toHaveLength(1);
|
|
247
|
+
expect(health.errors[0].message).toBe('Cannot read properties of undefined');
|
|
248
|
+
expect(health.url).toBe('http://localhost:3112/');
|
|
249
|
+
});
|
|
250
|
+
it('should store loaded/hasContent from load report', async () => {
|
|
251
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
252
|
+
targetServer = http.createServer((_req, res) => {
|
|
253
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
254
|
+
res.end('ok');
|
|
255
|
+
});
|
|
256
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
257
|
+
targetPort = targetServer.address().port;
|
|
258
|
+
const result = await startEditorProxy({
|
|
259
|
+
port: 0,
|
|
260
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
261
|
+
});
|
|
262
|
+
// Post load report
|
|
263
|
+
await fetch(`http://127.0.0.1:${result.port}/__codeyam__/preview-health`, {
|
|
264
|
+
method: 'POST',
|
|
265
|
+
headers: { 'Content-Type': 'application/json' },
|
|
266
|
+
body: JSON.stringify({
|
|
267
|
+
loaded: true,
|
|
268
|
+
hasContent: true,
|
|
269
|
+
url: 'http://localhost:3112/',
|
|
270
|
+
errorCount: 0,
|
|
271
|
+
}),
|
|
272
|
+
});
|
|
273
|
+
const health = getPreviewHealthReport();
|
|
274
|
+
expect(health).not.toBeNull();
|
|
275
|
+
expect(health.loaded).toBe(true);
|
|
276
|
+
expect(health.hasContent).toBe(true);
|
|
277
|
+
});
|
|
278
|
+
it('should inject health script into HTML responses', async () => {
|
|
279
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
280
|
+
// Target that returns HTML
|
|
281
|
+
targetServer = http.createServer((_req, res) => {
|
|
282
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
283
|
+
res.end('<html><head><title>App</title></head><body><div>Hello</div></body></html>');
|
|
284
|
+
});
|
|
285
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
286
|
+
targetPort = targetServer.address().port;
|
|
287
|
+
const result = await startEditorProxy({
|
|
288
|
+
port: 0,
|
|
289
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
290
|
+
});
|
|
291
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/`);
|
|
292
|
+
const body = await response.text();
|
|
293
|
+
expect(body).toContain('data-codeyam-health');
|
|
294
|
+
expect(body).toContain('/__codeyam__/preview-health');
|
|
295
|
+
expect(body).toContain('<title>App</title>');
|
|
296
|
+
expect(body).toContain('<div>Hello</div>');
|
|
297
|
+
});
|
|
298
|
+
it('should NOT inject health script into non-HTML responses', async () => {
|
|
299
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
300
|
+
targetServer = http.createServer((_req, res) => {
|
|
301
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
302
|
+
res.end('{"data": "test"}');
|
|
303
|
+
});
|
|
304
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
305
|
+
targetPort = targetServer.address().port;
|
|
306
|
+
const result = await startEditorProxy({
|
|
307
|
+
port: 0,
|
|
308
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
309
|
+
});
|
|
310
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/api/data`);
|
|
311
|
+
const body = await response.text();
|
|
312
|
+
expect(body).toBe('{"data": "test"}');
|
|
313
|
+
expect(body).not.toContain('data-codeyam-health');
|
|
314
|
+
});
|
|
315
|
+
it('should accumulate errors across multiple health reports', async () => {
|
|
316
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
317
|
+
targetServer = http.createServer((_req, res) => {
|
|
318
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
319
|
+
res.end('ok');
|
|
320
|
+
});
|
|
321
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
322
|
+
targetPort = targetServer.address().port;
|
|
323
|
+
const result = await startEditorProxy({
|
|
324
|
+
port: 0,
|
|
325
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
326
|
+
});
|
|
327
|
+
// First error report
|
|
328
|
+
await fetch(`http://127.0.0.1:${result.port}/__codeyam__/preview-health`, {
|
|
329
|
+
method: 'POST',
|
|
330
|
+
headers: { 'Content-Type': 'application/json' },
|
|
331
|
+
body: JSON.stringify({
|
|
332
|
+
errors: [{ type: 'error', message: 'Error 1', timestamp: 1 }],
|
|
333
|
+
url: 'http://localhost:3112/',
|
|
334
|
+
}),
|
|
335
|
+
});
|
|
336
|
+
// Second error report
|
|
337
|
+
await fetch(`http://127.0.0.1:${result.port}/__codeyam__/preview-health`, {
|
|
338
|
+
method: 'POST',
|
|
339
|
+
headers: { 'Content-Type': 'application/json' },
|
|
340
|
+
body: JSON.stringify({
|
|
341
|
+
errors: [
|
|
342
|
+
{ type: 'console.error', message: 'Error 2', timestamp: 2 },
|
|
343
|
+
],
|
|
344
|
+
url: 'http://localhost:3112/',
|
|
345
|
+
}),
|
|
346
|
+
});
|
|
347
|
+
const health = getPreviewHealthReport();
|
|
348
|
+
expect(health).not.toBeNull();
|
|
349
|
+
expect(health.errors).toHaveLength(2);
|
|
350
|
+
expect(health.errors[0].message).toBe('Error 1');
|
|
351
|
+
expect(health.errors[1].message).toBe('Error 2');
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
describe('buildLocalStorageScript', () => {
|
|
355
|
+
it('should return empty string when no localStorage config', () => {
|
|
356
|
+
const { buildLocalStorageScript } = require('../editorProxy');
|
|
357
|
+
expect(buildLocalStorageScript(null, 'scenario-123')).toBe('');
|
|
358
|
+
expect(buildLocalStorageScript(undefined, 'scenario-123')).toBe('');
|
|
359
|
+
});
|
|
360
|
+
it('should emit cleanup script when localStorage config is empty object', () => {
|
|
361
|
+
// BUG FIX: When switching from a scenario with localStorage data to one
|
|
362
|
+
// with empty localStorage (e.g. "First Launch - Welcome"), we must still
|
|
363
|
+
// clean up the previous scenario's keys. Otherwise the app reads stale data.
|
|
364
|
+
const { buildLocalStorageScript } = require('../editorProxy');
|
|
365
|
+
const script = buildLocalStorageScript({}, 'scenario-123');
|
|
366
|
+
expect(script).not.toBe('');
|
|
367
|
+
// Should clean up previous keys
|
|
368
|
+
expect(script).toContain('localStorage.removeItem');
|
|
369
|
+
expect(script).toContain('__codeyam_ls_keys__');
|
|
370
|
+
expect(script).toContain('__codeyam_ls_sid__');
|
|
371
|
+
// Should NOT set any new keys (empty config)
|
|
372
|
+
expect(script).not.toContain('localStorage.setItem(\"margo');
|
|
373
|
+
});
|
|
374
|
+
it('should generate a script that sets localStorage entries', () => {
|
|
375
|
+
const { buildLocalStorageScript } = require('../editorProxy');
|
|
376
|
+
const config = {
|
|
377
|
+
margo_articles: [{ id: 'a1', title: 'Test Article' }],
|
|
378
|
+
margo_collections: [{ id: 'c1', name: 'Test Collection' }],
|
|
379
|
+
};
|
|
380
|
+
const script = buildLocalStorageScript(config, 'scenario-abc');
|
|
381
|
+
// Should be wrapped in a script tag
|
|
382
|
+
expect(script).toMatch(/^<script data-codeyam-ls>/);
|
|
383
|
+
expect(script).toMatch(/<\/script>$/);
|
|
384
|
+
// Should gate on scenario ID to avoid re-seeding on reload
|
|
385
|
+
expect(script).toContain('scenario-abc');
|
|
386
|
+
expect(script).toContain('__codeyam_ls_sid__');
|
|
387
|
+
// Should set the localStorage entries
|
|
388
|
+
expect(script).toContain('margo_articles');
|
|
389
|
+
expect(script).toContain('margo_collections');
|
|
390
|
+
expect(script).toContain('localStorage.setItem');
|
|
391
|
+
// Should track which keys were set for cleanup
|
|
392
|
+
expect(script).toContain('__codeyam_ls_keys__');
|
|
393
|
+
});
|
|
394
|
+
it('should emit gated localStorage.clear() when prototypeId is provided and config is null', () => {
|
|
395
|
+
const { buildLocalStorageScript } = require('../editorProxy');
|
|
396
|
+
const script = buildLocalStorageScript(null, 'scenario-123', 'proto-abc');
|
|
397
|
+
// Should not be empty — prototypeId triggers a clear script
|
|
398
|
+
expect(script).not.toBe('');
|
|
399
|
+
expect(script).toContain('localStorage.clear()');
|
|
400
|
+
// Should be gated by the __codeyam_proto__ marker
|
|
401
|
+
expect(script).toContain('__codeyam_proto__');
|
|
402
|
+
expect(script).toContain('proto-abc');
|
|
403
|
+
});
|
|
404
|
+
it('should not emit clear script when prototypeId is absent and config is null', () => {
|
|
405
|
+
const { buildLocalStorageScript } = require('../editorProxy');
|
|
406
|
+
// No prototypeId — same as before
|
|
407
|
+
expect(buildLocalStorageScript(null, 'scenario-123')).toBe('');
|
|
408
|
+
expect(buildLocalStorageScript(null, 'scenario-123', null)).toBe('');
|
|
409
|
+
expect(buildLocalStorageScript(undefined, 'scenario-123', null)).toBe('');
|
|
410
|
+
});
|
|
411
|
+
it('should clean up keys from previous scenario before setting new ones', () => {
|
|
412
|
+
const { buildLocalStorageScript } = require('../editorProxy');
|
|
413
|
+
const config = { my_key: 'my_value' };
|
|
414
|
+
const script = buildLocalStorageScript(config, 'scenario-xyz');
|
|
415
|
+
// Should read previous keys and remove them
|
|
416
|
+
expect(script).toContain('__codeyam_ls_keys__');
|
|
417
|
+
expect(script).toContain('localStorage.removeItem');
|
|
418
|
+
});
|
|
419
|
+
it('should JSON.stringify non-string values', () => {
|
|
420
|
+
const { buildLocalStorageScript } = require('../editorProxy');
|
|
421
|
+
const config = {
|
|
422
|
+
string_key: 'plain string',
|
|
423
|
+
object_key: { nested: true },
|
|
424
|
+
array_key: [1, 2, 3],
|
|
425
|
+
};
|
|
426
|
+
const script = buildLocalStorageScript(config, 'scenario-1');
|
|
427
|
+
// Object/array values should be JSON-stringified and embedded as JS string literals.
|
|
428
|
+
// JSON.stringify is applied twice: once to serialize the value, once to make it a safe JS string.
|
|
429
|
+
// So {"nested":true} becomes "{\"nested\":true}" in the script source.
|
|
430
|
+
expect(script).toContain('object_key');
|
|
431
|
+
expect(script).toContain('nested');
|
|
432
|
+
expect(script).toContain('array_key');
|
|
433
|
+
expect(script).toContain('[1,2,3]');
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
describe('localStorage injection in HTML responses', () => {
|
|
437
|
+
it('should inject localStorage script before health script in HTML', () => {
|
|
438
|
+
const { buildLocalStorageScript, injectHealthScript, } = require('../editorProxy');
|
|
439
|
+
const lsScript = buildLocalStorageScript({ items: [1, 2] }, 'scenario-1');
|
|
440
|
+
// localStorage script should be injected before the health script
|
|
441
|
+
// (needs to run before app loads)
|
|
442
|
+
const html = '<html><head><title>Test</title></head><body>Hello</body></html>';
|
|
443
|
+
const result = injectHealthScript(html, lsScript);
|
|
444
|
+
expect(result).toContain('data-codeyam-ls');
|
|
445
|
+
expect(result).toContain('data-codeyam-health');
|
|
446
|
+
// localStorage script should appear BEFORE health script in the output
|
|
447
|
+
const lsIdx = result.indexOf('data-codeyam-ls');
|
|
448
|
+
const healthIdx = result.indexOf('data-codeyam-health');
|
|
449
|
+
expect(lsIdx).toBeLessThan(healthIdx);
|
|
450
|
+
});
|
|
451
|
+
it('should not inject localStorage script when none provided', () => {
|
|
452
|
+
const html = '<html><head><title>Test</title></head><body>Hello</body></html>';
|
|
453
|
+
const result = injectHealthScript(html);
|
|
454
|
+
expect(result).toContain('data-codeyam-health');
|
|
455
|
+
expect(result).not.toContain('data-codeyam-ls');
|
|
456
|
+
});
|
|
457
|
+
});
|
|
458
|
+
describe('compressed response handling', () => {
|
|
459
|
+
let targetServer;
|
|
460
|
+
let targetPort;
|
|
461
|
+
afterEach(async () => {
|
|
462
|
+
const { stopEditorProxy } = require('../editorProxy');
|
|
463
|
+
await stopEditorProxy();
|
|
464
|
+
if (targetServer?.listening) {
|
|
465
|
+
await new Promise((resolve) => targetServer.close(() => resolve()));
|
|
466
|
+
}
|
|
467
|
+
resetPreviewHealth();
|
|
468
|
+
});
|
|
469
|
+
it('should strip accept-encoding so target returns uncompressed HTML for injection', async () => {
|
|
470
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
471
|
+
const originalHtml = '<html><head><title>Compressed</title></head><body><div>Gzipped</div></body></html>';
|
|
472
|
+
// Target server that compresses only when client sends Accept-Encoding
|
|
473
|
+
// (simulates real dev servers like Next.js/Vite)
|
|
474
|
+
targetServer = http.createServer((req, res) => {
|
|
475
|
+
const acceptEncoding = req.headers['accept-encoding'] || '';
|
|
476
|
+
if (acceptEncoding.includes('gzip')) {
|
|
477
|
+
const compressed = zlib.gzipSync(Buffer.from(originalHtml));
|
|
478
|
+
res.writeHead(200, {
|
|
479
|
+
'Content-Type': 'text/html',
|
|
480
|
+
'Content-Encoding': 'gzip',
|
|
481
|
+
'Content-Length': compressed.length,
|
|
482
|
+
});
|
|
483
|
+
res.end(compressed);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
487
|
+
res.end(originalHtml);
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
491
|
+
targetPort = targetServer.address().port;
|
|
492
|
+
const result = await startEditorProxy({
|
|
493
|
+
port: 0,
|
|
494
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
495
|
+
});
|
|
496
|
+
expect(result).not.toBeNull();
|
|
497
|
+
// Request through proxy WITH Accept-Encoding (simulating a real browser)
|
|
498
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/`, {
|
|
499
|
+
headers: { 'Accept-Encoding': 'gzip, deflate, br' },
|
|
500
|
+
});
|
|
501
|
+
// The proxy should return a valid, readable response
|
|
502
|
+
expect(response.status).toBe(200);
|
|
503
|
+
const body = await response.text();
|
|
504
|
+
// Should contain the original HTML content (not corrupted binary)
|
|
505
|
+
expect(body).toContain('<title>Compressed</title>');
|
|
506
|
+
expect(body).toContain('<div>Gzipped</div>');
|
|
507
|
+
// Should have injected the health script
|
|
508
|
+
expect(body).toContain('data-codeyam-health');
|
|
509
|
+
// Should NOT have Content-Encoding since the proxy serves uncompressed
|
|
510
|
+
expect(response.headers.get('content-encoding')).toBeNull();
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
describe('Cache-Control on HTML responses', () => {
|
|
514
|
+
let targetServer;
|
|
515
|
+
let targetPort;
|
|
516
|
+
afterEach(async () => {
|
|
517
|
+
const { stopEditorProxy } = require('../editorProxy');
|
|
518
|
+
await stopEditorProxy();
|
|
519
|
+
if (targetServer?.listening) {
|
|
520
|
+
await new Promise((resolve) => targetServer.close(() => resolve()));
|
|
521
|
+
}
|
|
522
|
+
resetPreviewHealth();
|
|
523
|
+
});
|
|
524
|
+
it('should add Cache-Control: no-store to proxied HTML responses', async () => {
|
|
525
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
526
|
+
// Target server that serves plain HTML without cache headers
|
|
527
|
+
targetServer = http.createServer((_req, res) => {
|
|
528
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
529
|
+
res.end('<html><head><title>Test</title></head><body>Hello</body></html>');
|
|
530
|
+
});
|
|
531
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
532
|
+
targetPort = targetServer.address().port;
|
|
533
|
+
const result = await startEditorProxy({
|
|
534
|
+
port: 0,
|
|
535
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
536
|
+
});
|
|
537
|
+
expect(result).not.toBeNull();
|
|
538
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/`);
|
|
539
|
+
expect(response.status).toBe(200);
|
|
540
|
+
// Proxy must set no-store so the browser doesn't cache pages between
|
|
541
|
+
// scenario switches (application scenarios share the same proxy URL
|
|
542
|
+
// but differ in seed data).
|
|
543
|
+
expect(response.headers.get('cache-control')).toBe('no-store, must-revalidate');
|
|
544
|
+
});
|
|
545
|
+
it('should NOT add Cache-Control: no-store to non-HTML responses', async () => {
|
|
546
|
+
const { startEditorProxy } = require('../editorProxy');
|
|
547
|
+
// Target server that serves JSON without cache headers
|
|
548
|
+
targetServer = http.createServer((_req, res) => {
|
|
549
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
550
|
+
res.end(JSON.stringify({ ok: true }));
|
|
551
|
+
});
|
|
552
|
+
await new Promise((resolve) => targetServer.listen(0, '127.0.0.1', () => resolve()));
|
|
553
|
+
targetPort = targetServer.address().port;
|
|
554
|
+
const result = await startEditorProxy({
|
|
555
|
+
port: 0,
|
|
556
|
+
targetUrl: `http://localhost:${targetPort}`,
|
|
557
|
+
});
|
|
558
|
+
expect(result).not.toBeNull();
|
|
559
|
+
const response = await fetch(`http://127.0.0.1:${result.port}/api/data`);
|
|
560
|
+
expect(response.status).toBe(200);
|
|
561
|
+
// Non-HTML responses pass through the target's original headers;
|
|
562
|
+
// the proxy only forces no-store on HTML.
|
|
563
|
+
expect(response.headers.get('cache-control')).toBeNull();
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
//# sourceMappingURL=editorProxy.test.js.map
|