@zudojs/adapters 0.1.1 → 1.0.1

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 (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -13
  3. package/dist/adapter/adapter.registry.d.ts +104 -4
  4. package/dist/adapter/adapter.registry.js +185 -5
  5. package/dist/adapter/index.d.ts +1 -0
  6. package/dist/index.d.ts +1 -0
  7. package/package.json +27 -16
  8. package/dist/.tsbuildinfo +0 -1
  9. package/dist/adapter/adapter.registry.d.ts.map +0 -1
  10. package/dist/adapter/adapter.registry.js.map +0 -1
  11. package/dist/adapter/adapter.type.d.ts.map +0 -1
  12. package/dist/adapter/adapter.type.js.map +0 -1
  13. package/dist/adapter/index.d.ts.map +0 -1
  14. package/dist/adapter/index.js.map +0 -1
  15. package/dist/capabilities/capabilities.type.d.ts.map +0 -1
  16. package/dist/capabilities/capabilities.type.js.map +0 -1
  17. package/dist/capabilities/index.d.ts.map +0 -1
  18. package/dist/capabilities/index.js.map +0 -1
  19. package/dist/cli/cliAdapter.type.d.ts.map +0 -1
  20. package/dist/cli/cliAdapter.type.js.map +0 -1
  21. package/dist/cli/index.d.ts.map +0 -1
  22. package/dist/cli/index.js.map +0 -1
  23. package/dist/errors/index.d.ts.map +0 -1
  24. package/dist/errors/index.js.map +0 -1
  25. package/dist/http/httpAdapter.type.d.ts.map +0 -1
  26. package/dist/http/httpAdapter.type.js.map +0 -1
  27. package/dist/http/index.d.ts.map +0 -1
  28. package/dist/http/index.js.map +0 -1
  29. package/dist/index.d.ts.map +0 -1
  30. package/dist/index.js.map +0 -1
  31. package/dist/lifecycle/index.d.ts.map +0 -1
  32. package/dist/lifecycle/index.js.map +0 -1
  33. package/dist/lifecycle/lifecycle.type.d.ts.map +0 -1
  34. package/dist/lifecycle/lifecycle.type.js.map +0 -1
  35. package/dist/messaging/index.d.ts.map +0 -1
  36. package/dist/messaging/index.js.map +0 -1
  37. package/dist/messaging/messageAdapter.type.d.ts.map +0 -1
  38. package/dist/messaging/messageAdapter.type.js.map +0 -1
  39. package/dist/metadata/index.d.ts.map +0 -1
  40. package/dist/metadata/index.js.map +0 -1
  41. package/dist/metadata/metadata.type.d.ts.map +0 -1
  42. package/dist/metadata/metadata.type.js.map +0 -1
  43. package/dist/queue/index.d.ts.map +0 -1
  44. package/dist/queue/index.js.map +0 -1
  45. package/dist/queue/queueAdapter.type.d.ts.map +0 -1
  46. package/dist/queue/queueAdapter.type.js.map +0 -1
  47. package/dist/runtime/index.d.ts.map +0 -1
  48. package/dist/runtime/index.js.map +0 -1
  49. package/dist/runtime/runtimeAdapter.type.d.ts.map +0 -1
  50. package/dist/runtime/runtimeAdapter.type.js.map +0 -1
  51. package/dist/scheduler/index.d.ts.map +0 -1
  52. package/dist/scheduler/index.js.map +0 -1
  53. package/dist/scheduler/schedulerAdapter.type.d.ts.map +0 -1
  54. package/dist/scheduler/schedulerAdapter.type.js.map +0 -1
  55. package/dist/storage/index.d.ts.map +0 -1
  56. package/dist/storage/index.js.map +0 -1
  57. package/dist/storage/storageAdapter.type.d.ts.map +0 -1
  58. package/dist/storage/storageAdapter.type.js.map +0 -1
  59. package/dist/testing/adapterTesting.helper.d.ts.map +0 -1
  60. package/dist/testing/adapterTesting.helper.js.map +0 -1
  61. package/dist/testing/index.d.ts.map +0 -1
  62. package/dist/testing/index.js.map +0 -1
  63. package/dist/websocket/index.d.ts.map +0 -1
  64. package/dist/websocket/index.js.map +0 -1
  65. package/dist/websocket/websocketAdapter.type.d.ts.map +0 -1
  66. package/dist/websocket/websocketAdapter.type.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zudojs Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -11,26 +11,113 @@ npm install @zudojs/adapters
11
11
  ## Quick Start
12
12
 
13
13
  ```typescript
14
- import { createAdapterRegistry } from "@zudojs/adapters";
14
+ import { AdapterRegistry } from "@zudojs/adapters";
15
+ import type { Adapter } from "@zudojs/adapters";
15
16
 
16
- const registry = createAdapterRegistry();
17
+ const registry = new AdapterRegistry();
17
18
 
18
- registry.register({
19
+ const postgres: Adapter = {
19
20
  name: "postgres",
20
- capabilities: ["query", "transaction"],
21
- connect: async () => {
22
- /* ... */
21
+ version: "1.0.0",
22
+ // Capabilities are a declaration object, not a list of method names.
23
+ capabilities: { longRunning: true, gracefulShutdown: true },
24
+ initialize: async () => {
25
+ /* open the pool */
23
26
  },
24
- });
27
+ dispose: async () => {
28
+ /* close the pool */
29
+ },
30
+ };
31
+
32
+ registry.register(postgres);
33
+
34
+ registry.get("postgres"); // Adapter | undefined
35
+ registry.require("postgres"); // throws AdapterNotFoundError when absent
36
+ ```
37
+
38
+ Names are case-insensitive: they are trimmed and lowercased for registration
39
+ and lookup, and `getNames()` reports them in that form. A name that is blank
40
+ after trimming is rejected with `AdapterConfigurationError` — it could never
41
+ be looked up again. Registering the same name twice throws
42
+ `AdapterAlreadyRegisteredError`.
43
+
44
+ ## Lifecycle
45
+
46
+ An adapter may implement `initialize()`, `start()`, `stop()` and `dispose()`.
47
+ The registry drives them across everything it holds:
48
+
49
+ ```typescript
50
+ await registry.initializeAll(); // prepare resources
51
+ await registry.startAll(); // begin processing
52
+ await registry.stopAll(); // stop processing, stay registered
53
+ await registry.disposeAll(); // stop, dispose, and empty the registry
54
+ ```
55
+
56
+ Each of these attempts every adapter and then throws an `AggregateError`
57
+ carrying the failures, rather than stopping at the first one — a half-applied
58
+ transition leaves resources nobody is tracking.
59
+
60
+ `remove()` and `clear()` only drop references; `removeAndDispose()` and
61
+ `disposeAll()` also release resources.
62
+
63
+ ## Capabilities
64
+
65
+ Adapters declare what they support so runtime code can pick one that can do
66
+ the job:
67
+
68
+ ```typescript
69
+ registry.findByCapability("http"); // every adapter declaring http
70
+ registry.supports("postgres", "gracefulShutdown"); // boolean, never throws
71
+ registry.requireCapability("edge", "streaming"); // throws AdapterCapabilityMissingError
72
+ ```
73
+
74
+ Declared capabilities: `http`, `websocket`, `streaming`, `filesystem`, `tcp`,
75
+ `udp`, `backgroundTasks`, `longRunning`, `edgeRuntime`, `serverless`,
76
+ `gracefulShutdown`, `abortSignal`.
77
+
78
+ ## Health
79
+
80
+ ```typescript
81
+ import {
82
+ createHealthyHealth,
83
+ createDegradedHealth,
84
+ createUnhealthyHealth,
85
+ } from "@zudojs/adapters";
86
+
87
+ const health = createDegradedHealth("replica lag above threshold");
88
+ ```
89
+
90
+ A `LifecycleAdapter` adds optional `configure(options)` and `health()` to the
91
+ base contract.
92
+
93
+ ## Transport contracts
94
+
95
+ Type-only interfaces that extend `Adapter` for each transport:
96
+ `HTTPAdapter`, `HTTPServerAdapter`, `MessageAdapter`, `StorageAdapter`,
97
+ `QueueAdapter`, `RuntimeAdapter`, `WebSocketAdapter`, `CLIAdapter`,
98
+ `SchedulerAdapter`.
99
+
100
+ ## Testing utilities
101
+
102
+ ```typescript
103
+ import {
104
+ createMockAdapter,
105
+ createMockAdapterRegistry,
106
+ createMockHealth,
107
+ } from "@zudojs/adapters";
108
+
109
+ const { registry } = createMockAdapterRegistry([
110
+ createMockAdapter({ name: "fake-http", capabilities: { http: true } }),
111
+ ]);
25
112
  ```
26
113
 
27
- ## Features
114
+ ## Errors
28
115
 
29
- - Adapter contracts and interfaces
30
- - Adapter registry for discovery
31
- - Capability-based selection
32
- - Transport abstractions
33
- - Health check integration
116
+ Re-exported from `@zudojs/errors`: `AdapterError` · `AdapterNotFoundError` ·
117
+ `AdapterAlreadyRegisteredError` · `AdapterNotSupportedError` ·
118
+ `AdapterCapabilityMissingError` · `AdapterConnectionError` ·
119
+ `AdapterOperationError` · `AdapterTimeoutError` · `AdapterDisposeError` ·
120
+ `AdapterInitializationError` · `AdapterConfigurationError`.
34
121
 
35
122
  ## Use Cases
36
123
 
@@ -1,9 +1,20 @@
1
1
  /**
2
2
  * @zudojs/adapters/adapter
3
3
  *
4
- * Adapter registry — manages adapter registration, lookup, and lifecycle.
4
+ * Adapter registry — manages adapter registration and lookup.
5
+ *
6
+ * Adapter names are case-insensitive: they are trimmed and lowercased on
7
+ * registration and lookup, and `getNames()` reports them in that normalized
8
+ * form. The adapter object itself is stored untouched, so `getAll()` still
9
+ * reports whatever `adapter.name` it was built with.
10
+ *
11
+ * Note that `remove()` and `clear()` only drop references — use
12
+ * `removeAndDispose()` / `disposeAll()` to also release adapter resources.
5
13
  */
6
14
  import type { Adapter } from "./adapter.type.js";
15
+ import type { AdapterCapabilities } from "../capabilities/capabilities.type.js";
16
+ /** A capability an adapter can declare. */
17
+ export type AdapterCapabilityName = keyof AdapterCapabilities;
7
18
  /**
8
19
  * Registry for Zudojs adapters.
9
20
  *
@@ -14,6 +25,9 @@ export declare class AdapterRegistry {
14
25
  /**
15
26
  * Registers an adapter.
16
27
  *
28
+ * @throws {AdapterConfigurationError} If the adapter name is blank. A name
29
+ * that normalizes to the empty string is unaddressable — `get("")` is the
30
+ * only way back to it, and every other blank name collides with it.
17
31
  * @throws {AdapterAlreadyRegisteredError} If an adapter with the same name is already registered.
18
32
  */
19
33
  register(adapter: Adapter): void;
@@ -21,32 +35,118 @@ export declare class AdapterRegistry {
21
35
  * Returns an adapter by name.
22
36
  */
23
37
  get<T extends Adapter>(name: string): T | undefined;
38
+ /**
39
+ * Returns an adapter by name or throws.
40
+ *
41
+ * @throws {AdapterNotFoundError} If no adapter with the name is registered.
42
+ */
43
+ require<T extends Adapter>(name: string): T;
24
44
  /**
25
45
  * Returns whether an adapter is registered.
26
46
  */
27
47
  has(name: string): boolean;
28
48
  /**
29
- * Removes an adapter by name.
49
+ * Removes an adapter by name without disposing it.
50
+ *
51
+ * The caller keeps responsibility for the adapter's resources —
52
+ * use {@link removeAndDispose} to also stop and dispose it.
30
53
  *
31
54
  * @returns True if the adapter was removed, false if it was not registered.
32
55
  */
33
56
  remove(name: string): boolean;
57
+ /**
58
+ * Removes an adapter by name and releases its resources
59
+ * (calls `stop()` then `dispose()` when defined).
60
+ *
61
+ * @returns True if the adapter was removed, false if it was not registered.
62
+ */
63
+ removeAndDispose(name: string): Promise<boolean>;
34
64
  /**
35
65
  * Returns all registered adapters.
36
66
  */
37
67
  getAll(): readonly Adapter[];
38
68
  /**
39
- * Returns all registered adapter names.
69
+ * Returns all registered adapter names (normalized: trimmed, lowercase).
40
70
  */
41
71
  getNames(): readonly string[];
72
+ /**
73
+ * Returns every adapter that declares the given capability.
74
+ *
75
+ * Capabilities exist so runtime code can pick an adapter that can actually
76
+ * do the job; without a way to ask, every declaration was inert.
77
+ */
78
+ findByCapability(capability: AdapterCapabilityName): readonly Adapter[];
79
+ /**
80
+ * Returns whether a registered adapter declares a capability.
81
+ *
82
+ * A missing adapter reports `false` rather than throwing — use
83
+ * {@link requireCapability} when the absence should stop the caller.
84
+ */
85
+ supports(name: string, capability: AdapterCapabilityName): boolean;
86
+ /**
87
+ * Returns an adapter that declares the given capability, or throws.
88
+ *
89
+ * @throws {AdapterNotFoundError} If no adapter with the name is registered.
90
+ * @throws {AdapterCapabilityMissingError} If the adapter does not declare
91
+ * the capability.
92
+ */
93
+ requireCapability<T extends Adapter>(name: string, capability: AdapterCapabilityName): T;
94
+ /**
95
+ * Initializes every registered adapter, in registration order.
96
+ *
97
+ * The counterpart to {@link disposeAll}: `initialize()` and `start()` were
98
+ * part of the adapter contract with nothing in the package that ever called
99
+ * them, so an adapter could only be torn down, never brought up.
100
+ *
101
+ * @throws {AggregateError} After attempting all adapters, if any failed.
102
+ */
103
+ initializeAll(): Promise<void>;
104
+ /**
105
+ * Starts every registered adapter, in registration order.
106
+ *
107
+ * @throws {AggregateError} After attempting all adapters, if any failed.
108
+ */
109
+ startAll(): Promise<void>;
110
+ /**
111
+ * Stops every registered adapter without disposing or unregistering them.
112
+ *
113
+ * @throws {AggregateError} After attempting all adapters, if any failed.
114
+ */
115
+ stopAll(): Promise<void>;
42
116
  /**
43
117
  * Returns the number of registered adapters.
44
118
  */
45
119
  get size(): number;
46
120
  /**
47
- * Clears all registered adapters.
121
+ * Clears all registered adapters without disposing them.
122
+ *
123
+ * Use {@link disposeAll} to also release adapter resources.
48
124
  */
49
125
  clear(): void;
126
+ /**
127
+ * Clears the registry and releases every adapter's resources
128
+ * (calls `stop()` then `dispose()` when defined, best-effort).
129
+ *
130
+ * @throws {AggregateError} After attempting all adapters, if any failed.
131
+ */
132
+ disposeAll(): Promise<void>;
133
+ /**
134
+ * Stops, then disposes, one adapter.
135
+ *
136
+ * `dispose()` runs even when `stop()` throws: the adapter has already
137
+ * left the registry by the time this is called, so skipping disposal
138
+ * would orphan its connections and timers with nothing left holding a
139
+ * reference to release them. A `stop()` failure is still reported — on
140
+ * its own when disposal succeeds, alongside the disposal error when
141
+ * both fail.
142
+ */
143
+ private teardown;
144
+ /**
145
+ * Runs an operation against every adapter, collecting failures rather than
146
+ * stopping at the first one — a half-applied lifecycle transition leaves
147
+ * resources in a state nobody tracked.
148
+ */
149
+ private forEachAdapter;
50
150
  private normalizeName;
51
151
  }
52
152
  //# sourceMappingURL=adapter.registry.d.ts.map
@@ -1,9 +1,17 @@
1
1
  /**
2
2
  * @zudojs/adapters/adapter
3
3
  *
4
- * Adapter registry — manages adapter registration, lookup, and lifecycle.
4
+ * Adapter registry — manages adapter registration and lookup.
5
+ *
6
+ * Adapter names are case-insensitive: they are trimmed and lowercased on
7
+ * registration and lookup, and `getNames()` reports them in that normalized
8
+ * form. The adapter object itself is stored untouched, so `getAll()` still
9
+ * reports whatever `adapter.name` it was built with.
10
+ *
11
+ * Note that `remove()` and `clear()` only drop references — use
12
+ * `removeAndDispose()` / `disposeAll()` to also release adapter resources.
5
13
  */
6
- import { AdapterAlreadyRegisteredError, AdapterNotFoundError, } from "@zudojs/errors";
14
+ import { AdapterAlreadyRegisteredError, AdapterCapabilityMissingError, AdapterConfigurationError, AdapterNotFoundError, } from "@zudojs/errors";
7
15
  /**
8
16
  * Registry for Zudojs adapters.
9
17
  *
@@ -14,10 +22,16 @@ export class AdapterRegistry {
14
22
  /**
15
23
  * Registers an adapter.
16
24
  *
25
+ * @throws {AdapterConfigurationError} If the adapter name is blank. A name
26
+ * that normalizes to the empty string is unaddressable — `get("")` is the
27
+ * only way back to it, and every other blank name collides with it.
17
28
  * @throws {AdapterAlreadyRegisteredError} If an adapter with the same name is already registered.
18
29
  */
19
30
  register(adapter) {
20
31
  const name = this.normalizeName(adapter.name);
32
+ if (name === "") {
33
+ throw new AdapterConfigurationError(String(adapter.name), new Error("Adapter name cannot be blank."));
34
+ }
21
35
  if (this.adapters.has(name)) {
22
36
  throw new AdapterAlreadyRegisteredError(name);
23
37
  }
@@ -29,6 +43,18 @@ export class AdapterRegistry {
29
43
  get(name) {
30
44
  return this.adapters.get(this.normalizeName(name));
31
45
  }
46
+ /**
47
+ * Returns an adapter by name or throws.
48
+ *
49
+ * @throws {AdapterNotFoundError} If no adapter with the name is registered.
50
+ */
51
+ require(name) {
52
+ const adapter = this.get(name);
53
+ if (adapter === undefined) {
54
+ throw new AdapterNotFoundError(this.normalizeName(name));
55
+ }
56
+ return adapter;
57
+ }
32
58
  /**
33
59
  * Returns whether an adapter is registered.
34
60
  */
@@ -36,13 +62,32 @@ export class AdapterRegistry {
36
62
  return this.adapters.has(this.normalizeName(name));
37
63
  }
38
64
  /**
39
- * Removes an adapter by name.
65
+ * Removes an adapter by name without disposing it.
66
+ *
67
+ * The caller keeps responsibility for the adapter's resources —
68
+ * use {@link removeAndDispose} to also stop and dispose it.
40
69
  *
41
70
  * @returns True if the adapter was removed, false if it was not registered.
42
71
  */
43
72
  remove(name) {
44
73
  return this.adapters.delete(this.normalizeName(name));
45
74
  }
75
+ /**
76
+ * Removes an adapter by name and releases its resources
77
+ * (calls `stop()` then `dispose()` when defined).
78
+ *
79
+ * @returns True if the adapter was removed, false if it was not registered.
80
+ */
81
+ async removeAndDispose(name) {
82
+ const key = this.normalizeName(name);
83
+ const adapter = this.adapters.get(key);
84
+ if (adapter === undefined) {
85
+ return false;
86
+ }
87
+ this.adapters.delete(key);
88
+ await this.teardown(adapter);
89
+ return true;
90
+ }
46
91
  /**
47
92
  * Returns all registered adapters.
48
93
  */
@@ -50,11 +95,71 @@ export class AdapterRegistry {
50
95
  return Object.freeze([...this.adapters.values()]);
51
96
  }
52
97
  /**
53
- * Returns all registered adapter names.
98
+ * Returns all registered adapter names (normalized: trimmed, lowercase).
54
99
  */
55
100
  getNames() {
56
101
  return Object.freeze([...this.adapters.keys()]);
57
102
  }
103
+ /**
104
+ * Returns every adapter that declares the given capability.
105
+ *
106
+ * Capabilities exist so runtime code can pick an adapter that can actually
107
+ * do the job; without a way to ask, every declaration was inert.
108
+ */
109
+ findByCapability(capability) {
110
+ return Object.freeze([...this.adapters.values()].filter((adapter) => adapter.capabilities[capability] === true));
111
+ }
112
+ /**
113
+ * Returns whether a registered adapter declares a capability.
114
+ *
115
+ * A missing adapter reports `false` rather than throwing — use
116
+ * {@link requireCapability} when the absence should stop the caller.
117
+ */
118
+ supports(name, capability) {
119
+ return this.get(name)?.capabilities[capability] === true;
120
+ }
121
+ /**
122
+ * Returns an adapter that declares the given capability, or throws.
123
+ *
124
+ * @throws {AdapterNotFoundError} If no adapter with the name is registered.
125
+ * @throws {AdapterCapabilityMissingError} If the adapter does not declare
126
+ * the capability.
127
+ */
128
+ requireCapability(name, capability) {
129
+ const adapter = this.require(name);
130
+ if (adapter.capabilities[capability] !== true) {
131
+ throw new AdapterCapabilityMissingError(this.normalizeName(name), capability);
132
+ }
133
+ return adapter;
134
+ }
135
+ /**
136
+ * Initializes every registered adapter, in registration order.
137
+ *
138
+ * The counterpart to {@link disposeAll}: `initialize()` and `start()` were
139
+ * part of the adapter contract with nothing in the package that ever called
140
+ * them, so an adapter could only be torn down, never brought up.
141
+ *
142
+ * @throws {AggregateError} After attempting all adapters, if any failed.
143
+ */
144
+ async initializeAll() {
145
+ await this.forEachAdapter((adapter) => adapter.initialize?.(), "One or more adapters failed to initialize.");
146
+ }
147
+ /**
148
+ * Starts every registered adapter, in registration order.
149
+ *
150
+ * @throws {AggregateError} After attempting all adapters, if any failed.
151
+ */
152
+ async startAll() {
153
+ await this.forEachAdapter((adapter) => adapter.start?.(), "One or more adapters failed to start.");
154
+ }
155
+ /**
156
+ * Stops every registered adapter without disposing or unregistering them.
157
+ *
158
+ * @throws {AggregateError} After attempting all adapters, if any failed.
159
+ */
160
+ async stopAll() {
161
+ await this.forEachAdapter((adapter) => adapter.stop?.(), "One or more adapters failed to stop.");
162
+ }
58
163
  /**
59
164
  * Returns the number of registered adapters.
60
165
  */
@@ -62,11 +167,86 @@ export class AdapterRegistry {
62
167
  return this.adapters.size;
63
168
  }
64
169
  /**
65
- * Clears all registered adapters.
170
+ * Clears all registered adapters without disposing them.
171
+ *
172
+ * Use {@link disposeAll} to also release adapter resources.
66
173
  */
67
174
  clear() {
68
175
  this.adapters.clear();
69
176
  }
177
+ /**
178
+ * Clears the registry and releases every adapter's resources
179
+ * (calls `stop()` then `dispose()` when defined, best-effort).
180
+ *
181
+ * @throws {AggregateError} After attempting all adapters, if any failed.
182
+ */
183
+ async disposeAll() {
184
+ const adapters = [...this.adapters.values()];
185
+ this.adapters.clear();
186
+ const failures = [];
187
+ for (const adapter of adapters) {
188
+ try {
189
+ await this.teardown(adapter);
190
+ }
191
+ catch (error) {
192
+ failures.push(error);
193
+ }
194
+ }
195
+ if (failures.length > 0) {
196
+ throw new AggregateError(failures, "One or more adapters failed to dispose.");
197
+ }
198
+ }
199
+ /**
200
+ * Stops, then disposes, one adapter.
201
+ *
202
+ * `dispose()` runs even when `stop()` throws: the adapter has already
203
+ * left the registry by the time this is called, so skipping disposal
204
+ * would orphan its connections and timers with nothing left holding a
205
+ * reference to release them. A `stop()` failure is still reported — on
206
+ * its own when disposal succeeds, alongside the disposal error when
207
+ * both fail.
208
+ */
209
+ async teardown(adapter) {
210
+ let stopError;
211
+ let stopFailed = false;
212
+ try {
213
+ await adapter.stop?.();
214
+ }
215
+ catch (error) {
216
+ stopFailed = true;
217
+ stopError = error;
218
+ }
219
+ try {
220
+ await adapter.dispose?.();
221
+ }
222
+ catch (error) {
223
+ if (stopFailed) {
224
+ throw new AggregateError([stopError, error], `Adapter "${adapter.name}" failed to stop and to dispose.`);
225
+ }
226
+ throw error;
227
+ }
228
+ if (stopFailed)
229
+ throw stopError;
230
+ }
231
+ /**
232
+ * Runs an operation against every adapter, collecting failures rather than
233
+ * stopping at the first one — a half-applied lifecycle transition leaves
234
+ * resources in a state nobody tracked.
235
+ */
236
+ async forEachAdapter(operation, message) {
237
+ const failures = [];
238
+ for (const adapter of [...this.adapters.values()]) {
239
+ try {
240
+ await operation(adapter);
241
+ }
242
+ catch (error) {
243
+ failures.push(error);
244
+ }
245
+ }
246
+ if (failures.length > 0) {
247
+ throw new AggregateError(failures, message);
248
+ }
249
+ }
70
250
  normalizeName(name) {
71
251
  return name.trim().toLowerCase();
72
252
  }
@@ -5,4 +5,5 @@
5
5
  */
6
6
  export type { Adapter } from "./adapter.type.js";
7
7
  export { AdapterRegistry } from "./adapter.registry.js";
8
+ export type { AdapterCapabilityName } from "./adapter.registry.js";
8
9
  //# sourceMappingURL=index.d.ts.map
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@
17
17
  */
18
18
  export type { Adapter } from "./adapter/index.js";
19
19
  export { AdapterRegistry } from "./adapter/index.js";
20
+ export type { AdapterCapabilityName } from "./adapter/index.js";
20
21
  export type { AdapterCapabilities } from "./capabilities/index.js";
21
22
  export type { AdapterMetadata } from "./metadata/index.js";
22
23
  export type { AdapterHealthStatus, AdapterHealth, AdapterOperationOptions, LifecycleAdapter, } from "./lifecycle/index.js";
package/package.json CHANGED
@@ -1,8 +1,12 @@
1
1
  {
2
2
  "name": "@zudojs/adapters",
3
- "version": "0.1.1",
3
+ "version": "1.0.1",
4
4
  "description": "Boundary layer between Zudojs and external platforms — adapter contracts, registry, capabilities, and transport abstractions.",
5
5
  "license": "MIT",
6
+ "author": {
7
+ "name": "Oluwayemi Oyinlola",
8
+ "url": "https://github.com/oyinlola-tech"
9
+ },
6
10
  "type": "module",
7
11
  "main": "./dist/index.js",
8
12
  "module": "./dist/index.js",
@@ -14,26 +18,22 @@
14
18
  }
15
19
  },
16
20
  "files": [
17
- "dist"
21
+ "dist",
22
+ "!dist/**/*.map",
23
+ "!dist/**/*.tsbuildinfo",
24
+ "!dist/.tsbuildinfo"
18
25
  ],
19
- "scripts": {
20
- "build": "tsc -p tsconfig.json",
21
- "typecheck": "tsc -p tsconfig.json --noEmit",
22
- "clean": "rm -rf dist",
23
- "test": "vitest run",
24
- "test:watch": "vitest"
25
- },
26
26
  "engines": {
27
27
  "node": ">=24.0.0"
28
28
  },
29
29
  "dependencies": {
30
- "@zudojs/errors": "0.1.0",
31
- "@zudojs/constants": "0.1.0",
32
- "@zudojs/types": "0.1.0",
33
- "@zudojs/lifecycle": "0.1.0"
30
+ "@zudojs/errors": "1.0.1",
31
+ "@zudojs/constants": "1.0.1",
32
+ "@zudojs/types": "1.0.0",
33
+ "@zudojs/lifecycle": "1.1.0"
34
34
  },
35
35
  "devDependencies": {
36
- "typescript": "^7.0.2",
36
+ "typescript": "7.0.2",
37
37
  "vitest": "^4.1.11"
38
38
  },
39
39
  "publishConfig": {
@@ -47,8 +47,19 @@
47
47
  "integration"
48
48
  ],
49
49
  "homepage": "https://github.com/oyinlola-tech/zudo#readme",
50
+ "bugs": {
51
+ "url": "https://github.com/oyinlola-tech/zudo/issues"
52
+ },
50
53
  "repository": {
51
54
  "type": "git",
52
- "url": "https://github.com/oyinlola-tech/zudo"
55
+ "url": "https://github.com/oyinlola-tech/zudo",
56
+ "directory": "packages/adapters"
57
+ },
58
+ "scripts": {
59
+ "build": "tsc -p tsconfig.json",
60
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
61
+ "clean": "rm -rf dist",
62
+ "test": "vitest run",
63
+ "test:watch": "vitest"
53
64
  }
54
- }
65
+ }