@lmzhen/dsh-evolution-io 0.3.62 → 0.3.64

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/lib/index.js CHANGED
@@ -8,6 +8,9 @@ var EvolutionIoRegistry = class extends Service {
8
8
  providers = /* @__PURE__ */ new Map();
9
9
  /** Name of the provider that declared itself the default (P2-7, v14). */
10
10
  defaultName;
11
+ /** P2-3 (v15): per-name dispose — the idempotent re-registration returns
12
+ * the SAME function instance, so double-dispose is a single removal. */
13
+ disposals = /* @__PURE__ */ new Map();
11
14
  constructor(ctx) {
12
15
  super(ctx, "evolutionIo");
13
16
  }
@@ -15,26 +18,39 @@ var EvolutionIoRegistry = class extends Service {
15
18
  * @param options.default - mark this provider as the one a nameless
16
19
  * `provider()` resolves. The first explicit default wins; without any
17
20
  * default the registry keeps its historical registration-order fallback.
21
+ *
22
+ * P2-3 (v15): re-registering the IDENTICAL provider object is idempotent —
23
+ * it returns the original dispose and does not throw. This is what lets a
24
+ * backend (io-node) re-apply (HMR / re-mounted row) without the silent
25
+ * `hasProvider` early-return the v14 fix used, which had made the
26
+ * fail-loud-below unreachable for a FOREIGN provider under the same name.
27
+ * A different object under a registered name still throws.
18
28
  */
19
29
  registerProvider(provider, options = {}) {
20
- if (this.providers.has(provider.name)) throw new Error(`evolution IO provider "${provider.name}" already registered`);
21
- this.providers.set(provider.name, provider);
22
- if (options.default === true) this.defaultName ??= provider.name;
23
- return () => {
24
- if (this.providers.get(provider.name) === provider) {
25
- this.providers.delete(provider.name);
26
- if (this.defaultName === provider.name) this.defaultName = void 0;
27
- }
30
+ const idempotent = this.providers.get(provider.name) === provider;
31
+ if (!idempotent && this.providers.has(provider.name)) throw new Error(`evolution IO provider "${provider.name}" already registered`);
32
+ if (options.default === true) {
33
+ if (this.defaultName !== void 0 && this.defaultName !== provider.name) this.ctx.logger.warn(`evolution IO provider "${provider.name}" declared itself the default, but "${this.defaultName}" already is — the first declaration wins`);
34
+ this.defaultName ??= provider.name;
35
+ }
36
+ if (idempotent) {
37
+ const existing = this.disposals.get(provider.name);
38
+ if (existing !== void 0) return existing;
39
+ }
40
+ const dispose = () => {
41
+ if (this.disposals.get(provider.name) !== dispose) return;
42
+ this.providers.delete(provider.name);
43
+ this.disposals.delete(provider.name);
44
+ if (this.defaultName === provider.name) this.defaultName = void 0;
28
45
  };
46
+ this.providers.set(provider.name, provider);
47
+ this.disposals.set(provider.name, dispose);
48
+ return dispose;
29
49
  }
30
50
  /** Whether a provider with this exact name is mounted. */
31
51
  hasProvider(name) {
32
52
  return this.providers.has(name);
33
53
  }
34
- /** Whether ANY provider is mounted (used by lazy/boot-time probes). */
35
- hasProviders() {
36
- return this.providers.size > 0;
37
- }
38
54
  provider(name) {
39
55
  if (name) {
40
56
  const provider = this.providers.get(name);
@@ -42,7 +58,7 @@ var EvolutionIoRegistry = class extends Service {
42
58
  throw new Error(`evolution IO provider "${name}" is not registered`);
43
59
  }
44
60
  const first = (this.defaultName === void 0 ? void 0 : this.providers.get(this.defaultName)) ?? this.providers.values().next().value;
45
- if (!first) throw new Error("no evolution IO provider registered");
61
+ if (!first) throw new Error("no evolution IO provider registered — mount evolution-io-node (@lmzhen/dsh-evolution-io-node published, @lmzhen/dsh-evolution-io-node in the dev overlay; ships with evolution-host/evolution-all) and retry");
46
62
  return first;
47
63
  }
48
64
  };
@@ -30,19 +30,27 @@ export declare class EvolutionIoRegistry extends Service {
30
30
  private readonly providers;
31
31
  /** Name of the provider that declared itself the default (P2-7, v14). */
32
32
  private defaultName;
33
+ /** P2-3 (v15): per-name dispose — the idempotent re-registration returns
34
+ * the SAME function instance, so double-dispose is a single removal. */
35
+ private readonly disposals;
33
36
  constructor(ctx: Context);
34
37
  /**
35
38
  * @param options.default - mark this provider as the one a nameless
36
39
  * `provider()` resolves. The first explicit default wins; without any
37
40
  * default the registry keeps its historical registration-order fallback.
41
+ *
42
+ * P2-3 (v15): re-registering the IDENTICAL provider object is idempotent —
43
+ * it returns the original dispose and does not throw. This is what lets a
44
+ * backend (io-node) re-apply (HMR / re-mounted row) without the silent
45
+ * `hasProvider` early-return the v14 fix used, which had made the
46
+ * fail-loud-below unreachable for a FOREIGN provider under the same name.
47
+ * A different object under a registered name still throws.
38
48
  */
39
49
  registerProvider(provider: EvolutionIo, options?: {
40
50
  default?: boolean;
41
51
  }): () => void;
42
52
  /** Whether a provider with this exact name is mounted. */
43
53
  hasProvider(name: string): boolean;
44
- /** Whether ANY provider is mounted (used by lazy/boot-time probes). */
45
- hasProviders(): boolean;
46
54
  provider(name?: string): EvolutionIo;
47
55
  }
48
56
  export default EvolutionIoRegistry;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-io",
3
3
  "description": "IO seam for evolution providers (community build)",
4
- "version": "0.3.62",
4
+ "version": "0.3.64",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,7 +30,7 @@
30
30
  ],
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@lmzhen/dsh-evolution-core": "^0.3.62"
33
+ "@lmzhen/dsh-evolution-core": "^0.3.64"
34
34
  },
35
35
  "peerDependencies": {
36
36
  "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",