@ecopages/core 0.2.0-beta.20 → 0.2.0-beta.21

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.
Files changed (31) hide show
  1. package/package.json +2 -2
  2. package/src/adapters/bun/hmr-manager.d.ts +1 -2
  3. package/src/adapters/bun/hmr-manager.js +2 -2
  4. package/src/adapters/bun/server-adapter.js +5 -4
  5. package/src/adapters/node/node-hmr-manager.d.ts +1 -2
  6. package/src/adapters/node/node-hmr-manager.js +2 -2
  7. package/src/adapters/node/server-adapter.js +5 -6
  8. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +21 -28
  9. package/src/adapters/shared/hmr-entrypoint-registrar.js +49 -31
  10. package/src/adapters/shared/runtime-server-lifecycle.d.ts +9 -1
  11. package/src/adapters/shared/runtime-server-lifecycle.js +10 -3
  12. package/src/adapters/shared/server-static-builder.js +4 -1
  13. package/src/adapters/shared/shared-hmr-manager.d.ts +29 -23
  14. package/src/adapters/shared/shared-hmr-manager.js +144 -108
  15. package/src/build/app-build-manifest-runtime.d.ts +1 -2
  16. package/src/build/app-build-manifest-runtime.js +0 -3
  17. package/src/hmr/hmr-entrypoint-output.d.ts +18 -3
  18. package/src/hmr/hmr-entrypoint-output.js +6 -11
  19. package/src/hmr/hmr-strategy.d.ts +15 -0
  20. package/src/hmr/hmr-strategy.js +19 -0
  21. package/src/hmr/strategies/js-hmr-strategy.d.ts +0 -5
  22. package/src/hmr/strategies/js-hmr-strategy.js +0 -2
  23. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.d.ts +0 -1
  24. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +13 -32
  25. package/src/services/assets/browser-bundle.service.js +1 -1
  26. package/src/services/module-loading/page-module-import.service.js +0 -1
  27. package/src/services/module-loading/route-module-dependency-hasher.js +1 -0
  28. package/src/types/public-types.d.ts +10 -18
  29. package/src/watchers/project-watcher.d.ts +2 -2
  30. package/src/watchers/project-watcher.js +23 -28
  31. package/src/watchers/project-watcher.test-helpers.js +5 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/core",
3
- "version": "0.2.0-beta.20",
3
+ "version": "0.2.0-beta.21",
4
4
  "description": "Core package for Ecopages",
5
5
  "keywords": [
6
6
  "ecopages",
@@ -17,7 +17,7 @@
17
17
  "directory": "packages/core"
18
18
  },
