@zudojs/api 0.1.0 → 1.0.0

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +133 -9
  3. package/dist/api/constants.d.ts +32 -2
  4. package/dist/api/constants.js +32 -2
  5. package/dist/api/context/context.type.d.ts +38 -0
  6. package/dist/api/context/context.type.js +86 -5
  7. package/dist/api/context/contextKey.type.d.ts +13 -0
  8. package/dist/api/context/contextKey.type.js +6 -0
  9. package/dist/api/errors/index.d.ts +1 -1
  10. package/dist/api/errors/index.js +1 -1
  11. package/dist/api/executor/executor.core.d.ts +81 -11
  12. package/dist/api/executor/executor.core.js +267 -29
  13. package/dist/api/executor/index.d.ts +1 -0
  14. package/dist/api/handler/handler.type.d.ts +5 -2
  15. package/dist/api/interceptors/interceptor.type.d.ts +18 -1
  16. package/dist/api/operation/operation.type.d.ts +90 -0
  17. package/dist/api/operation/operation.type.js +115 -3
  18. package/dist/api/registry/operationRegistry.core.d.ts +15 -2
  19. package/dist/api/registry/operationRegistry.core.js +32 -4
  20. package/dist/index.d.ts +21 -12
  21. package/dist/index.js +19 -12
  22. package/package.json +24 -17
  23. package/dist/.tsbuildinfo +0 -1
  24. package/dist/api/constants.d.ts.map +0 -1
  25. package/dist/api/constants.js.map +0 -1
  26. package/dist/api/context/context.type.d.ts.map +0 -1
  27. package/dist/api/context/context.type.js.map +0 -1
  28. package/dist/api/context/contextKey.type.d.ts.map +0 -1
  29. package/dist/api/context/contextKey.type.js.map +0 -1
  30. package/dist/api/errors/index.d.ts.map +0 -1
  31. package/dist/api/errors/index.js.map +0 -1
  32. package/dist/api/executor/executor.core.d.ts.map +0 -1
  33. package/dist/api/executor/executor.core.js.map +0 -1
  34. package/dist/api/executor/index.d.ts.map +0 -1
  35. package/dist/api/executor/index.js.map +0 -1
  36. package/dist/api/handler/handler.type.d.ts.map +0 -1
  37. package/dist/api/handler/handler.type.js.map +0 -1
  38. package/dist/api/interceptors/interceptor.type.d.ts.map +0 -1
  39. package/dist/api/interceptors/interceptor.type.js.map +0 -1
  40. package/dist/api/operation/operation.type.d.ts.map +0 -1
  41. package/dist/api/operation/operation.type.js.map +0 -1
  42. package/dist/api/registry/index.d.ts.map +0 -1
  43. package/dist/api/registry/index.js.map +0 -1
  44. package/dist/api/registry/operationRegistry.core.d.ts.map +0 -1
  45. package/dist/api/registry/operationRegistry.core.js.map +0 -1
  46. package/dist/api/result/apiResult.type.d.ts.map +0 -1
  47. package/dist/api/result/apiResult.type.js.map +0 -1
  48. package/dist/index.d.ts.map +0 -1
  49. 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 new Error("Cannot register operations on a frozen registry.");
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 Error(`Operation "${operation.name}" is already registered.`);
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 Error(`Operation "${name}" is not registered.`);
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 new Error("Cannot unregister operations on a frozen registry.");
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, policies, and result types.
7
+ * interceptors, and result types.
8
8
  *
9
9
  * @example
10
10
  * ```ts
11
- * import { defineOperation, APIOperationRegistry, APIExecutor } from "@zudojs/api";
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 result = await executor.execute(getUser, { id: "123" }, context);
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, MAX_POLICIES, } from "./api/constants.js";
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, policies, and result types.
7
+ * interceptors, and result types.
8
8
  *
9
9
  * @example
10
10
  * ```ts
11
- * import { defineOperation, APIOperationRegistry, APIExecutor } from "@zudojs/api";
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 result = await executor.execute(getUser, { id: "123" }, context);
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, MAX_POLICIES, } from "./api/constants.js";
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,7 +1,7 @@
1
1
  {
2
2
  "name": "@zudojs/api",
3
- "version": "0.1.0",
4
- "description": "Application-facing API layer for Zudojs — operation definitions, execution context, interceptors, policies, and transport-agnostic contracts.",
3
+ "version": "1.0.0",
4
+ "description": "Application-facing API layer for Zudojs — operation definitions, execution context, interceptors, and transport-agnostic contracts.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -14,26 +14,22 @@
14
14
  }
15
15
  },
16
16
  "files": [
17
- "dist"
17
+ "dist",
18
+ "!dist/**/*.map",
19
+ "!dist/**/*.tsbuildinfo",
20
+ "!dist/.tsbuildinfo"
18
21
  ],
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
22
  "engines": {
27
23
  "node": ">=24.0.0"
28
24
  },
29
25
  "dependencies": {
30
- "@zudojs/errors": "0.1.0",
31
- "@zudojs/constants": "0.1.0",
32
- "@zudojs/types": "0.1.0",
33
- "@zudojs/schema": "0.1.0"
26
+ "@zudojs/errors": "1.0.0",
27
+ "@zudojs/constants": "1.0.0",
28
+ "@zudojs/types": "1.0.0",
29
+ "@zudojs/schema": "1.0.0"
34
30
  },
35
31
  "devDependencies": {
36
- "typescript": "^7.0.2",
32
+ "typescript": "7.0.2",
37
33
  "vitest": "^4.1.11"
38
34
  },
39
35
  "publishConfig": {
@@ -46,8 +42,19 @@
46
42
  "interceptors"
47
43
  ],
48
44
  "homepage": "https://github.com/oyinlola-tech/zudo#readme",
45
+ "bugs": {
46
+ "url": "https://github.com/oyinlola-tech/zudo/issues"
47
+ },
49
48
  "repository": {
50
49
  "type": "git",
51
- "url": "https://github.com/oyinlola-tech/zudo"
50
+ "url": "https://github.com/oyinlola-tech/zudo",
51
+ "directory": "packages/api"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc -p tsconfig.json",
55
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.test.json --noEmit",
56
+ "clean": "rm -rf dist",
57
+ "test": "vitest run",
58
+ "test:watch": "vitest"
52
59
  }
53
- }
60
+ }