@ecopages/core 0.2.0-alpha.4 → 0.2.0-alpha.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +213 -12
- package/package.json +100 -188
- package/src/adapters/README.md +39 -0
- package/src/adapters/bun/hmr-manager.test.ts +267 -0
- package/src/adapters/bun/hmr-manager.ts +181 -68
- package/src/adapters/bun/index.ts +1 -2
- package/src/adapters/bun/server-adapter.ts +41 -34
- package/src/adapters/bun/server-lifecycle.ts +40 -70
- package/src/adapters/index.ts +1 -1
- package/src/adapters/node/bootstrap-dependency-resolver.test.ts +282 -0
- package/src/adapters/node/bootstrap-dependency-resolver.ts +301 -0
- package/src/adapters/node/index.ts +7 -0
- package/src/adapters/node/node-client-bridge.test.ts +198 -0
- package/src/adapters/node/node-hmr-manager.test.ts +322 -0
- package/src/adapters/node/node-hmr-manager.ts +208 -116
- package/src/adapters/node/runtime-adapter.test.ts +868 -0
- package/src/adapters/node/runtime-adapter.ts +439 -0
- package/src/adapters/node/server-adapter.ts +31 -104
- package/src/adapters/node/static-content-server.test.ts +60 -0
- package/src/adapters/node/static-content-server.ts +36 -0
- package/src/adapters/node/write-runtime-manifest.ts +38 -0
- package/src/adapters/shared/api-response.test.ts +97 -0
- package/src/{define-api-handler.ts → adapters/shared/define-api-handler.ts} +1 -1
- package/src/adapters/shared/explicit-static-route-matcher.test.ts +381 -0
- package/src/adapters/shared/explicit-static-route-matcher.ts +7 -1
- package/src/adapters/shared/file-route-middleware-pipeline.test.ts +90 -0
- package/src/adapters/shared/file-route-middleware-pipeline.ts +6 -2
- package/src/adapters/shared/fs-server-response-factory.test.ts +187 -0
- package/src/adapters/shared/fs-server-response-matcher.test.ts +286 -0
- package/src/adapters/shared/fs-server-response-matcher.ts +17 -10
- package/src/adapters/shared/hmr-entrypoint-registrar.ts +149 -0
- package/src/adapters/shared/hmr-html-response.ts +52 -0
- package/src/adapters/shared/hmr-manager.contract.test.ts +196 -0
- package/src/adapters/shared/hmr-manager.dispatch.test.ts +220 -0
- package/src/adapters/shared/render-context.test.ts +146 -0
- package/src/adapters/shared/render-context.ts +21 -6
- package/src/adapters/shared/runtime-bootstrap.ts +79 -0
- package/src/adapters/shared/server-adapter.test.ts +77 -0
- package/src/adapters/shared/server-adapter.ts +51 -4
- package/src/adapters/shared/server-route-handler.test.ts +110 -0
- package/src/adapters/shared/server-route-handler.ts +5 -18
- package/src/adapters/shared/server-static-builder.test.ts +316 -0
- package/src/adapters/shared/server-static-builder.ts +92 -8
- package/src/build/README.md +101 -0
- package/src/build/build-adapter-serialization.test.ts +268 -0
- package/src/build/build-adapter.test.ts +815 -0
- package/src/build/build-adapter.ts +235 -6
- package/src/build/build-manifest.ts +54 -0
- package/src/build/dev-build-coordinator.ts +221 -0
- package/src/build/esbuild-build-adapter.ts +132 -83
- package/src/build/runtime-build-executor.ts +34 -0
- package/src/build/runtime-specifier-alias-plugin.test.ts +43 -0
- package/src/build/runtime-specifier-alias-plugin.ts +58 -0
- package/src/config/README.md +33 -0
- package/src/config/config-builder.test.ts +410 -0
- package/src/config/config-builder.ts +281 -49
- package/src/constants.ts +15 -0
- package/src/declarations.d.ts +18 -13
- package/src/eco/README.md +70 -16
- package/src/eco/component-render-context.ts +39 -17
- package/src/eco/eco.test.ts +678 -0
- package/src/eco/eco.ts +29 -8
- package/src/eco/eco.types.ts +20 -1
- package/src/eco/eco.utils.test.ts +124 -0
- package/src/eco/global-injector-map.test.ts +42 -0
- package/src/eco/lazy-injector-map.test.ts +66 -0
- package/src/eco/module-dependencies.test.ts +30 -0
- package/src/errors/http-error.test.ts +134 -0
- package/src/global/utils.test.ts +12 -0
- package/src/hmr/README.md +26 -0
- package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-HMR-Server-Integration-should-have-HMR-script-injected-in-page-1.png +0 -0
- package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-HMR-Server-Integration-should-load-fixture-app-page-1.png +0 -0
- package/src/hmr/client/__screenshots__/hmr-runtime.test.browser.ts/HMR-Runtime-WebSocket-Connection-should-connect-to-correct-HMR-endpoint-1.png +0 -0
- package/src/hmr/client/hmr-runtime.ts +38 -7
- package/src/hmr/hmr-strategy.test.ts +124 -0
- package/src/hmr/hmr.postcss.test.e2e.ts +41 -0
- package/src/hmr/hmr.test.e2e.ts +29 -38
- package/src/hmr/strategies/js-hmr-strategy.test.ts +335 -0
- package/src/hmr/strategies/js-hmr-strategy.ts +115 -115
- package/src/index.ts +1 -1
- package/src/integrations/ghtml/ghtml-renderer.test.ts +63 -0
- package/src/integrations/ghtml/ghtml-renderer.ts +4 -1
- package/src/internal-types.ts +39 -19
- package/src/plugins/README.md +34 -0
- package/src/plugins/alias-resolver-plugin.test.ts +41 -0
- package/src/plugins/alias-resolver-plugin.ts +21 -3
- package/src/plugins/eco-component-meta-plugin.test.ts +380 -0
- package/src/plugins/eco-component-meta-plugin.ts +10 -3
- package/src/plugins/integration-plugin.test.ts +111 -0
- package/src/plugins/integration-plugin.ts +45 -3
- package/src/plugins/processor.test.ts +148 -0
- package/src/plugins/processor.ts +22 -2
- package/src/plugins/runtime-capability.ts +14 -0
- package/src/public-types.ts +73 -16
- package/src/route-renderer/GRAPH.md +16 -20
- package/src/route-renderer/README.md +8 -21
- package/src/route-renderer/component-graph/component-graph-executor.test.ts +41 -0
- package/src/route-renderer/component-graph/component-graph.test.ts +63 -0
- package/src/route-renderer/component-graph/component-marker.test.ts +73 -0
- package/src/route-renderer/component-graph/component-reference.ts +29 -0
- package/src/route-renderer/component-graph/marker-graph-resolver.test.ts +135 -0
- package/src/route-renderer/{marker-graph-resolver.ts → component-graph/marker-graph-resolver.ts} +11 -9
- package/src/route-renderer/orchestration/integration-renderer.test.ts +936 -0
- package/src/route-renderer/{integration-renderer.ts → orchestration/integration-renderer.ts} +113 -19
- package/src/route-renderer/orchestration/render-execution.service.test.ts +97 -0
- package/src/route-renderer/{render-execution.service.ts → orchestration/render-execution.service.ts} +109 -37
- package/src/route-renderer/orchestration/render-preparation.service.test.ts +235 -0
- package/src/route-renderer/{render-preparation.service.ts → orchestration/render-preparation.service.ts} +127 -9
- package/src/route-renderer/page-loading/dependency-resolver.test.ts +345 -0
- package/src/route-renderer/{dependency-resolver.ts → page-loading/dependency-resolver.ts} +28 -12
- package/src/route-renderer/page-loading/page-module-loader.test.ts +96 -0
- package/src/route-renderer/{page-module-loader.ts → page-loading/page-module-loader.ts} +49 -21
- package/src/route-renderer/route-renderer.ts +36 -1
- package/src/router/README.md +26 -0
- package/src/router/client/link-intent.d.ts +53 -0
- package/src/router/client/link-intent.test.browser.ts +51 -0
- package/src/router/client/link-intent.ts +92 -0
- package/src/router/client/navigation-coordinator.test.ts +237 -0
- package/src/router/client/navigation-coordinator.ts +433 -0
- package/src/router/server/fs-router-scanner.test.ts +83 -0
- package/src/router/{fs-router-scanner.ts → server/fs-router-scanner.ts} +12 -10
- package/src/router/server/fs-router.test.ts +214 -0
- package/src/router/{fs-router.ts → server/fs-router.ts} +2 -2
- package/src/services/README.md +29 -0
- package/src/services/assets/asset-processing-service/asset-processing.service.test.ts +385 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/asset-processing.service.ts +101 -6
- package/src/services/assets/asset-processing-service/asset.factory.test.ts +63 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/asset.factory.ts +2 -2
- package/src/services/{asset-processing-service → assets/asset-processing-service}/assets.types.ts +2 -1
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.test.ts +72 -0
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.ts +95 -0
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.test.ts +67 -0
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.ts +78 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/index.ts +2 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/processor.interface.ts +1 -1
- package/src/services/assets/asset-processing-service/processors/base/base-processor.test.ts +59 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/base/base-processor.ts +11 -5
- package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/base/base-script-processor.ts +17 -27
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.test.ts +286 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/script/file-script.processor.ts +3 -3
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.test.ts +227 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/script/node-module-script.processor.ts +5 -4
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.test.ts +199 -0
- package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/stylesheet/file-stylesheet.processor.ts +4 -1
- package/src/services/assets/browser-bundle.service.test.ts +36 -0
- package/src/services/assets/browser-bundle.service.ts +53 -0
- package/src/services/cache/index.ts +3 -3
- package/src/services/cache/memory-cache-store.test.ts +225 -0
- package/src/services/cache/memory-cache-store.ts +1 -1
- package/src/services/cache/page-cache-service.test.ts +175 -0
- package/src/services/cache/page-cache-service.ts +3 -3
- package/src/services/cache/page-request-cache-coordinator.service.test.ts +79 -0
- package/src/services/{page-request-cache-coordinator.service.ts → cache/page-request-cache-coordinator.service.ts} +9 -6
- package/src/services/html/html-rewriter-provider.service.test.ts +183 -0
- package/src/services/html/html-rewriter-provider.service.ts +103 -0
- package/src/services/html/html-transformer.service.test.ts +378 -0
- package/src/services/html/html-transformer.service.ts +279 -0
- package/src/services/invalidation/development-invalidation.service.test.ts +77 -0
- package/src/services/invalidation/development-invalidation.service.ts +261 -0
- package/src/services/module-loading/app-server-module-transpiler.service.ts +52 -0
- package/src/services/module-loading/page-module-import.service.test.ts +253 -0
- package/src/services/module-loading/page-module-import.service.ts +200 -0
- package/src/services/module-loading/server-loader.service.test.ts +161 -0
- package/src/services/module-loading/server-loader.service.ts +130 -0
- package/src/services/module-loading/server-module-transpiler.service.test.ts +115 -0
- package/src/services/module-loading/server-module-transpiler.service.ts +105 -0
- package/src/services/runtime-manifest/node-runtime-manifest.service.test.ts +95 -0
- package/src/services/runtime-manifest/node-runtime-manifest.service.ts +101 -0
- package/src/services/runtime-state/dev-graph.service.ts +217 -0
- package/src/services/runtime-state/entrypoint-dependency-graph.service.ts +136 -0
- package/src/services/runtime-state/runtime-specifier-registry.service.ts +96 -0
- package/src/services/runtime-state/server-invalidation-state.service.ts +68 -0
- package/src/services/validation/schema-validation-service.test.ts +223 -0
- package/src/services/{schema-validation-service.ts → validation/schema-validation-service.ts} +1 -1
- package/src/static-site-generator/README.md +26 -0
- package/src/static-site-generator/static-site-generator.test.ts +307 -0
- package/src/static-site-generator/static-site-generator.ts +109 -6
- package/src/utils/deep-merge.test.ts +114 -0
- package/src/utils/invariant.test.ts +22 -0
- package/src/utils/path-utils.test.ts +15 -0
- package/src/utils/resolve-work-dir.ts +45 -0
- package/src/utils/server-utils.test.ts +38 -0
- package/src/watchers/project-watcher.integration.test.ts +337 -0
- package/src/watchers/project-watcher.test-helpers.ts +1 -1
- package/src/watchers/project-watcher.test.ts +678 -0
- package/src/watchers/project-watcher.ts +130 -111
- package/CHANGELOG.md +0 -91
- package/src/adapters/abstract/application-adapter.d.ts +0 -168
- package/src/adapters/abstract/application-adapter.js +0 -109
- package/src/adapters/abstract/router-adapter.d.ts +0 -26
- package/src/adapters/abstract/router-adapter.js +0 -5
- package/src/adapters/abstract/server-adapter.d.ts +0 -69
- package/src/adapters/abstract/server-adapter.js +0 -15
- package/src/adapters/bun/client-bridge.d.ts +0 -34
- package/src/adapters/bun/client-bridge.js +0 -48
- package/src/adapters/bun/create-app.d.ts +0 -60
- package/src/adapters/bun/create-app.js +0 -117
- package/src/adapters/bun/define-api-handler.d.ts +0 -61
- package/src/adapters/bun/define-api-handler.js +0 -15
- package/src/adapters/bun/define-api-handler.ts +0 -114
- package/src/adapters/bun/hmr-manager.d.ts +0 -85
- package/src/adapters/bun/hmr-manager.js +0 -240
- package/src/adapters/bun/index.d.ts +0 -3
- package/src/adapters/bun/index.js +0 -8
- package/src/adapters/bun/server-adapter.d.ts +0 -155
- package/src/adapters/bun/server-adapter.js +0 -368
- package/src/adapters/bun/server-lifecycle.d.ts +0 -52
- package/src/adapters/bun/server-lifecycle.js +0 -120
- package/src/adapters/index.d.ts +0 -6
- package/src/adapters/index.js +0 -14
- package/src/adapters/node/create-app.d.ts +0 -21
- package/src/adapters/node/create-app.js +0 -143
- package/src/adapters/node/index.d.ts +0 -4
- package/src/adapters/node/index.js +0 -8
- package/src/adapters/node/node-client-bridge.d.ts +0 -26
- package/src/adapters/node/node-client-bridge.js +0 -66
- package/src/adapters/node/node-hmr-manager.d.ts +0 -63
- package/src/adapters/node/node-hmr-manager.js +0 -237
- package/src/adapters/node/server-adapter.d.ts +0 -190
- package/src/adapters/node/server-adapter.js +0 -420
- package/src/adapters/node/static-content-server.d.ts +0 -24
- package/src/adapters/node/static-content-server.js +0 -166
- package/src/adapters/shared/api-response.d.ts +0 -52
- package/src/adapters/shared/api-response.js +0 -96
- package/src/adapters/shared/application-adapter.d.ts +0 -18
- package/src/adapters/shared/application-adapter.js +0 -90
- package/src/adapters/shared/explicit-static-route-matcher.d.ts +0 -38
- package/src/adapters/shared/explicit-static-route-matcher.js +0 -100
- package/src/adapters/shared/file-route-middleware-pipeline.d.ts +0 -65
- package/src/adapters/shared/file-route-middleware-pipeline.js +0 -98
- package/src/adapters/shared/fs-server-response-factory.d.ts +0 -19
- package/src/adapters/shared/fs-server-response-factory.js +0 -97
- package/src/adapters/shared/fs-server-response-matcher.d.ts +0 -71
- package/src/adapters/shared/fs-server-response-matcher.js +0 -155
- package/src/adapters/shared/render-context.d.ts +0 -14
- package/src/adapters/shared/render-context.js +0 -69
- package/src/adapters/shared/server-adapter.d.ts +0 -87
- package/src/adapters/shared/server-adapter.js +0 -353
- package/src/adapters/shared/server-route-handler.d.ts +0 -89
- package/src/adapters/shared/server-route-handler.js +0 -120
- package/src/adapters/shared/server-static-builder.d.ts +0 -38
- package/src/adapters/shared/server-static-builder.js +0 -46
- package/src/build/build-adapter.d.ts +0 -74
- package/src/build/build-adapter.js +0 -54
- package/src/build/build-types.d.ts +0 -57
- package/src/build/build-types.js +0 -0
- package/src/build/esbuild-build-adapter.d.ts +0 -69
- package/src/build/esbuild-build-adapter.js +0 -390
- package/src/config/config-builder.d.ts +0 -227
- package/src/config/config-builder.js +0 -392
- package/src/constants.d.ts +0 -32
- package/src/constants.js +0 -21
- package/src/create-app.d.ts +0 -17
- package/src/create-app.js +0 -66
- package/src/define-api-handler.d.ts +0 -25
- package/src/define-api-handler.js +0 -15
- package/src/dev/sc-server.d.ts +0 -30
- package/src/dev/sc-server.js +0 -111
- package/src/eco/component-render-context.d.ts +0 -105
- package/src/eco/component-render-context.js +0 -77
- package/src/eco/eco.d.ts +0 -9
- package/src/eco/eco.js +0 -110
- package/src/eco/eco.types.d.ts +0 -170
- package/src/eco/eco.types.js +0 -0
- package/src/eco/eco.utils.d.ts +0 -40
- package/src/eco/eco.utils.js +0 -40
- package/src/eco/global-injector-map.d.ts +0 -16
- package/src/eco/global-injector-map.js +0 -80
- package/src/eco/lazy-injector-map.d.ts +0 -8
- package/src/eco/lazy-injector-map.js +0 -70
- package/src/eco/module-dependencies.d.ts +0 -18
- package/src/eco/module-dependencies.js +0 -49
- package/src/errors/http-error.d.ts +0 -31
- package/src/errors/http-error.js +0 -50
- package/src/errors/index.d.ts +0 -2
- package/src/errors/index.js +0 -4
- package/src/errors/locals-access-error.d.ts +0 -4
- package/src/errors/locals-access-error.js +0 -9
- package/src/global/app-logger.d.ts +0 -2
- package/src/global/app-logger.js +0 -6
- package/src/hmr/client/hmr-runtime.d.ts +0 -10
- package/src/hmr/client/hmr-runtime.js +0 -86
- package/src/hmr/hmr-strategy.d.ts +0 -159
- package/src/hmr/hmr-strategy.js +0 -29
- package/src/hmr/hmr.test.e2e.d.ts +0 -1
- package/src/hmr/hmr.test.e2e.js +0 -50
- package/src/hmr/strategies/default-hmr-strategy.d.ts +0 -43
- package/src/hmr/strategies/default-hmr-strategy.js +0 -34
- package/src/hmr/strategies/js-hmr-strategy.d.ts +0 -136
- package/src/hmr/strategies/js-hmr-strategy.js +0 -188
- package/src/index.browser.d.ts +0 -3
- package/src/index.browser.js +0 -4
- package/src/index.d.ts +0 -5
- package/src/index.js +0 -10
- package/src/integrations/ghtml/ghtml-renderer.d.ts +0 -15
- package/src/integrations/ghtml/ghtml-renderer.js +0 -60
- package/src/integrations/ghtml/ghtml.plugin.d.ts +0 -20
- package/src/integrations/ghtml/ghtml.plugin.js +0 -21
- package/src/internal-types.d.ts +0 -200
- package/src/internal-types.js +0 -0
- package/src/plugins/alias-resolver-plugin.d.ts +0 -2
- package/src/plugins/alias-resolver-plugin.js +0 -39
- package/src/plugins/eco-component-meta-plugin.d.ts +0 -95
- package/src/plugins/eco-component-meta-plugin.js +0 -157
- package/src/plugins/integration-plugin.d.ts +0 -102
- package/src/plugins/integration-plugin.js +0 -100
- package/src/plugins/processor.d.ts +0 -82
- package/src/plugins/processor.js +0 -122
- package/src/public-types.d.ts +0 -1098
- package/src/public-types.js +0 -0
- package/src/route-renderer/component-graph-executor.d.ts +0 -32
- package/src/route-renderer/component-graph-executor.js +0 -31
- package/src/route-renderer/component-graph.d.ts +0 -42
- package/src/route-renderer/component-graph.js +0 -72
- package/src/route-renderer/component-marker.d.ts +0 -52
- package/src/route-renderer/component-marker.js +0 -46
- package/src/route-renderer/dependency-resolver.d.ts +0 -24
- package/src/route-renderer/dependency-resolver.js +0 -428
- package/src/route-renderer/html-post-processing.service.d.ts +0 -40
- package/src/route-renderer/html-post-processing.service.js +0 -86
- package/src/route-renderer/html-post-processing.service.ts +0 -103
- package/src/route-renderer/integration-renderer.d.ts +0 -339
- package/src/route-renderer/integration-renderer.js +0 -526
- package/src/route-renderer/marker-graph-resolver.d.ts +0 -76
- package/src/route-renderer/marker-graph-resolver.js +0 -93
- package/src/route-renderer/page-module-loader.d.ts +0 -61
- package/src/route-renderer/page-module-loader.js +0 -102
- package/src/route-renderer/render-execution.service.d.ts +0 -69
- package/src/route-renderer/render-execution.service.js +0 -91
- package/src/route-renderer/render-preparation.service.d.ts +0 -112
- package/src/route-renderer/render-preparation.service.js +0 -243
- package/src/route-renderer/route-renderer.d.ts +0 -26
- package/src/route-renderer/route-renderer.js +0 -68
- package/src/router/fs-router-scanner.d.ts +0 -41
- package/src/router/fs-router-scanner.js +0 -155
- package/src/router/fs-router.d.ts +0 -26
- package/src/router/fs-router.js +0 -100
- package/src/services/asset-processing-service/asset-processing.service.d.ts +0 -41
- package/src/services/asset-processing-service/asset-processing.service.js +0 -250
- package/src/services/asset-processing-service/asset.factory.d.ts +0 -17
- package/src/services/asset-processing-service/asset.factory.js +0 -82
- package/src/services/asset-processing-service/assets.types.d.ts +0 -88
- package/src/services/asset-processing-service/assets.types.js +0 -0
- package/src/services/asset-processing-service/index.d.ts +0 -3
- package/src/services/asset-processing-service/index.js +0 -3
- package/src/services/asset-processing-service/processor.interface.d.ts +0 -22
- package/src/services/asset-processing-service/processor.interface.js +0 -6
- package/src/services/asset-processing-service/processor.registry.d.ts +0 -8
- package/src/services/asset-processing-service/processor.registry.js +0 -15
- package/src/services/asset-processing-service/processors/base/base-processor.d.ts +0 -24
- package/src/services/asset-processing-service/processors/base/base-processor.js +0 -59
- package/src/services/asset-processing-service/processors/base/base-script-processor.d.ts +0 -16
- package/src/services/asset-processing-service/processors/base/base-script-processor.js +0 -80
- package/src/services/asset-processing-service/processors/index.d.ts +0 -5
- package/src/services/asset-processing-service/processors/index.js +0 -5
- package/src/services/asset-processing-service/processors/script/content-script.processor.d.ts +0 -5
- package/src/services/asset-processing-service/processors/script/content-script.processor.js +0 -57
- package/src/services/asset-processing-service/processors/script/file-script.processor.d.ts +0 -8
- package/src/services/asset-processing-service/processors/script/file-script.processor.js +0 -76
- package/src/services/asset-processing-service/processors/script/node-module-script.processor.d.ts +0 -7
- package/src/services/asset-processing-service/processors/script/node-module-script.processor.js +0 -74
- package/src/services/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -5
- package/src/services/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +0 -25
- package/src/services/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -9
- package/src/services/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +0 -63
- package/src/services/cache/cache.types.d.ts +0 -107
- package/src/services/cache/cache.types.js +0 -0
- package/src/services/cache/index.d.ts +0 -7
- package/src/services/cache/index.js +0 -7
- package/src/services/cache/memory-cache-store.d.ts +0 -42
- package/src/services/cache/memory-cache-store.js +0 -98
- package/src/services/cache/page-cache-service.d.ts +0 -70
- package/src/services/cache/page-cache-service.js +0 -152
- package/src/services/html-transformer.service.d.ts +0 -50
- package/src/services/html-transformer.service.js +0 -163
- package/src/services/html-transformer.service.ts +0 -217
- package/src/services/page-module-import.service.d.ts +0 -37
- package/src/services/page-module-import.service.js +0 -88
- package/src/services/page-module-import.service.ts +0 -129
- package/src/services/page-request-cache-coordinator.service.d.ts +0 -75
- package/src/services/page-request-cache-coordinator.service.js +0 -107
- package/src/services/schema-validation-service.d.ts +0 -122
- package/src/services/schema-validation-service.js +0 -101
- package/src/services/validation/standard-schema.types.d.ts +0 -65
- package/src/services/validation/standard-schema.types.js +0 -0
- package/src/static-site-generator/static-site-generator.d.ts +0 -57
- package/src/static-site-generator/static-site-generator.js +0 -272
- package/src/utils/css.d.ts +0 -1
- package/src/utils/css.js +0 -7
- package/src/utils/deep-merge.d.ts +0 -14
- package/src/utils/deep-merge.js +0 -32
- package/src/utils/hash.d.ts +0 -1
- package/src/utils/hash.js +0 -7
- package/src/utils/html.d.ts +0 -1
- package/src/utils/html.js +0 -4
- package/src/utils/invariant.d.ts +0 -5
- package/src/utils/invariant.js +0 -11
- package/src/utils/locals-utils.d.ts +0 -15
- package/src/utils/locals-utils.js +0 -24
- package/src/utils/parse-cli-args.d.ts +0 -24
- package/src/utils/parse-cli-args.js +0 -47
- package/src/utils/path-utils.module.d.ts +0 -5
- package/src/utils/path-utils.module.js +0 -14
- package/src/utils/runtime.d.ts +0 -11
- package/src/utils/runtime.js +0 -40
- package/src/utils/server-utils.module.d.ts +0 -19
- package/src/utils/server-utils.module.js +0 -56
- package/src/watchers/project-watcher.d.ts +0 -125
- package/src/watchers/project-watcher.js +0 -265
- package/src/watchers/project-watcher.test-helpers.d.ts +0 -4
- package/src/watchers/project-watcher.test-helpers.js +0 -52
- /package/src/route-renderer/{component-graph-executor.ts → component-graph/component-graph-executor.ts} +0 -0
- /package/src/route-renderer/{component-graph.ts → component-graph/component-graph.ts} +0 -0
- /package/src/route-renderer/{component-marker.ts → component-graph/component-marker.ts} +0 -0
- /package/src/services/{asset-processing-service → assets/asset-processing-service}/processor.registry.ts +0 -0
- /package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/index.ts +0 -0
- /package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/script/content-script.processor.ts +0 -0
- /package/src/services/{asset-processing-service → assets/asset-processing-service}/processors/stylesheet/content-stylesheet.processor.ts +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for MemoryCacheStore
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test, beforeEach } from 'vitest';
|
|
6
|
+
import { MemoryCacheStore } from './memory-cache-store.js';
|
|
7
|
+
import type { CacheEntry } from './cache.types.js';
|
|
8
|
+
|
|
9
|
+
function createEntry(overrides: Partial<CacheEntry> = {}): CacheEntry {
|
|
10
|
+
return {
|
|
11
|
+
html: '<html>test</html>',
|
|
12
|
+
createdAt: Date.now(),
|
|
13
|
+
revalidateAfter: null,
|
|
14
|
+
tags: [],
|
|
15
|
+
strategy: 'static',
|
|
16
|
+
...overrides,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('MemoryCacheStore', () => {
|
|
21
|
+
let store: MemoryCacheStore;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
store = new MemoryCacheStore();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
describe('get/set', () => {
|
|
28
|
+
test('should return null for cache miss', async () => {
|
|
29
|
+
const result = await store.get('/nonexistent');
|
|
30
|
+
expect(result).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('should store and retrieve an entry', async () => {
|
|
34
|
+
const entry = createEntry({ html: '<html>hello</html>' });
|
|
35
|
+
await store.set('/page', entry);
|
|
36
|
+
const result = await store.get('/page');
|
|
37
|
+
expect(result?.html).toBe('<html>hello</html>');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('should update existing entry', async () => {
|
|
41
|
+
await store.set('/page', createEntry({ html: '<html>v1</html>' }));
|
|
42
|
+
await store.set('/page', createEntry({ html: '<html>v2</html>' }));
|
|
43
|
+
const result = await store.get('/page');
|
|
44
|
+
expect(result?.html).toBe('<html>v2</html>');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('LRU eviction', () => {
|
|
49
|
+
test('should evict oldest entry when maxEntries reached', async () => {
|
|
50
|
+
const smallStore = new MemoryCacheStore({ maxEntries: 3 });
|
|
51
|
+
|
|
52
|
+
await smallStore.set('/page1', createEntry());
|
|
53
|
+
await smallStore.set('/page2', createEntry());
|
|
54
|
+
await smallStore.set('/page3', createEntry());
|
|
55
|
+
|
|
56
|
+
await smallStore.set('/page4', createEntry());
|
|
57
|
+
|
|
58
|
+
expect(await smallStore.get('/page1')).toBeNull();
|
|
59
|
+
expect(await smallStore.get('/page2')).not.toBeNull();
|
|
60
|
+
expect(await smallStore.get('/page3')).not.toBeNull();
|
|
61
|
+
expect(await smallStore.get('/page4')).not.toBeNull();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('should refresh entry position on access', async () => {
|
|
65
|
+
const smallStore = new MemoryCacheStore({ maxEntries: 3 });
|
|
66
|
+
|
|
67
|
+
await smallStore.set('/page1', createEntry());
|
|
68
|
+
await smallStore.set('/page2', createEntry());
|
|
69
|
+
await smallStore.set('/page3', createEntry());
|
|
70
|
+
|
|
71
|
+
await smallStore.get('/page1');
|
|
72
|
+
|
|
73
|
+
await smallStore.set('/page4', createEntry());
|
|
74
|
+
|
|
75
|
+
expect(await smallStore.get('/page1')).not.toBeNull();
|
|
76
|
+
expect(await smallStore.get('/page2')).toBeNull();
|
|
77
|
+
expect(await smallStore.get('/page3')).not.toBeNull();
|
|
78
|
+
expect(await smallStore.get('/page4')).not.toBeNull();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('should not evict when updating existing key', async () => {
|
|
82
|
+
const smallStore = new MemoryCacheStore({ maxEntries: 3 });
|
|
83
|
+
|
|
84
|
+
await smallStore.set('/page1', createEntry());
|
|
85
|
+
await smallStore.set('/page2', createEntry());
|
|
86
|
+
await smallStore.set('/page3', createEntry());
|
|
87
|
+
|
|
88
|
+
await smallStore.set('/page1', createEntry({ html: '<html>updated</html>' }));
|
|
89
|
+
|
|
90
|
+
const stats = await smallStore.stats();
|
|
91
|
+
expect(stats.entries).toBe(3);
|
|
92
|
+
expect((await smallStore.get('/page1'))?.html).toBe('<html>updated</html>');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('should refresh entry position on update (move to most recent)', async () => {
|
|
96
|
+
const smallStore = new MemoryCacheStore({ maxEntries: 3 });
|
|
97
|
+
|
|
98
|
+
await smallStore.set('/page1', createEntry({ html: '<html>v1</html>' }));
|
|
99
|
+
await smallStore.set('/page2', createEntry());
|
|
100
|
+
await smallStore.set('/page3', createEntry());
|
|
101
|
+
|
|
102
|
+
await smallStore.set('/page1', createEntry({ html: '<html>v2</html>' }));
|
|
103
|
+
|
|
104
|
+
await smallStore.set('/page4', createEntry());
|
|
105
|
+
|
|
106
|
+
expect(await smallStore.get('/page1')).not.toBeNull();
|
|
107
|
+
expect(await smallStore.get('/page2')).toBeNull();
|
|
108
|
+
expect(await smallStore.get('/page3')).not.toBeNull();
|
|
109
|
+
expect(await smallStore.get('/page4')).not.toBeNull();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('delete', () => {
|
|
114
|
+
test('should delete an entry', async () => {
|
|
115
|
+
await store.set('/page', createEntry());
|
|
116
|
+
const deleted = await store.delete('/page');
|
|
117
|
+
expect(deleted).toBe(true);
|
|
118
|
+
expect(await store.get('/page')).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('should return false for non-existent entry', async () => {
|
|
122
|
+
const deleted = await store.delete('/nonexistent');
|
|
123
|
+
expect(deleted).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('invalidateByTags', () => {
|
|
128
|
+
test('should invalidate all entries with matching tag', async () => {
|
|
129
|
+
await store.set('/blog/1', createEntry({ tags: ['blog'] }));
|
|
130
|
+
await store.set('/blog/2', createEntry({ tags: ['blog'] }));
|
|
131
|
+
await store.set('/about', createEntry({ tags: ['static'] }));
|
|
132
|
+
|
|
133
|
+
const count = await store.invalidateByTags(['blog']);
|
|
134
|
+
expect(count).toBe(2);
|
|
135
|
+
expect(await store.get('/blog/1')).toBeNull();
|
|
136
|
+
expect(await store.get('/blog/2')).toBeNull();
|
|
137
|
+
expect(await store.get('/about')).not.toBeNull();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('should handle multiple tags', async () => {
|
|
141
|
+
await store.set('/blog/1', createEntry({ tags: ['blog', 'featured'] }));
|
|
142
|
+
await store.set('/product/1', createEntry({ tags: ['product'] }));
|
|
143
|
+
|
|
144
|
+
const count = await store.invalidateByTags(['blog', 'product']);
|
|
145
|
+
expect(count).toBe(2);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('should return 0 for non-matching tags', async () => {
|
|
149
|
+
await store.set('/page', createEntry({ tags: ['blog'] }));
|
|
150
|
+
const count = await store.invalidateByTags(['nonexistent']);
|
|
151
|
+
expect(count).toBe(0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('should clean up all tag references when entry has multiple tags', async () => {
|
|
155
|
+
await store.set('/blog/featured', createEntry({ tags: ['blog', 'featured', 'homepage'] }));
|
|
156
|
+
await store.set('/blog/regular', createEntry({ tags: ['blog'] }));
|
|
157
|
+
await store.set('/featured-product', createEntry({ tags: ['featured', 'product'] }));
|
|
158
|
+
|
|
159
|
+
const count = await store.invalidateByTags(['blog']);
|
|
160
|
+
expect(count).toBe(2);
|
|
161
|
+
|
|
162
|
+
expect(await store.get('/blog/featured')).toBeNull();
|
|
163
|
+
expect(await store.get('/blog/regular')).toBeNull();
|
|
164
|
+
expect(await store.get('/featured-product')).not.toBeNull();
|
|
165
|
+
|
|
166
|
+
const countFeatured = await store.invalidateByTags(['featured']);
|
|
167
|
+
expect(countFeatured).toBe(1);
|
|
168
|
+
expect(await store.get('/featured-product')).toBeNull();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('should not leave orphaned tag references after invalidation', async () => {
|
|
172
|
+
await store.set('/page1', createEntry({ tags: ['tagA', 'tagB'] }));
|
|
173
|
+
|
|
174
|
+
await store.invalidateByTags(['tagA']);
|
|
175
|
+
|
|
176
|
+
expect(await store.get('/page1')).toBeNull();
|
|
177
|
+
|
|
178
|
+
await store.set('/page2', createEntry({ tags: ['tagB'] }));
|
|
179
|
+
const count = await store.invalidateByTags(['tagB']);
|
|
180
|
+
expect(count).toBe(1);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
describe('invalidateByPaths', () => {
|
|
185
|
+
test('should invalidate specific paths', async () => {
|
|
186
|
+
await store.set('/blog/1', createEntry());
|
|
187
|
+
await store.set('/blog/2', createEntry());
|
|
188
|
+
|
|
189
|
+
const count = await store.invalidateByPaths(['/blog/1']);
|
|
190
|
+
expect(count).toBe(1);
|
|
191
|
+
expect(await store.get('/blog/1')).toBeNull();
|
|
192
|
+
expect(await store.get('/blog/2')).not.toBeNull();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('should handle multiple paths', async () => {
|
|
196
|
+
await store.set('/blog/1', createEntry());
|
|
197
|
+
await store.set('/blog/2', createEntry());
|
|
198
|
+
await store.set('/about', createEntry());
|
|
199
|
+
|
|
200
|
+
const count = await store.invalidateByPaths(['/blog/1', '/about']);
|
|
201
|
+
expect(count).toBe(2);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('clear', () => {
|
|
206
|
+
test('should remove all entries', async () => {
|
|
207
|
+
await store.set('/page1', createEntry());
|
|
208
|
+
await store.set('/page2', createEntry());
|
|
209
|
+
await store.clear();
|
|
210
|
+
|
|
211
|
+
const stats = await store.stats();
|
|
212
|
+
expect(stats.entries).toBe(0);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe('stats', () => {
|
|
217
|
+
test('should return entry count', async () => {
|
|
218
|
+
await store.set('/page1', createEntry());
|
|
219
|
+
await store.set('/page2', createEntry());
|
|
220
|
+
|
|
221
|
+
const stats = await store.stats();
|
|
222
|
+
expect(stats.entries).toBe(2);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { CacheEntry, CacheStats, CacheStore } from './cache.types.
|
|
7
|
+
import type { CacheEntry, CacheStats, CacheStore } from './cache.types.js';
|
|
8
8
|
|
|
9
9
|
export interface MemoryCacheStoreOptions {
|
|
10
10
|
/** Maximum number of entries before LRU eviction. @default 1000 */
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for PageCacheService
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test, beforeEach, vi } from 'vitest';
|
|
6
|
+
import { PageCacheService, getCacheControlHeader } from './page-cache-service.js';
|
|
7
|
+
import { MemoryCacheStore } from './memory-cache-store.js';
|
|
8
|
+
|
|
9
|
+
describe('PageCacheService', () => {
|
|
10
|
+
let service: PageCacheService;
|
|
11
|
+
let store: MemoryCacheStore;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
store = new MemoryCacheStore();
|
|
15
|
+
service = new PageCacheService({ store });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('generateCacheKey', () => {
|
|
19
|
+
test('should use full URL as key', () => {
|
|
20
|
+
expect(service.generateCacheKey('/blog/post-1')).toBe('/blog/post-1');
|
|
21
|
+
expect(service.generateCacheKey('/search?q=test')).toBe('/search?q=test');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('getOrCreate', () => {
|
|
26
|
+
test('should return miss and render on cache miss', async () => {
|
|
27
|
+
const renderFn = vi.fn().mockResolvedValue({ html: '<html>hello</html>', strategy: 'static' as const });
|
|
28
|
+
|
|
29
|
+
const result = await service.getOrCreate('/page', 'static', renderFn);
|
|
30
|
+
|
|
31
|
+
expect(result.status).toBe('miss');
|
|
32
|
+
expect(result.html).toBe('<html>hello</html>');
|
|
33
|
+
expect(result.strategy).toBe('static');
|
|
34
|
+
expect(renderFn).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('should return hit on cache hit', async () => {
|
|
38
|
+
const renderFn = vi.fn().mockResolvedValue({ html: '<html>hello</html>', strategy: 'static' as const });
|
|
39
|
+
|
|
40
|
+
await service.getOrCreate('/page', 'static', renderFn);
|
|
41
|
+
|
|
42
|
+
renderFn.mockClear();
|
|
43
|
+
const result = await service.getOrCreate('/page', 'static', renderFn);
|
|
44
|
+
|
|
45
|
+
expect(result.status).toBe('hit');
|
|
46
|
+
expect(result.html).toBe('<html>hello</html>');
|
|
47
|
+
expect(result.strategy).toBe('static');
|
|
48
|
+
expect(renderFn).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('should bypass cache for dynamic strategy', async () => {
|
|
52
|
+
const renderFn = vi.fn().mockResolvedValue({ html: '<html>hello</html>', strategy: 'dynamic' as const });
|
|
53
|
+
|
|
54
|
+
const result1 = await service.getOrCreate('/page', 'dynamic', renderFn);
|
|
55
|
+
const result2 = await service.getOrCreate('/page', 'dynamic', renderFn);
|
|
56
|
+
|
|
57
|
+
expect(result1.status).toBe('miss');
|
|
58
|
+
expect(result2.status).toBe('miss');
|
|
59
|
+
expect(renderFn).toHaveBeenCalledTimes(2);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('should return stale when entry is past revalidation time', async () => {
|
|
63
|
+
const revalidateStrategy = { revalidate: 60 };
|
|
64
|
+
const renderFn = vi.fn().mockResolvedValue({ html: '<html>v1</html>', strategy: revalidateStrategy });
|
|
65
|
+
|
|
66
|
+
const staleEntry = {
|
|
67
|
+
html: '<html>stale</html>',
|
|
68
|
+
createdAt: Date.now() - 120000,
|
|
69
|
+
revalidateAfter: Date.now() - 60000,
|
|
70
|
+
tags: [],
|
|
71
|
+
strategy: revalidateStrategy,
|
|
72
|
+
};
|
|
73
|
+
await store.set('/stale-page', staleEntry);
|
|
74
|
+
|
|
75
|
+
const result = await service.getOrCreate('/stale-page', { revalidate: 60 }, renderFn);
|
|
76
|
+
expect(result.status).toBe('stale');
|
|
77
|
+
expect(result.html).toBe('<html>stale</html>');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('should deduplicate concurrent regeneration requests', async () => {
|
|
81
|
+
let callCount = 0;
|
|
82
|
+
const renderFn = vi.fn().mockImplementation(async () => {
|
|
83
|
+
callCount++;
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
85
|
+
return { html: `<html>v${callCount}</html>`, strategy: { revalidate: 60 } };
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const staleEntry = {
|
|
89
|
+
html: '<html>stale</html>',
|
|
90
|
+
createdAt: Date.now() - 120000,
|
|
91
|
+
revalidateAfter: Date.now() - 60000,
|
|
92
|
+
tags: [],
|
|
93
|
+
strategy: { revalidate: 60 },
|
|
94
|
+
};
|
|
95
|
+
await store.set('/dedup-page', staleEntry);
|
|
96
|
+
|
|
97
|
+
const results = await Promise.all([
|
|
98
|
+
service.getOrCreate('/dedup-page', { revalidate: 60 }, renderFn),
|
|
99
|
+
service.getOrCreate('/dedup-page', { revalidate: 60 }, renderFn),
|
|
100
|
+
service.getOrCreate('/dedup-page', { revalidate: 60 }, renderFn),
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
for (const result of results) {
|
|
104
|
+
expect(result.status).toBe('stale');
|
|
105
|
+
expect(result.html).toBe('<html>stale</html>');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
109
|
+
|
|
110
|
+
expect(renderFn).toHaveBeenCalledTimes(1);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
describe('invalidation', () => {
|
|
115
|
+
test('should invalidate by tags', async () => {
|
|
116
|
+
const strategy = { revalidate: 3600, tags: ['blog'] };
|
|
117
|
+
const renderFn = vi.fn().mockResolvedValue({ html: '<html>hello</html>', strategy });
|
|
118
|
+
|
|
119
|
+
await service.getOrCreate('/blog/1', strategy, renderFn);
|
|
120
|
+
await service.getOrCreate('/blog/2', strategy, renderFn);
|
|
121
|
+
|
|
122
|
+
const count = await service.invalidateByTags(['blog']);
|
|
123
|
+
expect(count).toBe(2);
|
|
124
|
+
|
|
125
|
+
renderFn.mockClear();
|
|
126
|
+
const result = await service.getOrCreate('/blog/1', 'static', renderFn);
|
|
127
|
+
expect(result.status).toBe('miss');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('should invalidate by paths', async () => {
|
|
131
|
+
const renderFn = vi.fn().mockResolvedValue({ html: '<html>hello</html>', strategy: 'static' as const });
|
|
132
|
+
|
|
133
|
+
await service.getOrCreate('/blog/1', 'static', renderFn);
|
|
134
|
+
|
|
135
|
+
const count = await service.invalidateByPaths(['/blog/1']);
|
|
136
|
+
expect(count).toBe(1);
|
|
137
|
+
|
|
138
|
+
renderFn.mockClear();
|
|
139
|
+
const result = await service.getOrCreate('/blog/1', 'static', renderFn);
|
|
140
|
+
expect(result.status).toBe('miss');
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('disabled cache', () => {
|
|
145
|
+
test('should bypass cache when disabled', async () => {
|
|
146
|
+
const disabledService = new PageCacheService({ store, enabled: false });
|
|
147
|
+
const renderFn = vi.fn().mockResolvedValue({ html: '<html>hello</html>', strategy: 'static' as const });
|
|
148
|
+
|
|
149
|
+
const result1 = await disabledService.getOrCreate('/page', 'static', renderFn);
|
|
150
|
+
const result2 = await disabledService.getOrCreate('/page', 'static', renderFn);
|
|
151
|
+
|
|
152
|
+
expect(result1.status).toBe('miss');
|
|
153
|
+
expect(result2.status).toBe('miss');
|
|
154
|
+
expect(renderFn).toHaveBeenCalledTimes(2);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('getCacheControlHeader', () => {
|
|
160
|
+
test('should return immutable for static strategy', () => {
|
|
161
|
+
expect(getCacheControlHeader('static')).toBe('public, max-age=31536000, immutable');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test('should return no-store for dynamic strategy', () => {
|
|
165
|
+
expect(getCacheControlHeader('dynamic')).toBe('no-store, must-revalidate');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('should return max-age and stale-while-revalidate for object strategy', () => {
|
|
169
|
+
expect(getCacheControlHeader({ revalidate: 3600 })).toBe('public, max-age=3600, stale-while-revalidate=7200');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('should return no-store when disabled', () => {
|
|
173
|
+
expect(getCacheControlHeader('disabled')).toBe('no-store, must-revalidate');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { appLogger } from '../../global/app-logger.
|
|
8
|
-
import type { CacheEntry, CacheResult, CacheStore, CacheStrategy, RenderResult } from './cache.types.
|
|
9
|
-
import { MemoryCacheStore } from './memory-cache-store.
|
|
7
|
+
import { appLogger } from '../../global/app-logger.js';
|
|
8
|
+
import type { CacheEntry, CacheResult, CacheStore, CacheStrategy, RenderResult } from './cache.types.js';
|
|
9
|
+
import { MemoryCacheStore } from './memory-cache-store.js';
|
|
10
10
|
|
|
11
11
|
export interface PageCacheServiceOptions {
|
|
12
12
|
store?: CacheStore;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { PageRequestCacheCoordinator } from './page-request-cache-coordinator.service.ts';
|
|
3
|
+
import type { PageCacheService } from './page-cache-service.js';
|
|
4
|
+
|
|
5
|
+
describe('PageRequestCacheCoordinator', () => {
|
|
6
|
+
it('should build cache keys with query parameters', () => {
|
|
7
|
+
const service = new PageRequestCacheCoordinator(null, 'static');
|
|
8
|
+
|
|
9
|
+
expect(service.buildCacheKey({ pathname: '/blog' })).toBe('/blog');
|
|
10
|
+
expect(service.buildCacheKey({ pathname: '/blog', query: { page: '2', tag: 'eco' } })).toBe(
|
|
11
|
+
'/blog?page=2&tag=eco',
|
|
12
|
+
);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should bypass the cache service for dynamic pages', async () => {
|
|
16
|
+
const cacheService = {
|
|
17
|
+
getOrCreate: vi.fn(),
|
|
18
|
+
} as unknown as PageCacheService;
|
|
19
|
+
const renderFn = vi.fn(async () => ({
|
|
20
|
+
html: '<html><body>dynamic</body></html>',
|
|
21
|
+
strategy: 'dynamic' as const,
|
|
22
|
+
}));
|
|
23
|
+
const service = new PageRequestCacheCoordinator(cacheService, 'static');
|
|
24
|
+
|
|
25
|
+
const response = await service.render({
|
|
26
|
+
cacheKey: '/dynamic',
|
|
27
|
+
pageCacheStrategy: 'dynamic',
|
|
28
|
+
renderFn,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(await response.text()).toBe('<html><body>dynamic</body></html>');
|
|
32
|
+
expect(response.headers.get('X-Cache')).toBe('DISABLED');
|
|
33
|
+
expect(cacheService.getOrCreate).not.toHaveBeenCalled();
|
|
34
|
+
expect(renderFn).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should delegate cached rendering to the cache service', async () => {
|
|
38
|
+
const cacheService = {
|
|
39
|
+
getOrCreate: vi.fn(async () => ({
|
|
40
|
+
html: '<html><body>cached</body></html>',
|
|
41
|
+
strategy: { revalidate: 60 },
|
|
42
|
+
status: 'hit',
|
|
43
|
+
})),
|
|
44
|
+
} as unknown as PageCacheService;
|
|
45
|
+
const service = new PageRequestCacheCoordinator(cacheService, 'static');
|
|
46
|
+
|
|
47
|
+
const response = await service.render({
|
|
48
|
+
cacheKey: '/cached',
|
|
49
|
+
pageCacheStrategy: { revalidate: 60 },
|
|
50
|
+
renderFn: async () => ({
|
|
51
|
+
html: '<html><body>fresh</body></html>',
|
|
52
|
+
strategy: { revalidate: 60 },
|
|
53
|
+
}),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
expect(await response.text()).toBe('<html><body>cached</body></html>');
|
|
57
|
+
expect(response.headers.get('X-Cache')).toBe('HIT');
|
|
58
|
+
expect(response.headers.get('Cache-Control')).toContain('max-age=60');
|
|
59
|
+
expect(cacheService.getOrCreate).toHaveBeenCalledWith('/cached', { revalidate: 60 }, expect.any(Function));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should normalize supported body types to strings', async () => {
|
|
63
|
+
const service = new PageRequestCacheCoordinator(null, 'static');
|
|
64
|
+
|
|
65
|
+
expect(await service.bodyToString('plain')).toBe('plain');
|
|
66
|
+
expect(await service.bodyToString(Buffer.from('buffered'))).toBe('buffered');
|
|
67
|
+
expect(await service.bodyToString(new Uint8Array([104, 101, 108, 108, 111]))).toBe('hello');
|
|
68
|
+
expect(
|
|
69
|
+
await service.bodyToString(
|
|
70
|
+
new ReadableStream({
|
|
71
|
+
start: (controller) => {
|
|
72
|
+
controller.enqueue(new TextEncoder().encode('streamed'));
|
|
73
|
+
controller.close();
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
),
|
|
77
|
+
).toBe('streamed');
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { getCacheControlHeader, type PageCacheService } from './
|
|
2
|
-
import type { CacheStrategy, RenderResult } from './cache
|
|
1
|
+
import { getCacheControlHeader, type PageCacheService } from './page-cache-service.js';
|
|
2
|
+
import type { CacheStrategy, RenderResult } from './cache.types.js';
|
|
3
3
|
|
|
4
4
|
type CacheStatus = 'hit' | 'miss' | 'stale' | 'expired' | 'disabled';
|
|
5
5
|
|
|
@@ -11,10 +11,13 @@ type CacheStatus = 'hit' | 'miss' | 'stale' | 'expired' | 'disabled';
|
|
|
11
11
|
* body normalization for cache storage, and final cache header generation.
|
|
12
12
|
*/
|
|
13
13
|
export class PageRequestCacheCoordinator {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
) {
|
|
14
|
+
private cacheService: PageCacheService | null;
|
|
15
|
+
private defaultCacheStrategy: CacheStrategy;
|
|
16
|
+
|
|
17
|
+
constructor(cacheService: PageCacheService | null, defaultCacheStrategy: CacheStrategy) {
|
|
18
|
+
this.cacheService = cacheService;
|
|
19
|
+
this.defaultCacheStrategy = defaultCacheStrategy;
|
|
20
|
+
}
|
|
18
21
|
|
|
19
22
|
/**
|
|
20
23
|
* Builds the cache key used for page lookups.
|