19
19
  "dependencies": {
20
- "@ecopages/file-system": "0.2.0-beta.20",
20
+ "@ecopages/file-system": "0.2.0-beta.21",
21
21
  "@ecopages/logger": "^0.2.3",
22
22
  "@ecopages/scripts-injector": "^0.1.5",
23
23
  "@oxc-project/runtime": "0.134.0",
@@ -7,7 +7,6 @@ type BunSocketHandler = WebSocketHandler<unknown>;
7
7
  export interface HmrManagerParams {
8
8
  appConfig: EcoPagesAppConfig;
9
9
  bridge: ClientBridge;
10
- registrationTimeoutMs?: number;
11
10
  }
12
11
  /**
13
12
  * Bun development HMR manager.
@@ -27,7 +26,7 @@ export declare class HmrManager extends SharedHmrManager {
27
26
  * generation to `SharedHmrManager`. The Bun subclass only supplies the
28
27
  * transport-specific dependency graph policy and websocket hook surface.
29
28
  */
30
- constructor({ appConfig, bridge, registrationTimeoutMs }: HmrManagerParams);
29
+ constructor({ appConfig, bridge }: HmrManagerParams);
31
30
  /**
32
31
  * Reuses the shared in-memory dependency graph when possible and otherwise
33
32
  * creates the Bun-compatible default graph implementation.
@@ -13,8 +13,8 @@ class HmrManager extends SharedHmrManager {
13
13
  * generation to `SharedHmrManager`. The Bun subclass only supplies the
14
14
  * transport-specific dependency graph policy and websocket hook surface.
15
15
  */
16
- constructor({ appConfig, bridge, registrationTimeoutMs }) {
17
- super({ appConfig, bridge, registrationTimeoutMs });
16
+ constructor({ appConfig, bridge }) {
17
+ super({ appConfig, bridge });
18
18
  }
19
19
  /**
20
20
  * Reuses the shared in-memory dependency graph when possible and otherwise
@@ -17,10 +17,10 @@ import { createBunUserWebSocketLifecycle } from "../shared/bun-user-websocket-li
17
17
  import { createEcopagesSocket } from "../shared/websocket-lifecycle.js";
18
18
  import { resolveServeRuntimeOrigin } from "../shared/runtime-app-bootstrap.js";
19
19
  import {
20
+ attachHmrToIntegrations,
20
21
  disposeDevResources,
21
22
  maybeInjectAdapterHmrHtmlResponse,
22
- prepareRuntimePublicDir,
23
- wireIntegrationHmrManagers
23
+ prepareRuntimePublicDir
24
24
  } from "../shared/runtime-server-lifecycle.js";
25
25
  import { ClientBridge } from "./client-bridge.js";
26
26
  import { setAppDevClientBridge } from "../../dev/client-bridge-registry.js";
@@ -193,12 +193,13 @@ class BunServerAdapter extends SharedServerAdapter {
193
193
  await setupAppRuntimePlugins({
194
194
  appConfig: this.appConfig,
195
195
  runtimeOrigin: this.runtimeOrigin,
196
- hmrManager: this.hmrManager,
197
196
  onRuntimePlugin: (plugin) => {
198
197
  this.registerBunRuntimePlugin(plugin);
199
198
  }
200
199
  });
201
- wireIntegrationHmrManagers(this.appConfig, this.hmrManager);
200
+ if (options?.watch) {
201
+ attachHmrToIntegrations(this.appConfig, this.hmrManager);
202
+ }
202
203
  } catch (error) {
203
204
  appLogger.error(`Failed to initialize plugins: ${error instanceof Error ? error.message : String(error)}`);
204
205
  throw error;
@@ -4,7 +4,6 @@ import { SharedHmrManager } from '../shared/shared-hmr-manager.js';
4
4
  export interface NodeHmrManagerParams {
5
5
  appConfig: EcoPagesAppConfig;
6
6
  bridge: IClientBridge;
7
- registrationTimeoutMs?: number;
8
7
  }
9
8
  /**
10
9
  * Node development HMR manager.
@@ -29,7 +28,7 @@ export declare class NodeHmrManager extends SharedHmrManager {
29
28
  * actually differs: dependency-graph storage, missing-file tolerance, and how
30
29
  * runtime bundle failures disable HMR.
31
30
  */
32
- constructor({ appConfig, bridge, registrationTimeoutMs }: NodeHmrManagerParams);
31
+ constructor({ appConfig, bridge }: NodeHmrManagerParams);
33
32
  /**
34
33
  * Reuses the shared in-memory dependency graph when the app already has one and
35
34
  * otherwise creates the default Node development graph.
@@ -13,8 +13,8 @@ class NodeHmrManager extends SharedHmrManager {
13
13
  * actually differs: dependency-graph storage, missing-file tolerance, and how
14
14
  * runtime bundle failures disable HMR.
15
15
  */
16
- constructor({ appConfig, bridge, registrationTimeoutMs }) {
17
- super({ appConfig, bridge, registrationTimeoutMs });
16
+ constructor({ appConfig, bridge }) {
17
+ super({ appConfig, bridge });
18
18
  }
19
19
  /**
20
20
  * Reuses the shared in-memory dependency graph when the app already has one and
@@ -15,10 +15,10 @@ import { SharedServerAdapter } from "../shared/server-adapter.js";
15
15
  import { ServerStaticBuilder } from "../shared/server-static-builder.js";
16
16
  import { DEFAULT_ECOPAGES_HOSTNAME, DEFAULT_ECOPAGES_PORT } from "../../config/constants.js";
17
17
  import {
18
+ attachHmrToIntegrations,
19
+ disposeDevResources,
18
20
  maybeInjectAdapterHmrHtmlResponse,
19
- prepareRuntimePublicDir,
20
- wireIntegrationHmrManagers,
21
- disposeDevResources
21
+ prepareRuntimePublicDir
22
22
  } from "../shared/runtime-server-lifecycle.js";
23
23
  import { resolveServeRuntimeOrigin } from "../shared/runtime-app-bootstrap.js";
24
24
  import { NodeClientAbortError, NodeHttpRequestBridge } from "./http-request-bridge.js";
@@ -119,8 +119,7 @@ class NodeServerAdapter extends SharedServerAdapter {
119
119
  if (!this.deferRuntimeAssetSetup) {
120
120
  await setupAppRuntimePlugins({
121
121
  appConfig: this.appConfig,
122
- runtimeOrigin: this.runtimeOrigin,
123
- hmrManager: this.hmrManager ?? void 0
122
+ runtimeOrigin: this.runtimeOrigin
124
123
  });
125
124
  }
126
125
  await this.initializeSharedRouteHandling({
@@ -298,7 +297,7 @@ class NodeServerAdapter extends SharedServerAdapter {
298
297
  }
299
298
  });
300
299
  }
301
- wireIntegrationHmrManagers(this.appConfig, this.hmrManager);
300
+ attachHmrToIntegrations(this.appConfig, this.hmrManager);
302
301
  this.configureSharedResponseHandlers(this.staticRoutes, this.hmrManager);
303
302
  const watcher = new ProjectWatcher({
304
303
  config: this.appConfig,
@@ -1,20 +1,4 @@
1
- /**
2
- * Shared runtime state used while registering HMR-owned entrypoints.
3
- */
4
- export interface HmrEntrypointRegistrarOptions {
5
- /** Absolute source directory used to derive the emitted HMR path. */
6
- srcDir: string;
7
- /** Absolute distribution directory where HMR outputs are written. */
8
- distDir: string;
9
- /** In-flight registrations keyed by normalized absolute entrypoint path. */
10
- entrypointRegistrations: Map<string, Promise<string>>;
11
- /** Stable entrypoint-to-output mapping retained once an entrypoint is registered. */
12
- watchedFiles: Map<string, string>;
13
- /** Runtime-specific cleanup invoked when an entrypoint registration fails. */
14
- clearFailedRegistration?: (entrypointPath: string) => void;
15
- /** Development-only guardrail for integrations that never finish producing output. */
16
- registrationTimeoutMs: number;
17
- }
1
+ import { type ResolvedHmrEntrypoint } from '../../hmr/hmr-entrypoint-output.js';
18
2
  /**
19
3
  * Runtime-specific hooks required to materialize a single HMR entrypoint.
20
4
  */
@@ -28,26 +12,35 @@ export interface HmrEntrypointRegistrationOptions {
28
12
  */
29
13
  getMissingOutputError(entrypointPath: string, outputPath: string): Error;
30
14
  }
15
+ export interface HmrEntrypointRegistrarOptions {
16
+ /** Absolute source directory used to derive the emitted HMR path. */
17
+ srcDir: string;
18
+ /** Absolute distribution directory where HMR outputs are written. */
19
+ distDir: string;
20
+ /** Runtime-specific cleanup invoked when an entrypoint registration fails. */
21
+ clearFailedRegistration?: (entrypointPath: string) => void;
22
+ }
31
23
  /**
32
24
  * Coordinates the shared HMR entrypoint registration lifecycle for both Node and Bun managers.
33
25
  *
34
- * The registrar owns the cross-runtime policy: normalize entrypoint identities, dedupe concurrent
35
- * registrations, derive the emitted `_hmr` output path, clear stale output before rebuilding, and
36
- * apply the development timeout that prevents unresolved registrations from hanging navigation.
37
- * Runtime-specific managers remain responsible for the actual emit step and any cleanup outside
38
- * this shared registration flow.
26
+ * @remarks
27
+ * The registrar owns in-flight deduplication and the registered entrypoint table.
28
+ * A source path is committed only after emit succeeds and the expected output exists on disk.
39
29
  */
40
30
  export declare class HmrEntrypointRegistrar {
41
31
  private readonly options;
32
+ private readonly inFlight;
33
+ private readonly registered;
42
34
  constructor(options: HmrEntrypointRegistrarOptions);
35
+ getRegistered(): ReadonlyMap<string, ResolvedHmrEntrypoint>;
36
+ getWatchedFiles(): Map<string, string>;
37
+ clearRegistration(entrypointPath: string): void;
38
+ clearAll(): void;
43
39
  /**
44
- * Registers a single source entrypoint and returns the browser URL for its emitted HMR module.
40
+ * Registers a single source entrypoint and returns its verified HMR artifact.
45
41
  *
46
- * Concurrent requests for the same normalized entrypoint share the same in-flight promise so the
47
- * integration only builds once per registration cycle.
42
+ * Concurrent requests for the same normalized entrypoint share the same in-flight promise.
48
43
  */
49
- registerEntrypoint(entrypointPath: string, registrationOptions: HmrEntrypointRegistrationOptions): Promise<string>;
44
+ registerEntrypoint(entrypointPath: string, registrationOptions: HmrEntrypointRegistrationOptions): Promise<ResolvedHmrEntrypoint>;
50
45
  private registerEntrypointInternal;
51
- private awaitEntrypointRegistration;
52
- private getEntrypointOutput;
53
46
  }
@@ -1,62 +1,80 @@
1
1
  import path from "node:path";
2
2
  import { fileSystem } from "@ecopages/file-system";
3
- import { removeStaleHmrEntrypointOutput, resolveHmrEntrypointOutputPaths } from "../../hmr/hmr-entrypoint-output.js";
3
+ import {
4
+ removeStaleHmrEntrypointOutput,
5
+ resolveHmrEntrypointOutputPaths
6
+ } from "../../hmr/hmr-entrypoint-output.js";
4
7
  class HmrEntrypointRegistrar {
5
8
  options;
9
+ inFlight = /* @__PURE__ */ new Map();
10
+ registered = /* @__PURE__ */ new Map();
6
11
  constructor(options) {
7
12
  this.options = options;
8
13
  }
14
+ getRegistered() {
15
+ return this.registered;
16
+ }
17
+ getWatchedFiles() {
18
+ const watchedFiles = /* @__PURE__ */ new Map();
19
+ for (const [sourcePath, entrypoint] of this.registered) {
20
+ watchedFiles.set(sourcePath, entrypoint.outputUrl);
21
+ }
22
+ return watchedFiles;
23
+ }
24
+ clearRegistration(entrypointPath) {
25
+ this.registered.delete(path.resolve(entrypointPath));
26
+ }
27
+ clearAll() {
28
+ this.inFlight.clear();
29
+ this.registered.clear();
30
+ }
9
31
  /**
10
- * Registers a single source entrypoint and returns the browser URL for its emitted HMR module.
32
+ * Registers a single source entrypoint and returns its verified HMR artifact.
11
33
  *
12
- * Concurrent requests for the same normalized entrypoint share the same in-flight promise so the
13
- * integration only builds once per registration cycle.
34
+ * Concurrent requests for the same normalized entrypoint share the same in-flight promise.
14
35
  */
15
36
  async registerEntrypoint(entrypointPath, registrationOptions) {
16
37
  const normalizedEntrypoint = path.resolve(entrypointPath);
17
- const existingRegistration = this.options.entrypointRegistrations.get(normalizedEntrypoint);
38
+ const existing = this.registered.get(normalizedEntrypoint);
39
+ if (existing && fileSystem.exists(existing.outputPath)) {
40
+ return existing;
41
+ }
42
+ const existingRegistration = this.inFlight.get(normalizedEntrypoint);
18
43
  if (existingRegistration) {
19
- return await this.awaitEntrypointRegistration(existingRegistration, normalizedEntrypoint);
44
+ return await existingRegistration;
20
45
  }
21
46
  const registration = this.registerEntrypointInternal(normalizedEntrypoint, registrationOptions);
22
- this.options.entrypointRegistrations.set(normalizedEntrypoint, registration);
47
+ this.inFlight.set(normalizedEntrypoint, registration);
23
48
  try {
24
- return await this.awaitEntrypointRegistration(registration, normalizedEntrypoint);
49
+ return await registration;
25
50
  } catch (error) {
26
51
  this.options.clearFailedRegistration?.(normalizedEntrypoint);
52
+ this.registered.delete(normalizedEntrypoint);
27
53
  throw error;
28
54
  } finally {
29
- this.options.entrypointRegistrations.delete(normalizedEntrypoint);
55
+ if (this.inFlight.get(normalizedEntrypoint) === registration) {
56
+ this.inFlight.delete(normalizedEntrypoint);
57
+ }
30
58
  }
31
59
  }
32
60
  async registerEntrypointInternal(entrypointPath, registrationOptions) {
33
- if (this.options.watchedFiles.has(entrypointPath)) {
34
- return this.options.watchedFiles.get(entrypointPath);
35
- }
36
- const { outputPath, outputUrl } = this.getEntrypointOutput(entrypointPath);
37
- this.options.watchedFiles.set(entrypointPath, outputUrl);
61
+ const { outputPath, outputUrl } = resolveHmrEntrypointOutputPaths(
62
+ this.options.srcDir,
63
+ this.options.distDir,
64
+ entrypointPath
65
+ );
38
66
  removeStaleHmrEntrypointOutput(outputPath, "HMR");
39
67
  await registrationOptions.emit(entrypointPath, outputPath);
40
68
  if (!fileSystem.exists(outputPath)) {
41
69
  throw registrationOptions.getMissingOutputError(entrypointPath, outputPath);
42
70
  }
43
- return outputUrl;
44
- }
45
- async awaitEntrypointRegistration(registration, entrypointPath) {
46
- if (process.env.NODE_ENV !== "development") {
47
- return await registration;
48
- }
49
- return await Promise.race([
50
- registration,
51
- new Promise((_, reject) => {
52
- setTimeout(() => {
53
- reject(new Error(`[HMR] Timed out registering entrypoint: ${entrypointPath}`));
54
- }, this.options.registrationTimeoutMs);
55
- })
56
- ]);
57
- }
58
- getEntrypointOutput(entrypointPath) {
59
- return resolveHmrEntrypointOutputPaths(this.options.srcDir, this.options.distDir, entrypointPath);
71
+ const resolved = {
72
+ sourcePath: entrypointPath,
73
+ outputPath,
74
+ outputUrl
75
+ };
76
+ this.registered.set(entrypointPath, resolved);
77
+ return resolved;
60
78
  }
61
79
  }
62
80
  export {
@@ -6,7 +6,15 @@ import type { ProjectWatcher } from '../../watchers/project-watcher.js';
6
6
  */
7
7
  export declare function prepareRuntimePublicDir(appConfig: EcoPagesAppConfig): void;
8
8
  /**
9
- * Propagates browser build plugins and the shared HMR manager into integrations.
9
+ * Attaches the shared HMR manager to every integration exactly once per app config.
10
+ *
11
+ * @remarks
12
+ * Runtime plugin setup must not call `integration.setHmrManager()` directly. Adapters invoke
13
+ * this after the manager is enabled so late plugin init cannot double-bind integrations.
14
+ */
15
+ export declare function attachHmrToIntegrations(appConfig: EcoPagesAppConfig, hmrManager: IHmrManager): void;
16
+ /**
17
+ * @deprecated Use {@link attachHmrToIntegrations} instead.
10
18
  */
11
19
  export declare function wireIntegrationHmrManagers(appConfig: EcoPagesAppConfig, hmrManager: IHmrManager): void;
12
20
  /**
@@ -2,7 +2,6 @@ import path from "node:path";
2
2
  import { fileSystem } from "@ecopages/file-system";
3
3
  import { RESOLVED_ASSETS_DIR } from "../../config/constants.js";
4
4
  import { disposeAppBuildRuntime } from "../../build/build-runtime.js";
5
- import { getAppBrowserBuildPlugins } from "../../build/build-adapter.js";
6
5
  import { copyRuntimePublicDirIfChanged } from "./copy-runtime-public-dir.js";
7
6
  import { clearAppDevClientBridge } from "../../dev/client-bridge-registry.js";
8
7
  import { clearAppHmrManager } from "../../dev/hmr-manager-registry.js";
@@ -14,12 +13,19 @@ function prepareRuntimePublicDir(appConfig) {
14
13
  }
15
14
  fileSystem.ensureDir(path.join(appConfig.absolutePaths.distDir, RESOLVED_ASSETS_DIR));
16
15
  }
17
- function wireIntegrationHmrManagers(appConfig, hmrManager) {
18
- hmrManager.setPlugins(getAppBrowserBuildPlugins(appConfig));
16
+ const attachedHmrApps = /* @__PURE__ */ new WeakSet();
17
+ function attachHmrToIntegrations(appConfig, hmrManager) {
18
+ if (attachedHmrApps.has(appConfig)) {
19
+ return;
20
+ }
21
+ attachedHmrApps.add(appConfig);
19
22
  for (const integration of appConfig.integrations) {
20
23
  integration.setHmrManager(hmrManager);
21
24
  }
22
25
  }
26
+ function wireIntegrationHmrManagers(appConfig, hmrManager) {
27
+ attachHmrToIntegrations(appConfig, hmrManager);
28
+ }
23
29
  async function maybeInjectAdapterHmrHtmlResponse(response, options) {
24
30
  if (shouldInjectHmrHtmlResponse(options.watch, options.hmrManager, options.hostOwnsDevClient) && isHtmlResponse(response)) {
25
31
  return injectHmrRuntimeIntoHtmlResponse(response);
@@ -36,6 +42,7 @@ async function disposeDevResources(options) {
36
42
  await options.previewHost.stop();
37
43
  }
38
44
  export {
45
+ attachHmrToIntegrations,
39
46
  disposeDevResources,
40
47
  maybeInjectAdapterHmrHtmlResponse,
41
48
  prepareRuntimePublicDir,
@@ -3,6 +3,7 @@ import { fileSystem } from "@ecopages/file-system";
3
3
  import { DEFAULT_ECOPAGES_HOSTNAME, DEFAULT_ECOPAGES_PORT } from "../../config/constants.js";
4
4
  import { appLogger } from "../../global/app-logger.js";
5
5
  import { build, getAppBuildAdapter, setupAppRuntimePlugins } from "../../build/build-adapter.js";
6
+ import { attachHmrToIntegrations } from "./runtime-server-lifecycle.js";
6
7
  import { resolveBuildProfileOptions } from "../../build/build-profile-options.js";
7
8
  import {
8
9
  getServerBundleOutputPaths,
@@ -76,9 +77,11 @@ class ServerStaticBuilder {
76
77
  await setupAppRuntimePlugins({
77
78
  appConfig: this.appConfig,
78
79
  runtimeOrigin: this.runtimeOrigin,
79
- hmrManager: this.hmrManager,
80
80
  onRuntimePlugin: this.onRuntimePlugin
81
81
  });
82
+ if (this.hmrManager?.isEnabled()) {
83
+ attachHmrToIntegrations(this.appConfig, this.hmrManager);
84
+ }
82
85
  } finally {
83
86
  appLogger.debugTimeEnd("refreshRuntimeAssets");
84
87
  }
@@ -1,7 +1,8 @@
1
1
  import fs from 'node:fs';
2
+ import { type ResolvedHmrEntrypoint } from '../../hmr/hmr-entrypoint-output.js';
2
3
  import type { DefaultHmrContext, EcoPagesAppConfig, IHmrManager, IClientBridge } from '../../types/internal-types.js';
3
- import type { EcoBuildPlugin } from '../../build/build-types.js';
4
4
  import { type HmrStrategy } from '../../hmr/hmr-strategy.js';
5
+ import { DevelopmentInvalidationService } from '../../services/invalidation/development-invalidation.service.js';
5
6
  import type { ClientBridgeEvent } from '../../types/public-types.js';
6
7
  import { HmrEntrypointRegistrar } from './hmr-entrypoint-registrar.js';
7
8
  import { BrowserBundleService } from '../../services/assets/browser-bundle.service.js';
@@ -10,28 +11,25 @@ import type { ServerModuleTranspiler } from '../../services/module-loading/serve
10
11
  type HandleFileChangeOptions = {
11
12
  broadcast?: boolean;
12
13
  };
13
- export declare function resolveHmrRegistrationTimeoutMs(explicitTimeoutMs?: number): number;
14
14
  type SharedHmrManagerParams = {
15
15
  appConfig: EcoPagesAppConfig;
16
16
  bridge: IClientBridge;
17
- registrationTimeoutMs?: number;
18
17
  };
19
18
  export declare abstract class SharedHmrManager implements IHmrManager {
20
19
  readonly appConfig: EcoPagesAppConfig;
21
20
  protected readonly bridge: IClientBridge;
22
21
  protected watchers: Map<string, fs.FSWatcher>;
23
- protected watchedFiles: Map<string, string>;
24
- protected entrypointRegistrations: Map<string, Promise<string>>;
25
22
  protected distDir: string;
26
- protected plugins: EcoBuildPlugin[];
27
23
  protected enabled: boolean;
28
24
  protected strategies: HmrStrategy[];
29
25
  protected readonly entrypointRegistrar: HmrEntrypointRegistrar;
30
26
  protected readonly browserBundleService: BrowserBundleService;
27
+ protected readonly invalidationService: DevelopmentInvalidationService;
31
28
  protected readonly entrypointDependencyGraph: EntrypointDependencyGraph;
32
29
  protected readonly serverModuleTranspiler: ServerModuleTranspiler;
33
30
  private runtimeBuildPromise;
34
- constructor({ appConfig, bridge, registrationTimeoutMs }: SharedHmrManagerParams);
31
+ private runtimeReady;
32
+ constructor({ appConfig, bridge }: SharedHmrManagerParams);
35
33
  protected abstract createEntrypointDependencyGraph(existingEntrypointDependencyGraph: EntrypointDependencyGraph): EntrypointDependencyGraph;
36
34
  protected shouldSkipMissingFileChange(_filePath: string): boolean;
37
35
  protected onRuntimeBundleFailure(error: unknown): void;
@@ -39,26 +37,39 @@ export declare abstract class SharedHmrManager implements IHmrManager {
39
37
  protected shouldJsStrategyProcessEntrypoint(entrypointPath: string): boolean;
40
38
  protected initializeStrategies(): void;
41
39
  registerStrategy(strategy: HmrStrategy): void;
42
- setPlugins(plugins: EcoBuildPlugin[]): void;
43
40
  setEnabled(enabled: boolean): void;
44
41
  isEnabled(): boolean;
45
42
  isRuntimeReady(): boolean;
46
43
  /**
47
- * Builds the browser HMR runtime once and reuses the in-flight build for concurrent callers.
44
+ * Builds the browser HMR runtime once per manager session and reuses the in-flight build for concurrent callers.
48
45
  */
49
46
  ensureRuntimeReady(): Promise<boolean>;
50
47
  buildRuntime(): Promise<void>;
51
48
  getRuntimePath(): string;
52
49
  private buildRuntimeInternal;
50
+ broadcast(event: ClientBridgeEvent): void;
51
+ handleFileChange(filePath: string, options?: HandleFileChangeOptions): Promise<void>;
52
+ private getStrategiesByPriority;
53
+ private selectChangeStrategy;
54
+ private selectEntrypointEmitter;
55
+ protected createUnownedEntrypointError(entrypointPath: string): Error;
53
56
  /**
57
+ * Materializes one integration-owned page entrypoint during cold registration.
58
+ *
54
59
  * @remarks
55
- * Rolldown may emit the runtime bundle under a derived filename when the
56
- * entrypoint lives outside the app root. The dev server always serves
57
- * `/_hmr_runtime.js` from a stable path under `.eco/assets/_hmr/`.
60
+ * Registration uses {@link HmrStrategy.canEmitEntrypoint} / {@link HmrStrategy.emitEntrypoint},
61
+ * not {@link HmrStrategy.matches} / {@link HmrStrategy.process}. File-change handling stays
62
+ * on `handleFileChange()`.
58
63
  */
59
- private syncRuntimeOutput;
60
- broadcast(event: ClientBridgeEvent): void;
61
- handleFileChange(filePath: string, options?: HandleFileChangeOptions): Promise<void>;
64
+ emitIntegrationEntrypoint(entrypointPath: string, outputPath: string): Promise<void>;
65
+ /**
66
+ * Runs server invalidation and integration hooks before rebuilding a registered script entrypoint.
67
+ *
68
+ * @remarks
69
+ * Browser-only `*.script.ts` entrypoints skip server module invalidation and bypass-cache
70
+ * import; only integration change handlers and the downstream strategy rebuild run.
71
+ */
72
+ private prepareRegisteredScriptChange;
62
73
  getOutputUrl(entrypointPath: string): string | undefined;
63
74
  /**
64
75
  * Returns the emitted HMR script output when the entrypoint is already registered
@@ -68,20 +79,15 @@ export declare abstract class SharedHmrManager implements IHmrManager {
68
79
  * Disk artifacts alone are not enough: a fresh dev session must still register
69
80
  * the entrypoint so file watchers can rebuild it on change.
70
81
  */
71
- getResolvedScriptOutput(entrypointPath: string): {
72
- outputUrl: string;
73
- outputPath: string;
74
- } | undefined;
82
+ getResolvedScriptOutput(entrypointPath: string): ResolvedHmrEntrypoint | undefined;
75
83
  getWatchedFiles(): Map<string, string>;
76
84
  getDistDir(): string;
77
- getPlugins(): EcoBuildPlugin[];
78
85
  getDefaultContext(): DefaultHmrContext;
86
+ stop(): void;
79
87
  protected clearFailedEntrypointRegistration(entrypointPath: string): void;
80
88
  registerEntrypoint(entrypointPath: string): Promise<string>;
81
- registerScriptEntrypoint(entrypointPath: string): Promise<string>;
82
- protected emitStrictEntrypoint(entrypointPath: string): Promise<void>;
89
+ registerScriptEntrypoint(entrypointPath: string): Promise<ResolvedHmrEntrypoint>;
83
90
  protected emitScriptEntrypoint(entrypointPath: string, outputPath: string): Promise<void>;
84
- stop(): void;
85
91
  [Symbol.dispose](): void;
86
92
  }
87
93
  export {};