@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.
- package/LICENSE +21 -0
- package/README.md +100 -13
- package/dist/adapter/adapter.registry.d.ts +104 -4
- package/dist/adapter/adapter.registry.js +185 -5
- package/dist/adapter/index.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/package.json +27 -16
- package/dist/.tsbuildinfo +0 -1
- package/dist/adapter/adapter.registry.d.ts.map +0 -1
- package/dist/adapter/adapter.registry.js.map +0 -1
- package/dist/adapter/adapter.type.d.ts.map +0 -1
- package/dist/adapter/adapter.type.js.map +0 -1
- package/dist/adapter/index.d.ts.map +0 -1
- package/dist/adapter/index.js.map +0 -1
- package/dist/capabilities/capabilities.type.d.ts.map +0 -1
- package/dist/capabilities/capabilities.type.js.map +0 -1
- package/dist/capabilities/index.d.ts.map +0 -1
- package/dist/capabilities/index.js.map +0 -1
- package/dist/cli/cliAdapter.type.d.ts.map +0 -1
- package/dist/cli/cliAdapter.type.js.map +0 -1
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js.map +0 -1
- package/dist/http/httpAdapter.type.d.ts.map +0 -1
- package/dist/http/httpAdapter.type.js.map +0 -1
- package/dist/http/index.d.ts.map +0 -1
- package/dist/http/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lifecycle/index.d.ts.map +0 -1
- package/dist/lifecycle/index.js.map +0 -1
- package/dist/lifecycle/lifecycle.type.d.ts.map +0 -1
- package/dist/lifecycle/lifecycle.type.js.map +0 -1
- package/dist/messaging/index.d.ts.map +0 -1
- package/dist/messaging/index.js.map +0 -1
- package/dist/messaging/messageAdapter.type.d.ts.map +0 -1
- package/dist/messaging/messageAdapter.type.js.map +0 -1
- package/dist/metadata/index.d.ts.map +0 -1
- package/dist/metadata/index.js.map +0 -1
- package/dist/metadata/metadata.type.d.ts.map +0 -1
- package/dist/metadata/metadata.type.js.map +0 -1
- package/dist/queue/index.d.ts.map +0 -1
- package/dist/queue/index.js.map +0 -1
- package/dist/queue/queueAdapter.type.d.ts.map +0 -1
- package/dist/queue/queueAdapter.type.js.map +0 -1
- package/dist/runtime/index.d.ts.map +0 -1
- package/dist/runtime/index.js.map +0 -1
- package/dist/runtime/runtimeAdapter.type.d.ts.map +0 -1
- package/dist/runtime/runtimeAdapter.type.js.map +0 -1
- package/dist/scheduler/index.d.ts.map +0 -1
- package/dist/scheduler/index.js.map +0 -1
- package/dist/scheduler/schedulerAdapter.type.d.ts.map +0 -1
- package/dist/scheduler/schedulerAdapter.type.js.map +0 -1
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/storageAdapter.type.d.ts.map +0 -1
- package/dist/storage/storageAdapter.type.js.map +0 -1
- package/dist/testing/adapterTesting.helper.d.ts.map +0 -1
- package/dist/testing/adapterTesting.helper.js.map +0 -1
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing/index.js.map +0 -1
- package/dist/websocket/index.d.ts.map +0 -1
- package/dist/websocket/index.js.map +0 -1
- package/dist/websocket/websocketAdapter.type.d.ts.map +0 -1
- 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 {
|
|
14
|
+
import { AdapterRegistry } from "@zudojs/adapters";
|
|
15
|
+
import type { Adapter } from "@zudojs/adapters";
|
|
15
16
|
|
|
16
|
-
const registry =
|
|
17
|
+
const registry = new AdapterRegistry();
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
const postgres: Adapter = {
|
|
19
20
|
name: "postgres",
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
##
|
|
114
|
+
## Errors
|
|
28
115
|
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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
|
|
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
|
}
|
package/dist/adapter/index.d.ts
CHANGED
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
|
|
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
|
|
31
|
-
"@zudojs/constants": "0.1
|
|
32
|
-
"@zudojs/types": "
|
|
33
|
-
"@zudojs/lifecycle": "
|
|
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": "
|
|
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
|
+
}
|