@ecopages/core 0.2.0-beta.30 → 0.2.0-beta.32
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 +1 -0
- package/package.json +82 -3
- package/src/adapters/abstract/application-adapter.d.ts +0 -8
- package/src/adapters/abstract/application-adapter.js +252 -261
- package/src/adapters/abstract/router-adapter.js +12 -4
- package/src/adapters/abstract/segment-path-matcher.js +132 -134
- package/src/adapters/abstract/server-adapter.js +22 -14
- package/src/adapters/abstract/ws-pattern-matcher.js +24 -26
- package/src/adapters/bun/bun-user-websocket-lifecycle.js +83 -86
- package/src/adapters/bun/client-bridge.js +51 -50
- package/src/adapters/bun/create-app.js +171 -161
- package/src/adapters/bun/hmr-manager.js +62 -57
- package/src/adapters/bun/index.js +2 -31
- package/src/adapters/bun/runtime-host.js +61 -54
- package/src/adapters/bun/server-adapter.d.ts +0 -1
- package/src/adapters/bun/server-adapter.js +561 -561
- package/src/adapters/bun/static-preview-host.js +43 -46
- package/src/adapters/create-app.js +11 -14
- package/src/adapters/index.js +2 -30
- package/src/adapters/node/create-app.js +123 -129
- package/src/adapters/node/http-request-bridge.js +147 -106
- package/src/adapters/node/node-client-bridge.js +62 -64
- package/src/adapters/node/node-hmr-manager.js +58 -48
- package/src/adapters/node/runtime-host.js +96 -89
- package/src/adapters/node/server-adapter-dependencies.js +18 -21
- package/src/adapters/node/server-adapter.d.ts +0 -1
- package/src/adapters/node/server-adapter.js +347 -326
- package/src/adapters/node/static-content-server.js +172 -191
- package/src/adapters/node/static-preview-host.js +87 -78
- package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.d.ts +16 -0
- package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.js +32 -0
- package/src/adapters/shared/hmr/hmr-html-response.js +46 -32
- package/src/adapters/shared/hmr/shared-hmr-manager.d.ts +29 -38
- package/src/adapters/shared/hmr/shared-hmr-manager.js +439 -425
- package/src/adapters/shared/http/api-request-pipeline.js +122 -119
- package/src/adapters/shared/http/api-response.js +77 -77
- package/src/adapters/shared/http/create-body-response.js +22 -19
- package/src/adapters/shared/http/define-api-handler.js +41 -55
- package/src/adapters/shared/http/explicit-static-render-preparation.js +26 -23
- package/src/adapters/shared/http/explicit-static-route-matcher.js +55 -62
- package/src/adapters/shared/http/file-route-middleware-pipeline.js +81 -75
- package/src/adapters/shared/http/fs-server-response-factory.js +101 -96
- package/src/adapters/shared/http/fs-server-response-matcher.js +239 -257
- package/src/adapters/shared/http/run-middleware-chain.js +15 -15
- package/src/adapters/shared/runtime/application-adapter.js +72 -87
- package/src/adapters/shared/runtime/copy-runtime-public-dir.js +25 -22
- package/src/adapters/shared/runtime/port-manager.js +207 -177
- package/src/adapters/shared/runtime/render-context.js +94 -79
- package/src/adapters/shared/runtime/runtime-app-bootstrap.js +33 -38
- package/src/adapters/shared/runtime/runtime-host.js +1 -0
- package/src/adapters/shared/runtime/runtime-server-lifecycle.d.ts +2 -3
- package/src/adapters/shared/runtime/runtime-server-lifecycle.js +65 -51
- package/src/adapters/shared/runtime/server-adapter.d.ts +1 -0
- package/src/adapters/shared/runtime/server-adapter.js +233 -217
- package/src/adapters/shared/runtime/server-route-handler.js +74 -70
- package/src/adapters/shared/runtime/server-static-builder.js +165 -184
- package/src/adapters/shared/runtime/static-preview-host.js +1 -0
- package/src/adapters/shared/ws/node-http-websocket-upgrades.js +97 -88
- package/src/adapters/shared/ws/websocket-lifecycle.js +57 -50
- package/src/build/README.md +52 -1
- package/src/build/app-build-manifest-runtime.js +129 -108
- package/src/build/browser/browser-runtime-manifest.js +47 -50
- package/src/build/browser/browser-runtime-plugin-helpers.js +32 -12
- package/src/build/browser/browser-runtime-plugin.js +206 -160
- package/src/build/browser/jsx-ownership-plugins.js +51 -37
- package/src/build/browser/lit-static-render-worker-context.js +3 -5
- package/src/build/build-adapter.js +276 -139
- package/src/build/cache/build-input-fingerprint.js +28 -26
- package/src/build/cache/cache-constants.js +4 -6
- package/src/build/cache/cache-keys.js +64 -56
- package/src/build/cache/pages-unified-graph-build.js +179 -178
- package/src/build/cache/production-build-cache.js +47 -54
- package/src/build/cache/server-entry-build-cache.js +134 -146
- package/src/build/contracts/build-contracts.d.ts +4 -4
- package/src/build/contracts/build-contracts.js +1 -0
- package/src/build/contracts/build-manifest.js +58 -43
- package/src/build/contracts/build-types.js +1 -0
- package/src/build/rolldown/rolldown-adapter-helpers.d.ts +9 -0
- package/src/build/rolldown/rolldown-adapter-helpers.js +405 -298
- package/src/build/rolldown/rolldown-build-adapter.js +53 -61
- package/src/build/rolldown/rolldown-build-invocation-metrics.js +31 -35
- package/src/build/rolldown/rolldown-plugin-bridge.js +264 -194
- package/src/build/rolldown/rolldown-source-transform-pass.js +59 -50
- package/src/build/rolldown/runtime-build-output-normalizer.js +227 -175
- package/src/build/rolldown/server-side-css-shim-plugin.js +54 -31
- package/src/build/runtime/build-profile-options.js +36 -36
- package/src/build/runtime/build-request-identity.js +70 -45
- package/src/build/runtime/build-request-policy.js +80 -42
- package/src/build/runtime/build-runtime.js +64 -54
- package/src/build/runtime/deduping-build-executor.js +38 -32
- package/src/build/runtime/parallel-build-executor.js +58 -50
- package/src/build/runtime/serialized-build-executor.js +78 -62
- package/src/cache/index.js +6 -6
- package/src/cache/module-parse-cache.js +99 -68
- package/src/client/navigation-scripts/classification.js +45 -37
- package/src/client/navigation-scripts/index.js +3 -23
- package/src/client/navigation-scripts/navigation-scripts.test.browser.js +58 -62
- package/src/client/navigation-scripts/registry.js +15 -11
- package/src/client/navigation-scripts/rerun-queue.js +81 -64
- package/src/client/scroll.js +32 -24
- package/src/client/scroll.test.browser.js +27 -33
- package/src/client/view-transitions.js +68 -55
- package/src/config/config-builder.d.ts +8 -0
- package/src/config/config-builder.js +637 -642
- package/src/config/constants.js +44 -24
- package/src/declarations.d.ts +1 -0
- package/src/dev/README.md +24 -0
- package/src/dev/client-bridge-registry.js +7 -12
- package/src/dev/dev-client-ownership.js +7 -5
- package/src/dev/hmr-manager-registry.js +7 -12
- package/src/dev/host-runtime.js +25 -30
- package/src/dev/runtime-server-started-message.js +2 -5
- package/src/dev/sc-server.js +106 -103
- package/src/dev/transform-server/dev-transform-bundler.d.ts +24 -0
- package/src/dev/transform-server/dev-transform-bundler.js +79 -0
- package/src/dev/transform-server/dev-transform-externalize-plugin.d.ts +12 -0
- package/src/dev/transform-server/dev-transform-externalize-plugin.js +29 -0
- package/src/dev/transform-server/dev-transform-import-rewriter.d.ts +16 -0
- package/src/dev/transform-server/dev-transform-import-rewriter.js +110 -0
- package/src/dev/transform-server/dev-transform-module-kind.d.ts +13 -0
- package/src/dev/transform-server/dev-transform-module-kind.js +17 -0
- package/src/dev/transform-server/dev-transform-runtime-specifiers.d.ts +5 -0
- package/src/dev/transform-server/dev-transform-runtime-specifiers.js +16 -0
- package/src/dev/transform-server/dev-transform-server.d.ts +41 -0
- package/src/dev/transform-server/dev-transform-server.js +191 -0
- package/src/dev/transform-server/dev-transform-url.d.ts +13 -0
- package/src/dev/transform-server/dev-transform-url.js +58 -0
- package/src/dev/transform-server/dev-transform-vendor-registry.d.ts +27 -0
- package/src/dev/transform-server/dev-transform-vendor-registry.js +145 -0
- package/src/dev/transform-server/index.d.ts +6 -0
- package/src/dev/transform-server/index.js +4 -0
- package/src/dev/transform-server/types.d.ts +15 -0
- package/src/dev/transform-server/types.js +1 -0
- package/src/dev-toolbar/README.md +51 -0
- package/src/dev-toolbar/define-dev-tool.d.ts +21 -0
- package/src/dev-toolbar/define-dev-tool.js +9 -0
- package/src/dev-toolbar/dev-toolbar-config.d.ts +25 -0
- package/src/dev-toolbar/dev-toolbar-config.js +19 -0
- package/src/dev-toolbar/dev-toolbar-host.d.ts +49 -0
- package/src/dev-toolbar/dev-toolbar-host.js +117 -0
- package/src/dev-toolbar/dev-toolbar-html-response.d.ts +11 -0
- package/src/dev-toolbar/dev-toolbar-html-response.js +14 -0
- package/src/dev-toolbar/dev-toolbar-manifest.d.ts +15 -0
- package/src/dev-toolbar/dev-toolbar-manifest.js +52 -0
- package/src/dev-toolbar/dev-toolbar-package.d.ts +31 -0
- package/src/dev-toolbar/dev-toolbar-package.js +72 -0
- package/src/dev-toolbar/dev-toolbar-runtime-paths.d.ts +10 -0
- package/src/dev-toolbar/dev-toolbar-runtime-paths.js +13 -0
- package/src/diagnostics/request-build-dedupe.js +34 -28
- package/src/diagnostics/startup-trace.d.ts +10 -1
- package/src/diagnostics/startup-trace.js +269 -241
- package/src/eco/README.md +6 -0
- package/src/eco/eco-declared-component.js +19 -14
- package/src/eco/eco.browser.js +56 -69
- package/src/eco/eco.js +156 -96
- package/src/eco/eco.types.js +5 -0
- package/src/eco/eco.utils.js +1 -10
- package/src/eco/global-injector-map.js +55 -41
- package/src/eco/lazy-injector-map.js +90 -60
- package/src/eco/module-dependencies.js +52 -44
- package/src/eco/page-layout-normalization.js +59 -49
- package/src/errors/http-error.js +50 -49
- package/src/errors/index.js +3 -12
- package/src/errors/locals-access-error.js +5 -8
- package/src/errors/undeclared-component-dependency-error.js +15 -15
- package/src/global/app-logger.js +3 -6
- package/src/hmr/README.md +10 -0
- package/src/hmr/client/hmr-runtime.js +117 -113
- package/src/hmr/client/module-update.d.ts +13 -0
- package/src/hmr/client/module-update.js +39 -0
- package/src/hmr/hmr-asset-paths.d.ts +14 -0
- package/src/hmr/hmr-asset-paths.js +22 -0
- package/src/hmr/hmr-entrypoint-output.d.ts +11 -39
- package/src/hmr/hmr-entrypoint-output.js +28 -54
- package/src/hmr/hmr-file-change-prep.js +13 -16
- package/src/hmr/hmr-registered-entrypoints-context.d.ts +7 -0
- package/src/hmr/hmr-registered-entrypoints-context.js +1 -0
- package/src/hmr/hmr-runtime-paths.d.ts +10 -0
- package/src/hmr/hmr-runtime-paths.js +13 -0
- package/src/hmr/hmr-strategy.d.ts +5 -11
- package/src/hmr/hmr-strategy.js +113 -61
- package/src/hmr/strategies/default-hmr-strategy.js +55 -33
- package/src/hmr/strategies/js-hmr-strategy.d.ts +6 -95
- package/src/hmr/strategies/js-hmr-strategy.js +104 -217
- package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +33 -27
- package/src/index.browser.js +1 -4
- package/src/index.d.ts +1 -0
- package/src/index.js +6 -47
- package/src/integrations/ghtml/ghtml-renderer.js +12 -7
- package/src/integrations/ghtml/ghtml.constants.js +1 -4
- package/src/integrations/ghtml/ghtml.plugin.js +9 -12
- package/src/islands/README.md +25 -0
- package/src/islands/island-host.d.ts +37 -0
- package/src/islands/island-host.js +55 -0
- package/src/plugins/alias-resolver-cache.js +114 -97
- package/src/plugins/alias-resolver-plugin.js +33 -47
- package/src/plugins/define-integration.js +18 -17
- package/src/plugins/eco-component-meta-plugin.js +387 -145
- package/src/plugins/foreign-jsx-override-plugin.js +49 -35
- package/src/plugins/integration-plugin.js +212 -203
- package/src/plugins/jsx-import-source.utils.js +16 -21
- package/src/plugins/processor.js +159 -149
- package/src/plugins/runtime-capability.js +1 -0
- package/src/plugins/source-transform.js +109 -79
- package/src/plugins/tsconfig-import-resolver.d.ts +17 -0
- package/src/plugins/tsconfig-import-resolver.js +218 -142
- package/src/route-renderer/README.md +12 -1
- package/src/route-renderer/orchestration/document-shell/document-shell-render.service.js +143 -136
- package/src/route-renderer/orchestration/document-shell/layout-shell-props.service.js +33 -27
- package/src/route-renderer/orchestration/foreign-child/component-render-context.js +173 -126
- package/src/route-renderer/orchestration/foreign-child/foreign-child-output.utils.js +165 -154
- package/src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js +329 -341
- package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js +33 -32
- package/src/route-renderer/orchestration/integration-renderer.d.ts +1 -0
- package/src/route-renderer/orchestration/integration-renderer.js +731 -750
- package/src/route-renderer/orchestration/integration-renderer.test-fixtures.js +168 -172
- package/src/route-renderer/orchestration/ownership-graph/component-graph-collectors.js +119 -131
- package/src/route-renderer/orchestration/ownership-graph/component-graph.js +82 -91
- package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.js +82 -76
- package/src/route-renderer/orchestration/page-browser-graph/global-injector-assets.service.js +29 -31
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.js +10 -6
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js +31 -28
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +3 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +430 -413
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.js +191 -212
- package/src/route-renderer/orchestration/page-browser-graph/processed-asset-dedupe.js +20 -24
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.d.ts +4 -0
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.js +24 -24
- package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.js +22 -24
- package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.js +41 -34
- package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.d.ts +5 -0
- package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.js +42 -22
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.js +50 -48
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.utils.js +29 -32
- package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.js +150 -156
- package/src/route-renderer/orchestration/string-markup-renderer.js +48 -51
- package/src/route-renderer/page-loading/component-dependency-collection.js +120 -124
- package/src/route-renderer/page-loading/declared-asset-collection.js +66 -94
- package/src/route-renderer/page-loading/dependency-resolver.js +101 -104
- package/src/route-renderer/page-loading/ecopages-virtual-imports.js +68 -54
- package/src/route-renderer/page-loading/lazy-entry-collection.js +75 -98
- package/src/route-renderer/page-loading/lazy-trigger-planning.js +46 -36
- package/src/route-renderer/page-loading/module-declaration-aggregation.js +38 -31
- package/src/route-renderer/page-loading/module-declaration-scripts.js +10 -15
- package/src/route-renderer/page-loading/page-dependency-bundling.js +156 -113
- package/src/route-renderer/page-loading/page-module-loader.js +130 -124
- package/src/route-renderer/route-renderer.js +74 -76
- package/src/router/client/link-intent.js +55 -39
- package/src/router/client/link-intent.test.browser.js +49 -49
- package/src/router/client/link-navigation-policy.js +114 -93
- package/src/router/client/link-navigation-policy.test.browser.js +70 -80
- package/src/router/client/navigation-coordinator.js +235 -211
- package/src/router/server/route-registry.js +237 -242
- package/src/services/assets/asset-processing-service/asset-dependency-keys.js +64 -53
- package/src/services/assets/asset-processing-service/asset-processing.service.js +287 -303
- package/src/services/assets/asset-processing-service/asset.factory.js +78 -81
- package/src/services/assets/asset-processing-service/assets.types.js +1 -0
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +57 -46
- package/src/services/assets/asset-processing-service/browser-runtime-entry-resolution.js +106 -69
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +53 -53
- package/src/services/assets/asset-processing-service/finalize-processed-asset.js +4 -6
- package/src/services/assets/asset-processing-service/grouped-content-bundles.js +79 -80
- package/src/services/assets/asset-processing-service/index.js +8 -8
- package/src/services/assets/asset-processing-service/inline-content-script-body.js +22 -16
- package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +21 -17
- package/src/services/assets/asset-processing-service/page-package.js +70 -72
- package/src/services/assets/asset-processing-service/processor.interface.js +7 -5
- package/src/services/assets/asset-processing-service/processor.registry.js +11 -14
- package/src/services/assets/asset-processing-service/processors/base/base-processor.js +59 -62
- package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +102 -117
- package/src/services/assets/asset-processing-service/processors/index.js +5 -5
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +110 -115
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +95 -97
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +136 -135
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +27 -29
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +34 -40
- package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +36 -35
- package/src/services/assets/asset-processing-service/resolve-integration-plugin.js +14 -16
- package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.js +27 -39
- package/src/services/assets/browser-bundle.service.js +52 -47
- package/src/services/cache/cache.types.js +5 -0
- package/src/services/cache/index.js +6 -7
- package/src/services/cache/memory-cache-store.js +101 -89
- package/src/services/cache/page-cache-service.js +156 -145
- package/src/services/cache/page-request-cache-coordinator.service.js +102 -102
- package/src/services/html/html-rewriter-provider.service.js +61 -61
- package/src/services/html/html-transformer.service.js +249 -266
- package/src/services/invalidation/development-invalidation.service.js +252 -241
- package/src/services/module-loading/app-module-loader.service.js +1 -0
- package/src/services/module-loading/app-server-module-transpiler.service.js +107 -101
- package/src/services/module-loading/host-module-loader-registry.js +6 -11
- package/src/services/module-loading/module-loading-types.js +1 -0
- package/src/services/module-loading/page-module-import.service.js +235 -243
- package/src/services/module-loading/route-module-build-cache-registry.js +47 -33
- package/src/services/module-loading/route-module-build-cache.js +3 -38
- package/src/services/module-loading/route-module-build-cache.store.js +195 -200
- package/src/services/module-loading/route-module-build-manifest.js +61 -74
- package/src/services/module-loading/route-module-dependency-hasher.js +136 -110
- package/src/services/module-loading/server-module-transpiler.service.js +68 -62
- package/src/services/module-loading/source-module-support.js +15 -7
- package/src/services/runtime-state/dev-graph.service.js +165 -157
- package/src/services/runtime-state/entrypoint-dependency-graph.service.js +85 -76
- package/src/services/runtime-state/server-invalidation-state.service.js +38 -30
- package/src/services/validation/schema-validation-service.js +157 -95
- package/src/services/validation/standard-schema.types.js +1 -0
- package/src/services/validation/validate-standard-schema.js +13 -13
- package/src/static-site-generator/production-page-browser-graph-prebuild.js +35 -19
- package/src/static-site-generator/sitemap-routes.js +33 -28
- package/src/static-site-generator/sitemap.js +38 -29
- package/src/static-site-generator/static-build-invalidation.js +34 -56
- package/src/static-site-generator/static-export-context.js +1 -0
- package/src/static-site-generator/static-site-generator.js +517 -553
- package/src/types/internal-types.d.ts +19 -3
- package/src/types/internal-types.js +1 -0
- package/src/types/public-types.d.ts +16 -46
- package/src/types/public-types.js +1 -0
- package/src/utils/deep-merge.js +39 -26
- package/src/utils/ecopages-route-info.js +27 -23
- package/src/utils/hash.js +3 -6
- package/src/utils/html-escaping.js +21 -8
- package/src/utils/html.js +1 -4
- package/src/utils/invariant.js +11 -10
- package/src/utils/locals-utils.js +32 -22
- package/src/utils/parse-cli-args.js +73 -65
- package/src/utils/path-pattern.js +38 -23
- package/src/utils/path-utils.module.js +8 -11
- package/src/utils/resolve-entry-file.js +70 -38
- package/src/utils/resolve-work-dir.js +23 -27
- package/src/utils/runtime.js +24 -33
- package/src/utils/server-utils.module.js +60 -52
- package/src/utils/static-file-extensions.js +52 -46
- package/src/watchers/project-watcher-ignore.js +16 -15
- package/src/watchers/project-watcher.js +385 -366
- package/src/watchers/project-watcher.test-helpers.js +45 -50
- package/src/adapters/shared/hmr/hmr-entrypoint-registrar.d.ts +0 -56
- package/src/adapters/shared/hmr/hmr-entrypoint-registrar.js +0 -106
- package/src/build/cache/dev-browser-script-cache.d.ts +0 -34
- package/src/build/cache/dev-browser-script-cache.js +0 -90
|
@@ -1,90 +1,75 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
register(path, method, handler, options) {
|
|
6
|
-
return this.addRouteHandler(path, method, handler, options?.middleware, options?.schema);
|
|
7
|
-
}
|
|
8
|
-
get(path, handler, options) {
|
|
9
|
-
return this.register(path, "GET", handler, options);
|
|
10
|
-
}
|
|
11
|
-
post(path, handler, options) {
|
|
12
|
-
return this.register(path, "POST", handler, options);
|
|
13
|
-
}
|
|
14
|
-
put(path, handler, options) {
|
|
15
|
-
return this.register(path, "PUT", handler, options);
|
|
16
|
-
}
|
|
17
|
-
delete(path, handler, options) {
|
|
18
|
-
return this.register(path, "DELETE", handler, options);
|
|
19
|
-
}
|
|
20
|
-
patch(path, handler, options) {
|
|
21
|
-
return this.register(path, "PATCH", handler, options);
|
|
22
|
-
}
|
|
23
|
-
options(path, handler, options) {
|
|
24
|
-
return this.register(path, "OPTIONS", handler, options);
|
|
25
|
-
}
|
|
26
|
-
head(path, handler, options) {
|
|
27
|
-
return this.register(path, "HEAD", handler, options);
|
|
28
|
-
}
|
|
29
|
-
route(path, method, handler, options) {
|
|
30
|
-
return this.register(path, method, handler, options);
|
|
31
|
-
}
|
|
32
|
-
add(handler) {
|
|
33
|
-
return this.addRouteHandler(handler.path, handler.method, handler.handler, handler.middleware, handler.schema);
|
|
34
|
-
}
|
|
35
|
-
group(prefixOrGroup, callback, options) {
|
|
36
|
-
if (typeof prefixOrGroup === "object") {
|
|
37
|
-
return this.registerGroup(prefixOrGroup);
|
|
1
|
+
import { AbstractApplicationAdapter, } from '../../abstract/application-adapter.js';
|
|
2
|
+
export class SharedApplicationAdapter extends AbstractApplicationAdapter {
|
|
3
|
+
register(path, method, handler, options) {
|
|
4
|
+
return this.addRouteHandler(path, method, handler, options?.middleware, options?.schema);
|
|
38
5
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
6
|
+
get(path, handler, options) {
|
|
7
|
+
return this.register(path, 'GET', handler, options);
|
|
8
|
+
}
|
|
9
|
+
post(path, handler, options) {
|
|
10
|
+
return this.register(path, 'POST', handler, options);
|
|
11
|
+
}
|
|
12
|
+
put(path, handler, options) {
|
|
13
|
+
return this.register(path, 'PUT', handler, options);
|
|
14
|
+
}
|
|
15
|
+
delete(path, handler, options) {
|
|
16
|
+
return this.register(path, 'DELETE', handler, options);
|
|
17
|
+
}
|
|
18
|
+
patch(path, handler, options) {
|
|
19
|
+
return this.register(path, 'PATCH', handler, options);
|
|
20
|
+
}
|
|
21
|
+
options(path, handler, options) {
|
|
22
|
+
return this.register(path, 'OPTIONS', handler, options);
|
|
23
|
+
}
|
|
24
|
+
head(path, handler, options) {
|
|
25
|
+
return this.register(path, 'HEAD', handler, options);
|
|
26
|
+
}
|
|
27
|
+
route(path, method, handler, options) {
|
|
28
|
+
return this.register(path, method, handler, options);
|
|
29
|
+
}
|
|
30
|
+
add(handler) {
|
|
31
|
+
return this.addRouteHandler(handler.path, handler.method, handler.handler, handler.middleware, handler.schema);
|
|
32
|
+
}
|
|
33
|
+
group(prefixOrGroup, callback, options) {
|
|
34
|
+
if (typeof prefixOrGroup === 'object') {
|
|
35
|
+
return this.registerGroup(prefixOrGroup);
|
|
36
|
+
}
|
|
37
|
+
const normalizedPrefix = prefixOrGroup.endsWith('/') ? prefixOrGroup.slice(0, -1) : prefixOrGroup;
|
|
38
|
+
const groupMiddleware = (options?.middleware ?? []);
|
|
39
|
+
const createHandler = (method) => {
|
|
40
|
+
return ((path, handler, routeOptions) => {
|
|
41
|
+
const combinedMiddleware = [
|
|
42
|
+
...groupMiddleware,
|
|
43
|
+
...(routeOptions?.middleware ?? []),
|
|
44
|
+
];
|
|
45
|
+
const fullPath = path === '/' ? normalizedPrefix : `${normalizedPrefix}${path}`;
|
|
46
|
+
this.addRouteHandler(fullPath, method, handler, combinedMiddleware.length > 0
|
|
47
|
+
? combinedMiddleware
|
|
48
|
+
: undefined, routeOptions?.schema);
|
|
49
|
+
return builder;
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const builder = {
|
|
53
|
+
get: createHandler('GET'),
|
|
54
|
+
post: createHandler('POST'),
|
|
55
|
+
put: createHandler('PUT'),
|
|
56
|
+
delete: createHandler('DELETE'),
|
|
57
|
+
patch: createHandler('PATCH'),
|
|
58
|
+
options: createHandler('OPTIONS'),
|
|
59
|
+
head: createHandler('HEAD'),
|
|
60
|
+
};
|
|
61
|
+
callback?.(builder);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
registerGroup(group) {
|
|
65
|
+
const normalizedPrefix = group.prefix.endsWith('/') ? group.prefix.slice(0, -1) : group.prefix;
|
|
66
|
+
const groupMiddleware = group.middleware ?? [];
|
|
67
|
+
for (const route of group.routes) {
|
|
68
|
+
const normalizedPath = route.path.startsWith('/') ? route.path : `/${route.path}`;
|
|
69
|
+
const fullPath = route.path === '/' ? normalizedPrefix : `${normalizedPrefix}${normalizedPath}`;
|
|
70
|
+
const combinedMiddleware = [...groupMiddleware, ...(route.middleware ?? [])];
|
|
71
|
+
this.addRouteHandler(fullPath, route.method, route.handler, combinedMiddleware.length > 0 ? combinedMiddleware : undefined, route.schema);
|
|
72
|
+
}
|
|
73
|
+
return this;
|
|
84
74
|
}
|
|
85
|
-
return this;
|
|
86
|
-
}
|
|
87
75
|
}
|
|
88
|
-
export {
|
|
89
|
-
SharedApplicationAdapter
|
|
90
|
-
};
|
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
import { readdirSync } from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileSystem } from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import { readdirSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileSystem } from '@ecopages/file-system';
|
|
4
|
+
/**
|
|
5
|
+
* Copies a source public directory into the runtime dist dir, skipping unchanged files.
|
|
6
|
+
*
|
|
7
|
+
* Dev restarts call this on every boot; byte comparison avoids rewriting identical assets.
|
|
8
|
+
*/
|
|
9
|
+
export function copyRuntimePublicDirIfChanged(sourceDir, destinationDir) {
|
|
10
|
+
fileSystem.ensureDir(destinationDir);
|
|
11
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
12
|
+
const sourcePath = path.join(sourceDir, entry.name);
|
|
13
|
+
const destinationPath = path.join(destinationDir, entry.name);
|
|
14
|
+
if (entry.isDirectory()) {
|
|
15
|
+
copyRuntimePublicDirIfChanged(sourcePath, destinationPath);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (!entry.isFile()) {
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (fileSystem.exists(destinationPath) &&
|
|
22
|
+
Buffer.compare(fileSystem.readFileAsBuffer(sourcePath), fileSystem.readFileAsBuffer(destinationPath)) === 0) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
fileSystem.copyFile(sourcePath, destinationPath);
|
|
12
26
|
}
|
|
13
|
-
if (!entry.isFile()) {
|
|
14
|
-
continue;
|
|
15
|
-
}
|
|
16
|
-
if (fileSystem.exists(destinationPath) && Buffer.compare(fileSystem.readFileAsBuffer(sourcePath), fileSystem.readFileAsBuffer(destinationPath)) === 0) {
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
fileSystem.copyFile(sourcePath, destinationPath);
|
|
20
|
-
}
|
|
21
27
|
}
|
|
22
|
-
export {
|
|
23
|
-
copyRuntimePublicDirIfChanged
|
|
24
|
-
};
|
|
@@ -1,196 +1,226 @@
|
|
|
1
|
-
import { createServer as createNetServer } from
|
|
2
|
-
import { createInterface } from
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { createServer as createNetServer } from 'node:net';
|
|
2
|
+
import { createInterface } from 'node:readline';
|
|
3
|
+
/**
|
|
4
|
+
* Detects whether a thrown error indicates the requested port is already in
|
|
5
|
+
* use.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Matches Node's `code: 'EADDRINUSE'` and Bun's surface, where the error
|
|
9
|
+
* carries the same `code` plus a human-readable message such as
|
|
10
|
+
* `"Failed to start server. Is port 3000 in use?"`. Older Bun builds that do
|
|
11
|
+
* not set `.code` are covered by a fallback match on `syscall === 'listen'`
|
|
12
|
+
* plus the message shape.
|
|
13
|
+
*/
|
|
14
|
+
export function isPortInUseError(error) {
|
|
15
|
+
if (!(error instanceof Error) && (typeof error !== 'object' || error === null)) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const maybeErr = error;
|
|
19
|
+
const code = typeof maybeErr.code === 'string' || typeof maybeErr.code === 'number' ? String(maybeErr.code) : undefined;
|
|
20
|
+
const syscall = typeof maybeErr.syscall === 'string' ? maybeErr.syscall : undefined;
|
|
21
|
+
const message = error instanceof Error ? error.message : String(maybeErr.message ?? '');
|
|
22
|
+
if (code === 'EADDRINUSE') {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (message.includes('EADDRINUSE')) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (syscall === 'listen' && /is port \d+ in use\??/i.test(message)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
5
31
|
return false;
|
|
6
|
-
}
|
|
7
|
-
const maybeErr = error;
|
|
8
|
-
const code = typeof maybeErr.code === "string" || typeof maybeErr.code === "number" ? String(maybeErr.code) : void 0;
|
|
9
|
-
const syscall = typeof maybeErr.syscall === "string" ? maybeErr.syscall : void 0;
|
|
10
|
-
const message = error instanceof Error ? error.message : String(maybeErr.message ?? "");
|
|
11
|
-
if (code === "EADDRINUSE") {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
if (message.includes("EADDRINUSE")) {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
if (syscall === "listen" && /is port \d+ in use\??/i.test(message)) {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
return false;
|
|
21
32
|
}
|
|
22
33
|
function sleep(ms) {
|
|
23
|
-
|
|
34
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
24
35
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Probes whether a TCP port is currently bindable from this process. Uses a
|
|
38
|
+
* short-lived Node `net.Server` so both Node and Bun can run it via their
|
|
39
|
+
* Node-compatible core.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* A successful probe only guarantees the port was free at probe time; callers
|
|
43
|
+
* must still handle EADDRINUSE when they actually bind, since release races
|
|
44
|
+
* can flip the answer between probe and bind.
|
|
45
|
+
*/
|
|
46
|
+
export function isPortAvailable(port, hostname = '127.0.0.1') {
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
const tester = createNetServer();
|
|
49
|
+
tester.once('error', () => resolve(false));
|
|
50
|
+
tester.once('listening', () => {
|
|
51
|
+
tester.close(() => resolve(true));
|
|
52
|
+
});
|
|
53
|
+
tester.listen(port, hostname);
|
|
31
54
|
});
|
|
32
|
-
tester.listen(port, hostname);
|
|
33
|
-
});
|
|
34
55
|
}
|
|
35
56
|
function createDefaultPrompt(stdin, stdout) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
return async (message, timeoutMs) => {
|
|
58
|
+
stdout.write(`${message} `);
|
|
59
|
+
return await new Promise((resolve) => {
|
|
60
|
+
const rl = createInterface({ input: stdin, output: undefined, terminal: false });
|
|
61
|
+
let settled = false;
|
|
62
|
+
const finish = (value) => {
|
|
63
|
+
if (settled) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
settled = true;
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
rl.close();
|
|
69
|
+
stdout.write('\n');
|
|
70
|
+
resolve(value);
|
|
71
|
+
};
|
|
72
|
+
const timer = setTimeout(() => finish(true), timeoutMs);
|
|
73
|
+
rl.once('line', (line) => {
|
|
74
|
+
const answer = line.trim().toLowerCase();
|
|
75
|
+
if (answer === '' || answer === 'y' || answer === 'yes') {
|
|
76
|
+
finish(true);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
finish(false);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
rl.once('close', () => finish(true));
|
|
83
|
+
});
|
|
84
|
+
};
|
|
63
85
|
}
|
|
64
86
|
const PREVIEW_PORT_RELEASE_RACE_RETRIES = 2;
|
|
65
87
|
const PREVIEW_PORT_FALLBACK_ATTEMPTS = 20;
|
|
66
88
|
const PREVIEW_PORT_RELEASE_RACE_DELAY_MS = 100;
|
|
67
|
-
const PREVIEW_PORT_AUTO_APPROVE_MS =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*/
|
|
108
|
-
async bind(options) {
|
|
109
|
-
const preferredPort = options.preferredPort;
|
|
110
|
-
const firstAttempt = await this.tryPort(preferredPort);
|
|
111
|
-
if (firstAttempt.status === "bound") {
|
|
112
|
-
return firstAttempt.boundPort;
|
|
113
|
-
}
|
|
114
|
-
if (firstAttempt.status === "factory-refused") {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
await this.shouldFallback(preferredPort, options.allowPortFallback, firstAttempt.error);
|
|
118
|
-
return await this.fallForward(preferredPort);
|
|
119
|
-
}
|
|
120
|
-
async shouldFallback(preferredPort, allowPortFallback, error) {
|
|
121
|
-
if (this.interactive && this.prompt) {
|
|
122
|
-
const nextFree = await this.findNextFreePort(preferredPort);
|
|
123
|
-
const nextFreeSuffix = typeof nextFree === "number" ? ` (next free: ${nextFree})` : "";
|
|
124
|
-
const autoApproveHint = `auto-yes in ${Math.round(this.autoApproveMs / 1e3)}s`;
|
|
125
|
-
const message = `[@ecopages/core] Port ${preferredPort} is already in use${nextFreeSuffix}. Bind there instead? [Y/n] (${autoApproveHint})`;
|
|
126
|
-
const accepted = await this.prompt(message, this.autoApproveMs);
|
|
127
|
-
if (!accepted) {
|
|
128
|
-
throw error;
|
|
129
|
-
}
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (!allowPortFallback) {
|
|
133
|
-
throw error;
|
|
89
|
+
const PREVIEW_PORT_AUTO_APPROVE_MS = 10_000;
|
|
90
|
+
/**
|
|
91
|
+
* Owns preview-port assignment: probing, race-release retries, fallback, and
|
|
92
|
+
* an optional interactive auto-approve prompt.
|
|
93
|
+
*
|
|
94
|
+
* @remarks
|
|
95
|
+
* Single-process API. The manager does not cache ports across calls; each
|
|
96
|
+
* `bind()` runs fresh from the preferred port.
|
|
97
|
+
*/
|
|
98
|
+
export class PortManager {
|
|
99
|
+
startOnPort;
|
|
100
|
+
warn;
|
|
101
|
+
probePort;
|
|
102
|
+
prompt;
|
|
103
|
+
interactive;
|
|
104
|
+
autoApproveMs;
|
|
105
|
+
maxPortOffset;
|
|
106
|
+
releaseRaceRetries;
|
|
107
|
+
releaseRaceDelayMs;
|
|
108
|
+
constructor(options) {
|
|
109
|
+
this.startOnPort = options.startOnPort;
|
|
110
|
+
this.warn = options.warn;
|
|
111
|
+
this.probePort = options.probePort ?? isPortAvailable;
|
|
112
|
+
this.maxPortOffset = options.maxPortOffset ?? PREVIEW_PORT_FALLBACK_ATTEMPTS - 1;
|
|
113
|
+
this.releaseRaceRetries = options.releaseRaceRetries ?? PREVIEW_PORT_RELEASE_RACE_RETRIES;
|
|
114
|
+
this.releaseRaceDelayMs = options.releaseRaceDelayMs ?? PREVIEW_PORT_RELEASE_RACE_DELAY_MS;
|
|
115
|
+
this.autoApproveMs = options.autoApproveMs ?? PREVIEW_PORT_AUTO_APPROVE_MS;
|
|
116
|
+
this.interactive =
|
|
117
|
+
typeof options.interactive === 'boolean'
|
|
118
|
+
? options.interactive
|
|
119
|
+
: Boolean(typeof process !== 'undefined' && process.stdin?.isTTY && process.stdout?.isTTY);
|
|
120
|
+
if (options.prompt) {
|
|
121
|
+
this.prompt = options.prompt;
|
|
122
|
+
}
|
|
123
|
+
else if (this.interactive) {
|
|
124
|
+
this.prompt = createDefaultPrompt(process.stdin, process.stdout);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.prompt = undefined;
|
|
128
|
+
}
|
|
134
129
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Binds the preview server to the preferred port.
|
|
132
|
+
*
|
|
133
|
+
* @remarks
|
|
134
|
+
* When the preferred port is free, binds it directly — no prompt, no
|
|
135
|
+
* fallback. The interactive prompt only fires on a genuine EADDRINUSE
|
|
136
|
+
* collision (TTY runs), and auto-approves after
|
|
137
|
+
* {@link PortManagerOptions.autoApproveMs}. In non-interactive (CI) runs,
|
|
138
|
+
* the manager falls back silently only when
|
|
139
|
+
* {@link BindPortOptions.allowPortFallback} is true (the default-port
|
|
140
|
+
* path); pinned ports rethrow EADDRINUSE so Docker-style fixed-port
|
|
141
|
+
* deployments fail loudly instead of silently moving.
|
|
142
|
+
*/
|
|
143
|
+
async bind(options) {
|
|
144
|
+
const preferredPort = options.preferredPort;
|
|
145
|
+
const firstAttempt = await this.tryPort(preferredPort);
|
|
146
|
+
if (firstAttempt.status === 'bound') {
|
|
147
|
+
return firstAttempt.boundPort;
|
|
148
|
+
}
|
|
149
|
+
if (firstAttempt.status === 'factory-refused') {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
// firstAttempt.status === 'port-in-use'
|
|
153
|
+
await this.shouldFallback(preferredPort, options.allowPortFallback, firstAttempt.error);
|
|
154
|
+
return await this.fallForward(preferredPort);
|
|
146
155
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
* factory surfaces loudly instead of causing a silent fallback. A `null`
|
|
159
|
-
* return from the factory (no throw) is reported as `factory-refused`, not
|
|
160
|
-
* conflated with a real collision.
|
|
161
|
-
*/
|
|
162
|
-
async tryPort(port) {
|
|
163
|
-
for (let attempt = 0; attempt < this.releaseRaceRetries; attempt += 1) {
|
|
164
|
-
try {
|
|
165
|
-
const bound = await this.startOnPort(port);
|
|
166
|
-
if (bound) {
|
|
167
|
-
return { status: "bound", boundPort: bound };
|
|
156
|
+
async shouldFallback(preferredPort, allowPortFallback, error) {
|
|
157
|
+
if (this.interactive && this.prompt) {
|
|
158
|
+
const nextFree = await this.findNextFreePort(preferredPort);
|
|
159
|
+
const nextFreeSuffix = typeof nextFree === 'number' ? ` (next free: ${nextFree})` : '';
|
|
160
|
+
const autoApproveHint = `auto-yes in ${Math.round(this.autoApproveMs / 1000)}s`;
|
|
161
|
+
const message = `[@ecopages/core] Port ${preferredPort} is already in use${nextFreeSuffix}. Bind there instead? [Y/n] (${autoApproveHint})`;
|
|
162
|
+
const accepted = await this.prompt(message, this.autoApproveMs);
|
|
163
|
+
if (!accepted) {
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
168
167
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (!isPortInUseError(error)) {
|
|
172
|
-
throw error;
|
|
168
|
+
if (!allowPortFallback) {
|
|
169
|
+
throw error;
|
|
173
170
|
}
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
}
|
|
172
|
+
async fallForward(preferredPort) {
|
|
173
|
+
for (let offset = 1; offset <= this.maxPortOffset; offset += 1) {
|
|
174
|
+
const candidatePort = preferredPort + offset;
|
|
175
|
+
const attempt = await this.tryPort(candidatePort);
|
|
176
|
+
if (attempt.status === 'bound') {
|
|
177
|
+
this.warn?.(`Port ${preferredPort} is in use; preview serving on port ${attempt.boundPort} instead. Pin the preview port with --port or ECOPAGES_PORT when a fixed binding is required.`);
|
|
178
|
+
return attempt.boundPort;
|
|
179
|
+
}
|
|
176
180
|
}
|
|
177
|
-
|
|
178
|
-
}
|
|
181
|
+
return null;
|
|
179
182
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Attempts to bind via {@link PortManagerOptions.startOnPort} on `port`,
|
|
185
|
+
* retrying EADDRINUSE collisions up to
|
|
186
|
+
* {@link PortManagerOptions.releaseRaceRetries} to ride through TIME_WAIT
|
|
187
|
+
* race release.
|
|
188
|
+
*
|
|
189
|
+
* @remarks
|
|
190
|
+
* Only EADDRINUSE is retryable. Any other error is rethrown synchronously
|
|
191
|
+
* rather than swallowed as a soft "port taken" result, so a misconfigured
|
|
192
|
+
* factory surfaces loudly instead of causing a silent fallback. A `null`
|
|
193
|
+
* return from the factory (no throw) is reported as `factory-refused`, not
|
|
194
|
+
* conflated with a real collision.
|
|
195
|
+
*/
|
|
196
|
+
async tryPort(port) {
|
|
197
|
+
for (let attempt = 0; attempt < this.releaseRaceRetries; attempt += 1) {
|
|
198
|
+
try {
|
|
199
|
+
const bound = await this.startOnPort(port);
|
|
200
|
+
if (bound) {
|
|
201
|
+
return { status: 'bound', boundPort: bound };
|
|
202
|
+
}
|
|
203
|
+
return { status: 'factory-refused' };
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
if (!isPortInUseError(error)) {
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
if (attempt === this.releaseRaceRetries - 1) {
|
|
210
|
+
return { status: 'port-in-use', error };
|
|
211
|
+
}
|
|
212
|
+
await sleep(this.releaseRaceDelayMs);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return { status: 'factory-refused' };
|
|
216
|
+
}
|
|
217
|
+
async findNextFreePort(startPort) {
|
|
218
|
+
for (let offset = 1; offset <= this.maxPortOffset; offset += 1) {
|
|
219
|
+
const candidate = startPort + offset;
|
|
220
|
+
if (await this.probePort(candidate)) {
|
|
221
|
+
return candidate;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
188
225
|
}
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
226
|
}
|
|
192
|
-
export {
|
|
193
|
-
PortManager,
|
|
194
|
-
isPortAvailable,
|
|
195
|
-
isPortInUseError
|
|
196
|
-
};
|