@zudojs/api 0.1.2 → 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 +137 -9
- package/dist/api/constants.d.ts +31 -3
- package/dist/api/constants.js +31 -3
- package/dist/api/context/context.type.d.ts +38 -0
- package/dist/api/context/context.type.js +86 -6
- package/dist/api/context/contextKey.type.d.ts +13 -0
- package/dist/api/context/contextKey.type.js +6 -0
- package/dist/api/errors/index.d.ts +1 -1
- package/dist/api/errors/index.js +1 -1
- package/dist/api/executor/executor.core.d.ts +68 -7
- package/dist/api/executor/executor.core.js +184 -54
- package/dist/api/executor/index.d.ts +1 -0
- package/dist/api/interceptors/interceptor.type.d.ts +16 -4
- package/dist/api/operation/operation.type.d.ts +85 -0
- package/dist/api/operation/operation.type.js +115 -3
- package/dist/api/registry/operationRegistry.core.d.ts +15 -2
- package/dist/api/registry/operationRegistry.core.js +32 -4
- package/dist/index.d.ts +21 -12
- package/dist/index.js +19 -12
- package/package.json +28 -17
- package/dist/.tsbuildinfo +0 -1
- package/dist/api/constants.d.ts.map +0 -1
- package/dist/api/constants.js.map +0 -1
- package/dist/api/context/context.type.d.ts.map +0 -1
- package/dist/api/context/context.type.js.map +0 -1
- package/dist/api/context/contextKey.type.d.ts.map +0 -1
- package/dist/api/context/contextKey.type.js.map +0 -1
- package/dist/api/errors/index.d.ts.map +0 -1
- package/dist/api/errors/index.js.map +0 -1
- package/dist/api/executor/executor.core.d.ts.map +0 -1
- package/dist/api/executor/executor.core.js.map +0 -1
- package/dist/api/executor/index.d.ts.map +0 -1
- package/dist/api/executor/index.js.map +0 -1
- package/dist/api/handler/handler.type.d.ts.map +0 -1
- package/dist/api/handler/handler.type.js.map +0 -1
- package/dist/api/interceptors/interceptor.type.d.ts.map +0 -1
- package/dist/api/interceptors/interceptor.type.js.map +0 -1
- package/dist/api/operation/operation.type.d.ts.map +0 -1
- package/dist/api/operation/operation.type.js.map +0 -1
- package/dist/api/registry/index.d.ts.map +0 -1
- package/dist/api/registry/index.js.map +0 -1
- package/dist/api/registry/operationRegistry.core.d.ts.map +0 -1
- package/dist/api/registry/operationRegistry.core.js.map +0 -1
- package/dist/api/result/apiResult.type.d.ts.map +0 -1
- package/dist/api/result/apiResult.type.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { assertValidOperationShape, freezeOperationMetadata, } from "../operation/operation.type.js";
|
|
2
|
+
import { APIDuplicateOperationError, APIOperationNotFoundError, createAPIError, } from "../errors/index.js";
|
|
1
3
|
/**
|
|
2
4
|
* Registry for API operations.
|
|
3
5
|
*
|
|
4
6
|
* Enforces uniqueness and provides O(1) lookup by operation name.
|
|
7
|
+
*
|
|
8
|
+
* Every failure leaving this class is an `APIError`, so a transport can
|
|
9
|
+
* map it by `statusCode` / `code` without special-casing the registry.
|
|
5
10
|
*/
|
|
6
11
|
export class APIOperationRegistry {
|
|
7
12
|
operations = new Map();
|
|
@@ -9,16 +14,24 @@ export class APIOperationRegistry {
|
|
|
9
14
|
/**
|
|
10
15
|
* Registers an operation.
|
|
11
16
|
*
|
|
17
|
+
* The operation and its metadata are frozen on registration, so a
|
|
18
|
+
* registered operation cannot be rewritten through `metadata.tags` or
|
|
19
|
+
* `metadata.timeout` after the fact.
|
|
20
|
+
*
|
|
12
21
|
* @throws {APIDuplicateOperationError} if an operation with the same name is already registered.
|
|
22
|
+
* @throws {APIError} if the registry is frozen.
|
|
23
|
+
* @throws {TypeError | RangeError} if the operation's name or handler is invalid.
|
|
13
24
|
*/
|
|
14
25
|
register(operation) {
|
|
15
26
|
if (this.frozen) {
|
|
16
|
-
throw
|
|
27
|
+
throw frozenRegistryError("register");
|
|
17
28
|
}
|
|
29
|
+
assertValidOperationShape(operation);
|
|
18
30
|
const existing = this.operations.get(operation.name);
|
|
19
31
|
if (existing !== undefined) {
|
|
20
|
-
throw new
|
|
32
|
+
throw new APIDuplicateOperationError(operation.name);
|
|
21
33
|
}
|
|
34
|
+
freezeOperationMetadata(operation.metadata);
|
|
22
35
|
this.operations.set(operation.name, Object.freeze(operation));
|
|
23
36
|
}
|
|
24
37
|
/**
|
|
@@ -35,11 +48,13 @@ export class APIOperationRegistry {
|
|
|
35
48
|
}
|
|
36
49
|
/**
|
|
37
50
|
* Retrieves an operation by name or throws.
|
|
51
|
+
*
|
|
52
|
+
* @throws {APIOperationNotFoundError} (404) if no operation is registered under `name`.
|
|
38
53
|
*/
|
|
39
54
|
require(name) {
|
|
40
55
|
const operation = this.get(name);
|
|
41
56
|
if (operation === undefined) {
|
|
42
|
-
throw new
|
|
57
|
+
throw new APIOperationNotFoundError(name);
|
|
43
58
|
}
|
|
44
59
|
return operation;
|
|
45
60
|
}
|
|
@@ -57,10 +72,12 @@ export class APIOperationRegistry {
|
|
|
57
72
|
}
|
|
58
73
|
/**
|
|
59
74
|
* Unregisters an operation.
|
|
75
|
+
*
|
|
76
|
+
* @throws {APIError} if the registry is frozen.
|
|
60
77
|
*/
|
|
61
78
|
unregister(name) {
|
|
62
79
|
if (this.frozen) {
|
|
63
|
-
throw
|
|
80
|
+
throw frozenRegistryError("unregister");
|
|
64
81
|
}
|
|
65
82
|
return this.operations.delete(name);
|
|
66
83
|
}
|
|
@@ -77,4 +94,15 @@ export class APIOperationRegistry {
|
|
|
77
94
|
return this.frozen;
|
|
78
95
|
}
|
|
79
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Mutating a frozen registry is a server-side programming error, never a
|
|
99
|
+
* client mistake — hence 500 and `expose: false`.
|
|
100
|
+
*/
|
|
101
|
+
function frozenRegistryError(action) {
|
|
102
|
+
return createAPIError(`Cannot ${action} operations on a frozen registry.`, {
|
|
103
|
+
statusCode: 500,
|
|
104
|
+
expose: false,
|
|
105
|
+
isOperational: false,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
80
108
|
//# sourceMappingURL=operationRegistry.core.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -4,40 +4,49 @@
|
|
|
4
4
|
* Application-facing API layer for the Zudojs framework.
|
|
5
5
|
*
|
|
6
6
|
* Provides transport-agnostic operation definitions, execution context,
|
|
7
|
-
* interceptors,
|
|
7
|
+
* interceptors, and result types.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```ts
|
|
11
|
-
* import {
|
|
11
|
+
* import {
|
|
12
|
+
* defineOperation,
|
|
13
|
+
* APIOperationRegistry,
|
|
14
|
+
* APIExecutor,
|
|
15
|
+
* createAPIContext,
|
|
16
|
+
* } from "@zudojs/api";
|
|
12
17
|
*
|
|
13
18
|
* const getUser = defineOperation({
|
|
14
19
|
* name: "users.get",
|
|
15
|
-
* input: GetUserSchema,
|
|
20
|
+
* input: GetUserSchema, // any Standard Schema (Zod, Valibot, ArkType, …)
|
|
16
21
|
* output: UserSchema,
|
|
17
|
-
* handler: async (input, context) =>
|
|
18
|
-
* return userService.findById(input.id);
|
|
19
|
-
* },
|
|
22
|
+
* handler: async (input, context) => userService.findById(input.id),
|
|
20
23
|
* });
|
|
21
24
|
*
|
|
22
25
|
* const registry = new APIOperationRegistry();
|
|
23
26
|
* registry.register(getUser);
|
|
24
27
|
*
|
|
25
28
|
* const executor = new APIExecutor();
|
|
26
|
-
* const
|
|
29
|
+
* const context = createAPIContext("req-1", {});
|
|
30
|
+
* const result = await executor.execute(
|
|
31
|
+
* registry.require("users.get"),
|
|
32
|
+
* { id: "123" },
|
|
33
|
+
* context,
|
|
34
|
+
* );
|
|
27
35
|
* ```
|
|
28
36
|
*/
|
|
29
37
|
export type { APISuccess, APIFailure, APIResult, } from "./api/result/apiResult.type.js";
|
|
30
38
|
export { apiSuccess, apiFailure, isApiSuccess, isApiFailure, } from "./api/result/apiResult.type.js";
|
|
31
39
|
export type { APIErrorOptions } from "./api/errors/index.js";
|
|
32
|
-
export { APIError, APIValidationError, APIAuthenticationError, APIAuthorizationError, APINotFoundError, APIConflictError, APIRateLimitError, APITimeoutError, APIUnavailableError, APIInternalError, APIVersionError, APIOperationNotFoundError, APIDuplicateOperationError, APIIdempotencyError, createAPIError, isAPIError, } from "./api/errors/index.js";
|
|
33
|
-
export { DEFAULT_OPERATION_TIMEOUT, MAX_INTERCEPTORS,
|
|
40
|
+
export { APIError, APIValidationError, APIAuthenticationError, APIAuthorizationError, APINotFoundError, APIConflictError, APIRateLimitError, APITimeoutError, APIUnavailableError, APIInternalError, APIVersionError, APIOperationNotFoundError, APIDuplicateOperationError, APIIdempotencyError, createAPIError, isAPIError, ErrorCode, } from "./api/errors/index.js";
|
|
41
|
+
export { DEFAULT_OPERATION_TIMEOUT, MAX_OPERATION_TIMEOUT, MAX_INTERCEPTORS, MAX_VALIDATION_ISSUES, MAX_VALIDATION_ISSUE_LENGTH, MAX_OPERATION_NAME_LENGTH, MAX_REQUEST_ID_LENGTH, } from "./api/constants.js";
|
|
34
42
|
export type { APIContext, APIContextKey } from "./api/context/context.type.js";
|
|
35
|
-
export { createAPIContext, RequestIdContextKey, CorrelationIdContextKey, TenantIdContextKey, UserIdContextKey, StartTimeContextKey, } from "./api/context/context.type.js";
|
|
43
|
+
export { createAPIContext, createContextKey, isValidRequestId, normalizeRequestId, RequestIdContextKey, CorrelationIdContextKey, TenantIdContextKey, UserIdContextKey, StartTimeContextKey, } from "./api/context/context.type.js";
|
|
36
44
|
export type { APIHandler } from "./api/handler/handler.type.js";
|
|
37
|
-
export type { APIOperation, APIOperationMetadata, DefineOperationOptions, } from "./api/operation/operation.type.js";
|
|
38
|
-
export { defineOperation } from "./api/operation/operation.type.js";
|
|
45
|
+
export type { AnyAPIOperation, APIOperation, APIOperationMetadata, DefineOperationOptions, } from "./api/operation/operation.type.js";
|
|
46
|
+
export { defineOperation, resolveOperationTimeout, } from "./api/operation/operation.type.js";
|
|
39
47
|
export { APIOperationRegistry } from "./api/registry/index.js";
|
|
40
48
|
export type { APIInterceptor, APIExecutionContext, } from "./api/interceptors/interceptor.type.js";
|
|
41
49
|
export { createNoopInterceptor } from "./api/interceptors/interceptor.type.js";
|
|
50
|
+
export type { APIExecutorOptions } from "./api/executor/index.js";
|
|
42
51
|
export { APIExecutor, normalizeAPIError } from "./api/executor/index.js";
|
|
43
52
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -4,37 +4,44 @@
|
|
|
4
4
|
* Application-facing API layer for the Zudojs framework.
|
|
5
5
|
*
|
|
6
6
|
* Provides transport-agnostic operation definitions, execution context,
|
|
7
|
-
* interceptors,
|
|
7
|
+
* interceptors, and result types.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
10
|
* ```ts
|
|
11
|
-
* import {
|
|
11
|
+
* import {
|
|
12
|
+
* defineOperation,
|
|
13
|
+
* APIOperationRegistry,
|
|
14
|
+
* APIExecutor,
|
|
15
|
+
* createAPIContext,
|
|
16
|
+
* } from "@zudojs/api";
|
|
12
17
|
*
|
|
13
18
|
* const getUser = defineOperation({
|
|
14
19
|
* name: "users.get",
|
|
15
|
-
* input: GetUserSchema,
|
|
20
|
+
* input: GetUserSchema, // any Standard Schema (Zod, Valibot, ArkType, …)
|
|
16
21
|
* output: UserSchema,
|
|
17
|
-
* handler: async (input, context) =>
|
|
18
|
-
* return userService.findById(input.id);
|
|
19
|
-
* },
|
|
22
|
+
* handler: async (input, context) => userService.findById(input.id),
|
|
20
23
|
* });
|
|
21
24
|
*
|
|
22
25
|
* const registry = new APIOperationRegistry();
|
|
23
26
|
* registry.register(getUser);
|
|
24
27
|
*
|
|
25
28
|
* const executor = new APIExecutor();
|
|
26
|
-
* const
|
|
29
|
+
* const context = createAPIContext("req-1", {});
|
|
30
|
+
* const result = await executor.execute(
|
|
31
|
+
* registry.require("users.get"),
|
|
32
|
+
* { id: "123" },
|
|
33
|
+
* context,
|
|
34
|
+
* );
|
|
27
35
|
* ```
|
|
28
36
|
*/
|
|
29
37
|
export { apiSuccess, apiFailure, isApiSuccess, isApiFailure, } from "./api/result/apiResult.type.js";
|
|
30
|
-
export { APIError, APIValidationError, APIAuthenticationError, APIAuthorizationError, APINotFoundError, APIConflictError, APIRateLimitError, APITimeoutError, APIUnavailableError, APIInternalError, APIVersionError, APIOperationNotFoundError, APIDuplicateOperationError, APIIdempotencyError, createAPIError, isAPIError, } from "./api/errors/index.js";
|
|
38
|
+
export { APIError, APIValidationError, APIAuthenticationError, APIAuthorizationError, APINotFoundError, APIConflictError, APIRateLimitError, APITimeoutError, APIUnavailableError, APIInternalError, APIVersionError, APIOperationNotFoundError, APIDuplicateOperationError, APIIdempotencyError, createAPIError, isAPIError, ErrorCode, } from "./api/errors/index.js";
|
|
31
39
|
// Constants
|
|
32
|
-
export { DEFAULT_OPERATION_TIMEOUT, MAX_INTERCEPTORS,
|
|
33
|
-
export { createAPIContext, RequestIdContextKey, CorrelationIdContextKey, TenantIdContextKey, UserIdContextKey, StartTimeContextKey, } from "./api/context/context.type.js";
|
|
34
|
-
export { defineOperation } from "./api/operation/operation.type.js";
|
|
40
|
+
export { DEFAULT_OPERATION_TIMEOUT, MAX_OPERATION_TIMEOUT, MAX_INTERCEPTORS, MAX_VALIDATION_ISSUES, MAX_VALIDATION_ISSUE_LENGTH, MAX_OPERATION_NAME_LENGTH, MAX_REQUEST_ID_LENGTH, } from "./api/constants.js";
|
|
41
|
+
export { createAPIContext, createContextKey, isValidRequestId, normalizeRequestId, RequestIdContextKey, CorrelationIdContextKey, TenantIdContextKey, UserIdContextKey, StartTimeContextKey, } from "./api/context/context.type.js";
|
|
42
|
+
export { defineOperation, resolveOperationTimeout, } from "./api/operation/operation.type.js";
|
|
35
43
|
// Registry
|
|
36
44
|
export { APIOperationRegistry } from "./api/registry/index.js";
|
|
37
45
|
export { createNoopInterceptor } from "./api/interceptors/interceptor.type.js";
|
|
38
|
-
// Executor
|
|
39
46
|
export { APIExecutor, normalizeAPIError } from "./api/executor/index.js";
|
|
40
47
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zudojs/api",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Application-facing API layer for Zudojs — operation definitions, execution context, interceptors,
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Application-facing API layer for Zudojs — operation definitions, execution context, interceptors, and transport-agnostic contracts.",
|
|
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/schema": "0.1
|
|
30
|
+
"@zudojs/errors": "1.0.1",
|
|
31
|
+
"@zudojs/constants": "1.0.1",
|
|
32
|
+
"@zudojs/types": "1.0.0",
|
|
33
|
+
"@zudojs/schema": "1.0.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"typescript": "
|
|
36
|
+
"typescript": "7.0.2",
|
|
37
37
|
"vitest": "^4.1.11"
|
|
38
38
|
},
|
|
39
39
|
"publishConfig": {
|
|
@@ -46,8 +46,19 @@
|
|
|
46
46
|
"interceptors"
|
|
47
47
|
],
|
|
48
48
|
"homepage": "https://github.com/oyinlola-tech/zudo#readme",
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/oyinlola-tech/zudo/issues"
|
|
51
|
+
},
|
|
49
52
|
"repository": {
|
|
50
53
|
"type": "git",
|
|
51
|
-
"url": "https://github.com/oyinlola-tech/zudo"
|
|
54
|
+
"url": "https://github.com/oyinlola-tech/zudo",
|
|
55
|
+
"directory": "packages/api"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"build": "tsc -p tsconfig.json",
|
|
59
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
60
|
+
"clean": "rm -rf dist",
|
|
61
|
+
"test": "vitest run",
|
|
62
|
+
"test:watch": "vitest"
|
|
52
63
|
}
|
|
53
|
-
}
|
|
64
|
+
}
|