@webpieces/http-api 0.2.92 → 0.2.93

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-api",
3
- "version": "0.2.92",
3
+ "version": "0.2.93",
4
4
  "description": "HTTP API decorators for defining REST APIs",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,6 +22,6 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@webpieces/core-util": "0.2.92"
25
+ "@webpieces/core-util": "0.2.93"
26
26
  }
27
27
  }
@@ -4,94 +4,104 @@ import 'reflect-metadata';
4
4
  * These keys are used by both server-side (routing) and client-side (client generation).
5
5
  */
6
6
  export declare const METADATA_KEYS: {
7
- API_INTERFACE: string;
8
- ROUTES: string;
9
- HTTP_METHOD: string;
10
- PATH: string;
7
+ API_PATH: string;
8
+ ENDPOINTS: string;
9
+ AUTH_META: string;
11
10
  };
12
11
  /**
13
- * Route metadata stored on methods.
14
- * Used by both server-side routing and client-side HTTP client generation.
12
+ * Route metadata stored per-method at runtime.
13
+ * Used internally by http-routing and http-client as the runtime representation
14
+ * of a route. Constructed from @ApiPath + @Endpoint metadata by createApiClient
15
+ * and ApiRoutingFactory.
15
16
  */
16
17
  export declare class RouteMetadata {
17
18
  httpMethod: string;
18
19
  path: string;
19
20
  methodName: string;
20
- parameterTypes?: any[];
21
21
  controllerClassName?: string;
22
- constructor(httpMethod: string, path: string, methodName: string, parameterTypes?: any[], controllerClassName?: string);
22
+ authMeta?: AuthMeta;
23
+ constructor(httpMethod: string, path: string, methodName: string, controllerClassName?: string, authMeta?: AuthMeta);
23
24
  }
24
25
  /**
25
- * Mark a class as an API interface.
26
- * Similar to Java's JAX-RS interface pattern.
26
+ * Auth metadata attached to a class or method via @Authentication().
27
27
  *
28
- * This decorator is used by:
29
- * - Server: RESTApiRoutes reads it to validate API interfaces
30
- * - Client: Client generator reads it to identify API interfaces
28
+ * - authenticated=false public endpoint (no auth check)
29
+ * - authenticated=true, no roles requires authentication
30
+ * - authenticated=true, roles=['admin'] requires authentication + specific roles
31
+ * - authenticated=false + roles → INVALID (caught at decorator time)
32
+ */
33
+ export declare class AuthMeta {
34
+ authenticated: boolean;
35
+ roles: string[];
36
+ constructor(authenticated: boolean, roles?: string[]);
37
+ }
38
+ /**
39
+ * @ApiPath(basePath) - Class decorator that marks a class as an API definition
40
+ * and sets the base path for all endpoints.
31
41
  *
32
42
  * Usage:
33
43
  * ```typescript
34
- * @ApiInterface()
44
+ * @Authentication({authenticated: true})
45
+ * @ApiPath('/api/save')
35
46
  * abstract class SaveApiPrototype {
36
- * @Post()
37
- * @Path('/search/item')
38
- * save(request: SaveRequest): Promise<SaveResponse> {
39
- * throw new Error('Must be implemented');
40
- * }
47
+ * @Endpoint('/item')
48
+ * save(request: SaveRequest): Promise<SaveResponse> { ... }
41
49
  * }
42
50
  * ```
43
51
  */
44
- export declare function ApiInterface(): ClassDecorator;
52
+ export declare function ApiPath(basePath: string): ClassDecorator;
45
53
  /**
46
- * @Get decorator for GET requests.
47
- * Usage: @Get()
54
+ * @Endpoint(path) - Method decorator that registers a POST endpoint at the given path.
55
+ *
56
+ * All endpoints are POST-only (matching gRPC/thrift style).
57
+ *
58
+ * Usage:
59
+ * ```typescript
60
+ * @Endpoint('/item')
61
+ * save(request: SaveRequest): Promise<SaveResponse> { ... }
62
+ * ```
48
63
  */
49
- export declare function Get(): MethodDecorator;
64
+ export declare function Endpoint(path: string): MethodDecorator;
50
65
  /**
51
- * @Post decorator for POST requests.
52
- * Usage: @Post()
66
+ * Authentication config passed to @Authentication() decorator.
53
67
  */
54
- export declare function Post(): MethodDecorator;
68
+ export declare class AuthenticationConfig {
69
+ authenticated: boolean;
70
+ roles?: string[];
71
+ constructor(authenticated: boolean, roles?: string[]);
72
+ }
55
73
  /**
56
- * @Put decorator for PUT requests.
57
- * Usage: @Put()
74
+ * @Authentication(config) - Class or method decorator for auth requirements.
75
+ *
76
+ * Single decorator replaces @Public/@Authenticated/@Roles:
77
+ * - @Authentication({authenticated: false}) → public, no auth check
78
+ * - @Authentication({authenticated: true}) → requires authentication
79
+ * - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles
80
+ *
81
+ * Class-level is required. Methods can override class-level.
82
+ * Throws if authenticated=false but roles are specified (contradictory).
58
83
  */
59
- export declare function Put(): MethodDecorator;
84
+ export declare function Authentication(config: AuthenticationConfig): ClassDecorator & MethodDecorator;
60
85
  /**
61
- * @Delete decorator for DELETE requests.
62
- * Usage: @Delete()
86
+ * Get the base path from @ApiPath decorator.
63
87
  */
64
- export declare function Delete(): MethodDecorator;
88
+ export declare function getApiPath(apiClass: Function): string | undefined;
65
89
  /**
66
- * @Patch decorator for PATCH requests.
67
- * Usage: @Patch()
90
+ * Get all endpoints from @Endpoint decorators.
91
+ * Returns a record of methodName -> endpoint path.
68
92
  */
69
- export declare function Patch(): MethodDecorator;
93
+ export declare function getEndpoints(apiClass: Function): Record<string, string> | undefined;
70
94
  /**
71
- * @Path decorator to specify the route path.
72
- * Similar to JAX-RS @Path annotation.
73
- *
74
- * This decorator is used by:
75
- * - Server: To register routes at the specified path
76
- * - Client: To make HTTP requests to the specified path
77
- *
78
- * Usage:
79
- * ```typescript
80
- * @Post()
81
- * @Path('/search/item')
82
- * save(request: SaveRequest): Promise<SaveResponse> {
83
- * throw new Error('Must be implemented');
84
- * }
85
- * ```
95
+ * Check if a class has @ApiPath decorator.
86
96
  */
87
- export declare function Path(path: string): MethodDecorator;
97
+ export declare function isApiPath(apiClass: Function): boolean;
88
98
  /**
89
- * Helper function to get all routes from an API interface class.
90
- * Used by both server-side routing and client-side client generation.
99
+ * Get auth metadata for a specific method, falling back to class-level auth.
100
+ * Method-level auth takes precedence over class-level auth.
91
101
  */
92
- export declare function getRoutes(apiClass: any): RouteMetadata[];
102
+ export declare function getAuthMeta(apiClass: Function, methodName?: string): AuthMeta | undefined;
93
103
  /**
94
- * Helper function to check if a class is an API interface.
95
- * Used by both server-side routing and client-side client generation.
104
+ * Validate that a class/method doesn't have conflicting auth decorators.
105
+ * @throws Error if multiple @Authentication decorators are found on the same target.
96
106
  */
97
- export declare function isApiInterface(apiClass: any): boolean;
107
+ export declare function validateNoConflictingDecorators(apiClass: Function, methodName: string | undefined): void;
package/src/decorators.js CHANGED
@@ -1,175 +1,192 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RouteMetadata = exports.METADATA_KEYS = void 0;
4
- exports.ApiInterface = ApiInterface;
5
- exports.Get = Get;
6
- exports.Post = Post;
7
- exports.Put = Put;
8
- exports.Delete = Delete;
9
- exports.Patch = Patch;
10
- exports.Path = Path;
11
- exports.getRoutes = getRoutes;
12
- exports.isApiInterface = isApiInterface;
3
+ exports.AuthenticationConfig = exports.AuthMeta = exports.RouteMetadata = exports.METADATA_KEYS = void 0;
4
+ exports.ApiPath = ApiPath;
5
+ exports.Endpoint = Endpoint;
6
+ exports.Authentication = Authentication;
7
+ exports.getApiPath = getApiPath;
8
+ exports.getEndpoints = getEndpoints;
9
+ exports.isApiPath = isApiPath;
10
+ exports.getAuthMeta = getAuthMeta;
11
+ exports.validateNoConflictingDecorators = validateNoConflictingDecorators;
13
12
  require("reflect-metadata");
14
13
  /**
15
14
  * Metadata keys for storing API routing information.
16
15
  * These keys are used by both server-side (routing) and client-side (client generation).
17
16
  */
18
17
  exports.METADATA_KEYS = {
19
- API_INTERFACE: 'webpieces:api-interface',
20
- ROUTES: 'webpieces:routes',
21
- HTTP_METHOD: 'webpieces:http-method',
22
- PATH: 'webpieces:path',
18
+ API_PATH: 'webpieces:api-path',
19
+ ENDPOINTS: 'webpieces:endpoints',
20
+ AUTH_META: 'webpieces:auth-meta',
23
21
  };
24
22
  /**
25
- * Route metadata stored on methods.
26
- * Used by both server-side routing and client-side HTTP client generation.
23
+ * Route metadata stored per-method at runtime.
24
+ * Used internally by http-routing and http-client as the runtime representation
25
+ * of a route. Constructed from @ApiPath + @Endpoint metadata by createApiClient
26
+ * and ApiRoutingFactory.
27
27
  */
28
28
  class RouteMetadata {
29
- constructor(httpMethod, path, methodName, parameterTypes, controllerClassName) {
29
+ constructor(httpMethod, path, methodName, controllerClassName, authMeta) {
30
30
  this.httpMethod = httpMethod;
31
31
  this.path = path;
32
32
  this.methodName = methodName;
33
- this.parameterTypes = parameterTypes;
34
33
  this.controllerClassName = controllerClassName;
34
+ this.authMeta = authMeta;
35
35
  }
36
36
  }
37
37
  exports.RouteMetadata = RouteMetadata;
38
38
  /**
39
- * Mark a class as an API interface.
40
- * Similar to Java's JAX-RS interface pattern.
39
+ * Auth metadata attached to a class or method via @Authentication().
41
40
  *
42
- * This decorator is used by:
43
- * - Server: RESTApiRoutes reads it to validate API interfaces
44
- * - Client: Client generator reads it to identify API interfaces
41
+ * - authenticated=false public endpoint (no auth check)
42
+ * - authenticated=true, no roles requires authentication
43
+ * - authenticated=true, roles=['admin'] requires authentication + specific roles
44
+ * - authenticated=false + roles → INVALID (caught at decorator time)
45
+ */
46
+ class AuthMeta {
47
+ constructor(authenticated, roles) {
48
+ this.authenticated = authenticated;
49
+ this.roles = roles ?? [];
50
+ }
51
+ }
52
+ exports.AuthMeta = AuthMeta;
53
+ /**
54
+ * @ApiPath(basePath) - Class decorator that marks a class as an API definition
55
+ * and sets the base path for all endpoints.
45
56
  *
46
57
  * Usage:
47
58
  * ```typescript
48
- * @ApiInterface()
59
+ * @Authentication({authenticated: true})
60
+ * @ApiPath('/api/save')
49
61
  * abstract class SaveApiPrototype {
50
- * @Post()
51
- * @Path('/search/item')
52
- * save(request: SaveRequest): Promise<SaveResponse> {
53
- * throw new Error('Must be implemented');
54
- * }
62
+ * @Endpoint('/item')
63
+ * save(request: SaveRequest): Promise<SaveResponse> { ... }
55
64
  * }
56
65
  * ```
57
66
  */
58
- function ApiInterface() {
67
+ function ApiPath(basePath) {
68
+ // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any
59
69
  return (target) => {
60
- Reflect.defineMetadata(exports.METADATA_KEYS.API_INTERFACE, true, target);
61
- // Initialize routes array if not exists
62
- if (!Reflect.hasMetadata(exports.METADATA_KEYS.ROUTES, target)) {
63
- Reflect.defineMetadata(exports.METADATA_KEYS.ROUTES, [], target);
70
+ Reflect.defineMetadata(exports.METADATA_KEYS.API_PATH, basePath, target);
71
+ // Initialize endpoints map if not exists
72
+ if (!Reflect.hasMetadata(exports.METADATA_KEYS.ENDPOINTS, target)) {
73
+ Reflect.defineMetadata(exports.METADATA_KEYS.ENDPOINTS, {}, target);
64
74
  }
65
75
  };
66
76
  }
67
77
  /**
68
- * Internal helper to mark a method with an HTTP method.
69
- * Used by @Get, @Post, @Put, @Delete, @Patch decorators.
78
+ * @Endpoint(path) - Method decorator that registers a POST endpoint at the given path.
79
+ *
80
+ * All endpoints are POST-only (matching gRPC/thrift style).
81
+ *
82
+ * Usage:
83
+ * ```typescript
84
+ * @Endpoint('/item')
85
+ * save(request: SaveRequest): Promise<SaveResponse> { ... }
86
+ * ```
70
87
  */
71
- function httpMethod(method) {
72
- return (target, propertyKey, descriptor) => {
73
- // For static methods, target is the constructor itself
74
- // For instance methods, target is the prototype
88
+ function Endpoint(path) {
89
+ // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any
90
+ return (target, propertyKey, _descriptor) => {
75
91
  const metadataTarget = typeof target === 'function' ? target : target.constructor;
76
- const existingMetadata = Reflect.getMetadata(exports.METADATA_KEYS.ROUTES, metadataTarget) || [];
77
- // Find or create route metadata for this method
78
- let routeMetadata = existingMetadata.find((r) => r.methodName === propertyKey);
79
- if (!routeMetadata) {
80
- routeMetadata = new RouteMetadata('', '', propertyKey);
81
- existingMetadata.push(routeMetadata);
82
- }
83
- routeMetadata.httpMethod = method;
84
- // Get parameter types
85
- const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyKey);
86
- if (paramTypes) {
87
- routeMetadata.parameterTypes = paramTypes;
88
- }
89
- Reflect.defineMetadata(exports.METADATA_KEYS.ROUTES, existingMetadata, metadataTarget);
92
+ const endpoints = Reflect.getMetadata(exports.METADATA_KEYS.ENDPOINTS, metadataTarget) || {};
93
+ endpoints[propertyKey] = path;
94
+ Reflect.defineMetadata(exports.METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);
90
95
  };
91
96
  }
92
97
  /**
93
- * @Get decorator for GET requests.
94
- * Usage: @Get()
98
+ * Authentication config passed to @Authentication() decorator.
95
99
  */
96
- function Get() {
97
- return httpMethod('GET');
100
+ class AuthenticationConfig {
101
+ constructor(authenticated, roles) {
102
+ this.authenticated = authenticated;
103
+ this.roles = roles;
104
+ }
98
105
  }
106
+ exports.AuthenticationConfig = AuthenticationConfig;
99
107
  /**
100
- * @Post decorator for POST requests.
101
- * Usage: @Post()
108
+ * @Authentication(config) - Class or method decorator for auth requirements.
109
+ *
110
+ * Single decorator replaces @Public/@Authenticated/@Roles:
111
+ * - @Authentication({authenticated: false}) → public, no auth check
112
+ * - @Authentication({authenticated: true}) → requires authentication
113
+ * - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles
114
+ *
115
+ * Class-level is required. Methods can override class-level.
116
+ * Throws if authenticated=false but roles are specified (contradictory).
102
117
  */
103
- function Post() {
104
- return httpMethod('POST');
118
+ function Authentication(config) {
119
+ // Validate: can't be public with roles
120
+ if (!config.authenticated && config.roles && config.roles.length > 0) {
121
+ throw new Error(`Invalid @Authentication config: authenticated=false but roles=${JSON.stringify(config.roles)}. ` +
122
+ `Cannot require roles on a public endpoint. Set authenticated=true or remove roles.`);
123
+ }
124
+ const authMeta = new AuthMeta(config.authenticated, config.roles);
125
+ // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any
126
+ return (target, propertyKey, _descriptor) => {
127
+ if (propertyKey !== undefined) {
128
+ // Method decorator
129
+ const metadataTarget = typeof target === 'function' ? target : target.constructor;
130
+ validateNoConflictingDecorators(metadataTarget, propertyKey);
131
+ Reflect.defineMetadata(exports.METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);
132
+ }
133
+ else {
134
+ // Class decorator
135
+ validateNoConflictingDecorators(target, undefined);
136
+ Reflect.defineMetadata(exports.METADATA_KEYS.AUTH_META, authMeta, target);
137
+ }
138
+ };
105
139
  }
140
+ // ============================================================
141
+ // Helper functions
142
+ // ============================================================
106
143
  /**
107
- * @Put decorator for PUT requests.
108
- * Usage: @Put()
144
+ * Get the base path from @ApiPath decorator.
109
145
  */
110
- function Put() {
111
- return httpMethod('PUT');
146
+ function getApiPath(apiClass) {
147
+ return Reflect.getMetadata(exports.METADATA_KEYS.API_PATH, apiClass);
112
148
  }
113
149
  /**
114
- * @Delete decorator for DELETE requests.
115
- * Usage: @Delete()
150
+ * Get all endpoints from @Endpoint decorators.
151
+ * Returns a record of methodName -> endpoint path.
116
152
  */
117
- function Delete() {
118
- return httpMethod('DELETE');
153
+ function getEndpoints(apiClass) {
154
+ return Reflect.getMetadata(exports.METADATA_KEYS.ENDPOINTS, apiClass);
119
155
  }
120
156
  /**
121
- * @Patch decorator for PATCH requests.
122
- * Usage: @Patch()
157
+ * Check if a class has @ApiPath decorator.
123
158
  */
124
- function Patch() {
125
- return httpMethod('PATCH');
159
+ function isApiPath(apiClass) {
160
+ return Reflect.hasMetadata(exports.METADATA_KEYS.API_PATH, apiClass);
126
161
  }
127
162
  /**
128
- * @Path decorator to specify the route path.
129
- * Similar to JAX-RS @Path annotation.
130
- *
131
- * This decorator is used by:
132
- * - Server: To register routes at the specified path
133
- * - Client: To make HTTP requests to the specified path
134
- *
135
- * Usage:
136
- * ```typescript
137
- * @Post()
138
- * @Path('/search/item')
139
- * save(request: SaveRequest): Promise<SaveResponse> {
140
- * throw new Error('Must be implemented');
141
- * }
142
- * ```
163
+ * Get auth metadata for a specific method, falling back to class-level auth.
164
+ * Method-level auth takes precedence over class-level auth.
143
165
  */
144
- function Path(path) {
145
- return (target, propertyKey, descriptor) => {
146
- // For static methods, target is the constructor itself
147
- // For instance methods, target is the prototype
148
- const metadataTarget = typeof target === 'function' ? target : target.constructor;
149
- const existingMetadata = Reflect.getMetadata(exports.METADATA_KEYS.ROUTES, metadataTarget) || [];
150
- // Find or create route metadata for this method
151
- let routeMetadata = existingMetadata.find((r) => r.methodName === propertyKey);
152
- if (!routeMetadata) {
153
- routeMetadata = new RouteMetadata('', '', propertyKey);
154
- existingMetadata.push(routeMetadata);
166
+ function getAuthMeta(apiClass, methodName) {
167
+ // Check method-level first
168
+ if (methodName) {
169
+ const methodAuth = Reflect.getMetadata(exports.METADATA_KEYS.AUTH_META, apiClass, methodName);
170
+ if (methodAuth) {
171
+ return methodAuth;
155
172
  }
156
- routeMetadata.path = path;
157
- Reflect.defineMetadata(exports.METADATA_KEYS.ROUTES, existingMetadata, metadataTarget);
158
- };
159
- }
160
- /**
161
- * Helper function to get all routes from an API interface class.
162
- * Used by both server-side routing and client-side client generation.
163
- */
164
- function getRoutes(apiClass) {
165
- const routes = Reflect.getMetadata(exports.METADATA_KEYS.ROUTES, apiClass);
166
- return routes || [];
173
+ }
174
+ // Fall back to class-level
175
+ return Reflect.getMetadata(exports.METADATA_KEYS.AUTH_META, apiClass);
167
176
  }
168
177
  /**
169
- * Helper function to check if a class is an API interface.
170
- * Used by both server-side routing and client-side client generation.
178
+ * Validate that a class/method doesn't have conflicting auth decorators.
179
+ * @throws Error if multiple @Authentication decorators are found on the same target.
171
180
  */
172
- function isApiInterface(apiClass) {
173
- return Reflect.getMetadata(exports.METADATA_KEYS.API_INTERFACE, apiClass) === true;
181
+ function validateNoConflictingDecorators(apiClass, methodName) {
182
+ const existing = methodName
183
+ ? Reflect.getMetadata(exports.METADATA_KEYS.AUTH_META, apiClass, methodName)
184
+ : Reflect.getMetadata(exports.METADATA_KEYS.AUTH_META, apiClass);
185
+ if (existing) {
186
+ const targetName = apiClass.name || 'Unknown';
187
+ const location = methodName ? `method '${methodName}' of ${targetName}` : `class ${targetName}`;
188
+ throw new Error(`Conflicting @Authentication on ${location}. ` +
189
+ `Only one @Authentication() decorator allowed per target.`);
190
+ }
174
191
  }
175
192
  //# sourceMappingURL=decorators.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/decorators.ts"],"names":[],"mappings":";;;AA2DA,oCASC;AAwCD,kBAEC;AAMD,oBAEC;AAMD,kBAEC;AAMD,wBAEC;AAMD,sBAEC;AAmBD,oBAsBC;AAMD,8BAGC;AAMD,wCAEC;AAxMD,4BAA0B;AAE1B;;;GAGG;AACU,QAAA,aAAa,GAAG;IACzB,aAAa,EAAE,yBAAyB;IACxC,MAAM,EAAE,kBAAkB;IAC1B,WAAW,EAAE,uBAAuB;IACpC,IAAI,EAAE,gBAAgB;CACzB,CAAC;AAEF;;;GAGG;AACH,MAAa,aAAa;IAOtB,YACI,UAAkB,EAClB,IAAY,EACZ,UAAkB,EAClB,cAAsB,EACtB,mBAA4B;QAE5B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;IACnD,CAAC;CACJ;AApBD,sCAoBC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,YAAY;IACxB,OAAO,CAAC,MAAW,EAAE,EAAE;QACnB,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAElE,wCAAwC;QACxC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;YACrD,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QAC7D,CAAC;IACL,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,MAAc;IAC9B,OAAO,CAAC,MAAW,EAAE,WAA4B,EAAE,UAA8B,EAAE,EAAE;QACjF,uDAAuD;QACvD,gDAAgD;QAChD,MAAM,cAAc,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;QAElF,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,CAAC;QAEzF,gDAAgD;QAChD,IAAI,aAAa,GAAG,gBAAgB,CAAC,IAAI,CACrC,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,WAAW,CACrD,CAAC;QAEF,IAAI,CAAC,aAAa,EAAE,CAAC;YACjB,aAAa,GAAG,IAAI,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,WAAqB,CAAC,CAAC;YACjE,gBAAgB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzC,CAAC;QAED,aAAa,CAAC,UAAU,GAAG,MAAM,CAAC;QAElC,sBAAsB;QACtB,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,mBAAmB,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;QACjF,IAAI,UAAU,EAAE,CAAC;YACb,aAAa,CAAC,cAAc,GAAG,UAAU,CAAC;QAC9C,CAAC;QAED,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,cAAc,CAAC,CAAC;IACnF,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,SAAgB,GAAG;IACf,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,SAAgB,IAAI;IAChB,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC;AAED;;;GAGG;AACH,SAAgB,GAAG;IACf,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,SAAgB,MAAM;IAClB,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,SAAgB,KAAK;IACjB,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,IAAI,CAAC,IAAY;IAC7B,OAAO,CAAC,MAAW,EAAE,WAA4B,EAAE,UAA8B,EAAE,EAAE;QACjF,uDAAuD;QACvD,gDAAgD;QAChD,MAAM,cAAc,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC;QAElF,MAAM,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,CAAC;QAEzF,gDAAgD;QAChD,IAAI,aAAa,GAAG,gBAAgB,CAAC,IAAI,CACrC,CAAC,CAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,WAAW,CACrD,CAAC;QAEF,IAAI,CAAC,aAAa,EAAE,CAAC;YACjB,aAAa,GAAG,IAAI,aAAa,CAAC,EAAE,EAAE,EAAE,EAAE,WAAqB,CAAC,CAAC;YACjE,gBAAgB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACzC,CAAC;QAED,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC;QAE1B,OAAO,CAAC,cAAc,CAAC,qBAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,cAAc,CAAC,CAAC;IACnF,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,QAAa;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACnE,OAAO,MAAM,IAAI,EAAE,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc,CAAC,QAAa;IACxC,OAAO,OAAO,CAAC,WAAW,CAAC,qBAAa,CAAC,aAAa,EAAE,QAAQ,CAAC,KAAK,IAAI,CAAC;AAC/E,CAAC","sourcesContent":["import 'reflect-metadata';\n\n/**\n * Metadata keys for storing API routing information.\n * These keys are used by both server-side (routing) and client-side (client generation).\n */\nexport const METADATA_KEYS = {\n API_INTERFACE: 'webpieces:api-interface',\n ROUTES: 'webpieces:routes',\n HTTP_METHOD: 'webpieces:http-method',\n PATH: 'webpieces:path',\n};\n\n/**\n * Route metadata stored on methods.\n * Used by both server-side routing and client-side HTTP client generation.\n */\nexport class RouteMetadata {\n httpMethod: string;\n path: string;\n methodName: string;\n parameterTypes?: any[];\n controllerClassName?: string;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n parameterTypes?: any[],\n controllerClassName?: string,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.parameterTypes = parameterTypes;\n this.controllerClassName = controllerClassName;\n }\n}\n\n/**\n * Mark a class as an API interface.\n * Similar to Java's JAX-RS interface pattern.\n *\n * This decorator is used by:\n * - Server: RESTApiRoutes reads it to validate API interfaces\n * - Client: Client generator reads it to identify API interfaces\n *\n * Usage:\n * ```typescript\n * @ApiInterface()\n * abstract class SaveApiPrototype {\n * @Post()\n * @Path('/search/item')\n * save(request: SaveRequest): Promise<SaveResponse> {\n * throw new Error('Must be implemented');\n * }\n * }\n * ```\n */\nexport function ApiInterface(): ClassDecorator {\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_INTERFACE, true, target);\n\n // Initialize routes array if not exists\n if (!Reflect.hasMetadata(METADATA_KEYS.ROUTES, target)) {\n Reflect.defineMetadata(METADATA_KEYS.ROUTES, [], target);\n }\n };\n}\n\n/**\n * Internal helper to mark a method with an HTTP method.\n * Used by @Get, @Post, @Put, @Delete, @Patch decorators.\n */\nfunction httpMethod(method: string): MethodDecorator {\n return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n // For static methods, target is the constructor itself\n // For instance methods, target is the prototype\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n\n const existingMetadata = Reflect.getMetadata(METADATA_KEYS.ROUTES, metadataTarget) || [];\n\n // Find or create route metadata for this method\n let routeMetadata = existingMetadata.find(\n (r: RouteMetadata) => r.methodName === propertyKey,\n );\n\n if (!routeMetadata) {\n routeMetadata = new RouteMetadata('', '', propertyKey as string);\n existingMetadata.push(routeMetadata);\n }\n\n routeMetadata.httpMethod = method;\n\n // Get parameter types\n const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyKey);\n if (paramTypes) {\n routeMetadata.parameterTypes = paramTypes;\n }\n\n Reflect.defineMetadata(METADATA_KEYS.ROUTES, existingMetadata, metadataTarget);\n };\n}\n\n/**\n * @Get decorator for GET requests.\n * Usage: @Get()\n */\nexport function Get(): MethodDecorator {\n return httpMethod('GET');\n}\n\n/**\n * @Post decorator for POST requests.\n * Usage: @Post()\n */\nexport function Post(): MethodDecorator {\n return httpMethod('POST');\n}\n\n/**\n * @Put decorator for PUT requests.\n * Usage: @Put()\n */\nexport function Put(): MethodDecorator {\n return httpMethod('PUT');\n}\n\n/**\n * @Delete decorator for DELETE requests.\n * Usage: @Delete()\n */\nexport function Delete(): MethodDecorator {\n return httpMethod('DELETE');\n}\n\n/**\n * @Patch decorator for PATCH requests.\n * Usage: @Patch()\n */\nexport function Patch(): MethodDecorator {\n return httpMethod('PATCH');\n}\n\n/**\n * @Path decorator to specify the route path.\n * Similar to JAX-RS @Path annotation.\n *\n * This decorator is used by:\n * - Server: To register routes at the specified path\n * - Client: To make HTTP requests to the specified path\n *\n * Usage:\n * ```typescript\n * @Post()\n * @Path('/search/item')\n * save(request: SaveRequest): Promise<SaveResponse> {\n * throw new Error('Must be implemented');\n * }\n * ```\n */\nexport function Path(path: string): MethodDecorator {\n return (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {\n // For static methods, target is the constructor itself\n // For instance methods, target is the prototype\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n\n const existingMetadata = Reflect.getMetadata(METADATA_KEYS.ROUTES, metadataTarget) || [];\n\n // Find or create route metadata for this method\n let routeMetadata = existingMetadata.find(\n (r: RouteMetadata) => r.methodName === propertyKey,\n );\n\n if (!routeMetadata) {\n routeMetadata = new RouteMetadata('', '', propertyKey as string);\n existingMetadata.push(routeMetadata);\n }\n\n routeMetadata.path = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ROUTES, existingMetadata, metadataTarget);\n };\n}\n\n/**\n * Helper function to get all routes from an API interface class.\n * Used by both server-side routing and client-side client generation.\n */\nexport function getRoutes(apiClass: any): RouteMetadata[] {\n const routes = Reflect.getMetadata(METADATA_KEYS.ROUTES, apiClass);\n return routes || [];\n}\n\n/**\n * Helper function to check if a class is an API interface.\n * Used by both server-side routing and client-side client generation.\n */\nexport function isApiInterface(apiClass: any): boolean {\n return Reflect.getMetadata(METADATA_KEYS.API_INTERFACE, apiClass) === true;\n}\n"]}
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/decorators.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\n\n/**\n * Metadata keys for storing API routing information.\n * These keys are used by both server-side (routing) and client-side (client generation).\n */\nexport const METADATA_KEYS = {\n API_PATH: 'webpieces:api-path',\n ENDPOINTS: 'webpieces:endpoints',\n AUTH_META: 'webpieces:auth-meta',\n};\n\n/**\n * Route metadata stored per-method at runtime.\n * Used internally by http-routing and http-client as the runtime representation\n * of a route. Constructed from @ApiPath + @Endpoint metadata by createApiClient\n * and ApiRoutingFactory.\n */\nexport class RouteMetadata {\n httpMethod: string;\n path: string;\n methodName: string;\n controllerClassName?: string;\n authMeta?: AuthMeta;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.controllerClassName = controllerClassName;\n this.authMeta = authMeta;\n }\n}\n\n/**\n * Auth metadata attached to a class or method via @Authentication().\n *\n * - authenticated=false → public endpoint (no auth check)\n * - authenticated=true, no roles → requires authentication\n * - authenticated=true, roles=['admin'] → requires authentication + specific roles\n * - authenticated=false + roles → INVALID (caught at decorator time)\n */\nexport class AuthMeta {\n authenticated: boolean;\n roles: string[];\n\n constructor(authenticated: boolean, roles?: string[]) {\n this.authenticated = authenticated;\n this.roles = roles ?? [];\n }\n}\n\n/**\n * @ApiPath(basePath) - Class decorator that marks a class as an API definition\n * and sets the base path for all endpoints.\n *\n * Usage:\n * ```typescript\n * @Authentication({authenticated: true})\n * @ApiPath('/api/save')\n * abstract class SaveApiPrototype {\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * }\n * ```\n */\nexport function ApiPath(basePath: string): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_PATH, basePath, target);\n\n // Initialize endpoints map if not exists\n if (!Reflect.hasMetadata(METADATA_KEYS.ENDPOINTS, target)) {\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, {}, target);\n }\n };\n}\n\n/**\n * @Endpoint(path) - Method decorator that registers a POST endpoint at the given path.\n *\n * All endpoints are POST-only (matching gRPC/thrift style).\n *\n * Usage:\n * ```typescript\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * ```\n */\nexport function Endpoint(path: string): MethodDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, metadataTarget) || {};\n\n endpoints[propertyKey as string] = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);\n };\n}\n\n/**\n * Authentication config passed to @Authentication() decorator.\n */\nexport class AuthenticationConfig {\n authenticated: boolean;\n roles?: string[];\n\n constructor(authenticated: boolean, roles?: string[]) {\n this.authenticated = authenticated;\n this.roles = roles;\n }\n}\n\n/**\n * @Authentication(config) - Class or method decorator for auth requirements.\n *\n * Single decorator replaces @Public/@Authenticated/@Roles:\n * - @Authentication({authenticated: false}) → public, no auth check\n * - @Authentication({authenticated: true}) → requires authentication\n * - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles\n *\n * Class-level is required. Methods can override class-level.\n * Throws if authenticated=false but roles are specified (contradictory).\n */\nexport function Authentication(config: AuthenticationConfig): ClassDecorator & MethodDecorator {\n // Validate: can't be public with roles\n if (!config.authenticated && config.roles && config.roles.length > 0) {\n throw new Error(\n `Invalid @Authentication config: authenticated=false but roles=${JSON.stringify(config.roles)}. ` +\n `Cannot require roles on a public endpoint. Set authenticated=true or remove roles.`\n );\n }\n\n const authMeta = new AuthMeta(config.authenticated, config.roles);\n\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey?: string | symbol, _descriptor?: PropertyDescriptor) => {\n if (propertyKey !== undefined) {\n // Method decorator\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n validateNoConflictingDecorators(metadataTarget, propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);\n } else {\n // Class decorator\n validateNoConflictingDecorators(target, undefined);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, target);\n }\n };\n}\n\n// ============================================================\n// Helper functions\n// ============================================================\n\n/**\n * Get the base path from @ApiPath decorator.\n */\nexport function getApiPath(apiClass: Function): string | undefined {\n return Reflect.getMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get all endpoints from @Endpoint decorators.\n * Returns a record of methodName -> endpoint path.\n */\nexport function getEndpoints(apiClass: Function): Record<string, string> | undefined {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass);\n}\n\n/**\n * Check if a class has @ApiPath decorator.\n */\nexport function isApiPath(apiClass: Function): boolean {\n return Reflect.hasMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get auth metadata for a specific method, falling back to class-level auth.\n * Method-level auth takes precedence over class-level auth.\n */\nexport function getAuthMeta(apiClass: Function, methodName?: string): AuthMeta | undefined {\n // Check method-level first\n if (methodName) {\n const methodAuth = Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName);\n if (methodAuth) {\n return methodAuth;\n }\n }\n\n // Fall back to class-level\n return Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n}\n\n/**\n * Validate that a class/method doesn't have conflicting auth decorators.\n * @throws Error if multiple @Authentication decorators are found on the same target.\n */\nexport function validateNoConflictingDecorators(apiClass: Function, methodName: string | undefined): void {\n const existing = methodName\n ? Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName)\n : Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n\n if (existing) {\n const targetName = apiClass.name || 'Unknown';\n const location = methodName ? `method '${methodName}' of ${targetName}` : `class ${targetName}`;\n throw new Error(\n `Conflicting @Authentication on ${location}. ` +\n `Only one @Authentication() decorator allowed per target.`\n );\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -16,7 +16,7 @@
16
16
  * └── http-client (client: contract → HTTP requests)
17
17
  * ```
18
18
  */
19
- export { ApiInterface, Get, Post, Put, Delete, Patch, Path, getRoutes, isApiInterface, RouteMetadata, METADATA_KEYS, } from './decorators';
19
+ export { ApiPath, Endpoint, Authentication, AuthenticationConfig, getApiPath, getEndpoints, isApiPath, getAuthMeta, validateNoConflictingDecorators, AuthMeta, RouteMetadata, METADATA_KEYS, } from './decorators';
20
20
  export { ValidateImplementation } from './validators';
21
21
  export { ProtocolError, HttpError, HttpNotFoundError, EndpointNotFoundError, HttpBadRequestError, HttpUnauthorizedError, HttpForbiddenError, HttpTimeoutError, HttpBadGatewayError, HttpGatewayTimeoutError, HttpInternalServerError, HttpVendorError, HttpUserError, ENTITY_NOT_FOUND, WRONG_LOGIN_TYPE, WRONG_LOGIN, NOT_APPROVED, EMAIL_NOT_CONFIRMED, WRONG_DOMAIN, WRONG_COMPANY, NO_REG_CODE, } from './errors';
22
22
  export { InstantDto, DateDto, TimeDto, DateTimeDto, InstantUtil, DateUtil, TimeUtil, DateTimeUtil, } from './datetime';
package/src/index.js CHANGED
@@ -18,18 +18,19 @@
18
18
  * ```
19
19
  */
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.LogApiCall = exports.HEADER_TYPES = exports.HeaderMethods = exports.PlatformHeadersExtension = exports.PlatformHeader = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.HttpUserError = exports.HttpVendorError = exports.HttpInternalServerError = exports.HttpGatewayTimeoutError = exports.HttpBadGatewayError = exports.HttpTimeoutError = exports.HttpForbiddenError = exports.HttpUnauthorizedError = exports.HttpBadRequestError = exports.EndpointNotFoundError = exports.HttpNotFoundError = exports.HttpError = exports.ProtocolError = exports.METADATA_KEYS = exports.RouteMetadata = exports.isApiInterface = exports.getRoutes = exports.Path = exports.Patch = exports.Delete = exports.Put = exports.Post = exports.Get = exports.ApiInterface = void 0;
21
+ exports.LogApiCall = exports.HEADER_TYPES = exports.HeaderMethods = exports.PlatformHeadersExtension = exports.PlatformHeader = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.HttpUserError = exports.HttpVendorError = exports.HttpInternalServerError = exports.HttpGatewayTimeoutError = exports.HttpBadGatewayError = exports.HttpTimeoutError = exports.HttpForbiddenError = exports.HttpUnauthorizedError = exports.HttpBadRequestError = exports.EndpointNotFoundError = exports.HttpNotFoundError = exports.HttpError = exports.ProtocolError = exports.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getAuthMeta = exports.isApiPath = exports.getEndpoints = exports.getApiPath = exports.AuthenticationConfig = exports.Authentication = exports.Endpoint = exports.ApiPath = void 0;
22
22
  // API definition decorators
23
23
  var decorators_1 = require("./decorators");
24
- Object.defineProperty(exports, "ApiInterface", { enumerable: true, get: function () { return decorators_1.ApiInterface; } });
25
- Object.defineProperty(exports, "Get", { enumerable: true, get: function () { return decorators_1.Get; } });
26
- Object.defineProperty(exports, "Post", { enumerable: true, get: function () { return decorators_1.Post; } });
27
- Object.defineProperty(exports, "Put", { enumerable: true, get: function () { return decorators_1.Put; } });
28
- Object.defineProperty(exports, "Delete", { enumerable: true, get: function () { return decorators_1.Delete; } });
29
- Object.defineProperty(exports, "Patch", { enumerable: true, get: function () { return decorators_1.Patch; } });
30
- Object.defineProperty(exports, "Path", { enumerable: true, get: function () { return decorators_1.Path; } });
31
- Object.defineProperty(exports, "getRoutes", { enumerable: true, get: function () { return decorators_1.getRoutes; } });
32
- Object.defineProperty(exports, "isApiInterface", { enumerable: true, get: function () { return decorators_1.isApiInterface; } });
24
+ Object.defineProperty(exports, "ApiPath", { enumerable: true, get: function () { return decorators_1.ApiPath; } });
25
+ Object.defineProperty(exports, "Endpoint", { enumerable: true, get: function () { return decorators_1.Endpoint; } });
26
+ Object.defineProperty(exports, "Authentication", { enumerable: true, get: function () { return decorators_1.Authentication; } });
27
+ Object.defineProperty(exports, "AuthenticationConfig", { enumerable: true, get: function () { return decorators_1.AuthenticationConfig; } });
28
+ Object.defineProperty(exports, "getApiPath", { enumerable: true, get: function () { return decorators_1.getApiPath; } });
29
+ Object.defineProperty(exports, "getEndpoints", { enumerable: true, get: function () { return decorators_1.getEndpoints; } });
30
+ Object.defineProperty(exports, "isApiPath", { enumerable: true, get: function () { return decorators_1.isApiPath; } });
31
+ Object.defineProperty(exports, "getAuthMeta", { enumerable: true, get: function () { return decorators_1.getAuthMeta; } });
32
+ Object.defineProperty(exports, "validateNoConflictingDecorators", { enumerable: true, get: function () { return decorators_1.validateNoConflictingDecorators; } });
33
+ Object.defineProperty(exports, "AuthMeta", { enumerable: true, get: function () { return decorators_1.AuthMeta; } });
33
34
  Object.defineProperty(exports, "RouteMetadata", { enumerable: true, get: function () { return decorators_1.RouteMetadata; } });
34
35
  Object.defineProperty(exports, "METADATA_KEYS", { enumerable: true, get: function () { return decorators_1.METADATA_KEYS; } });
35
36
  // HTTP errors
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAEH,4BAA4B;AAC5B,2CAYsB;AAXlB,0GAAA,YAAY,OAAA;AACZ,iGAAA,GAAG,OAAA;AACH,kGAAA,IAAI,OAAA;AACJ,iGAAA,GAAG,OAAA;AACH,oGAAA,MAAM,OAAA;AACN,mGAAA,KAAK,OAAA;AACL,kGAAA,IAAI,OAAA;AACJ,uGAAA,SAAS,OAAA;AACT,4GAAA,cAAc,OAAA;AACd,2GAAA,aAAa,OAAA;AACb,2GAAA,aAAa,OAAA;AAMjB,cAAc;AACd,mCAuBkB;AAtBd,uGAAA,aAAa,OAAA;AACb,mGAAA,SAAS,OAAA;AACT,2GAAA,iBAAiB,OAAA;AACjB,+GAAA,qBAAqB,OAAA;AACrB,6GAAA,mBAAmB,OAAA;AACnB,+GAAA,qBAAqB,OAAA;AACrB,4GAAA,kBAAkB,OAAA;AAClB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,iHAAA,uBAAuB,OAAA;AACvB,iHAAA,uBAAuB,OAAA;AACvB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,0BAA0B;AAC1B,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAChB,qGAAA,WAAW,OAAA;AACX,sGAAA,YAAY,OAAA;AACZ,6GAAA,mBAAmB,OAAA;AACnB,sGAAA,YAAY,OAAA;AACZ,uGAAA,aAAa,OAAA;AACb,qGAAA,WAAW,OAAA;AAGf,iEAAiE;AACjE,uCASoB;AAJhB,uGAAA,WAAW,OAAA;AACX,oGAAA,QAAQ,OAAA;AACR,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AAGhB,mBAAmB;AACnB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AACjC,iDAA+D;AAAtD,8GAAA,aAAa,OAAA;AACtB,6CAA6C;AAApC,2GAAA,YAAY,OAAA;AAErB,UAAU;AACV,2CAA0C;AAAjC,wGAAA,UAAU,OAAA","sourcesContent":["/**\n * @webpieces/http-api\n *\n * Core HTTP API definition package.\n * Contains decorators and utilities for defining HTTP APIs.\n *\n * This package is used by:\n * - @webpieces/http-routing (server-side): Routes HTTP requests to controllers\n * - @webpieces/http-client (client-side): Generates HTTP clients from API definitions\n *\n * Architecture:\n * ```\n * http-api (defines the contract)\n * ↑\n * ├── http-routing (server: contract → handlers)\n * └── http-client (client: contract → HTTP requests)\n * ```\n */\n\n// API definition decorators\nexport {\n ApiInterface,\n Get,\n Post,\n Put,\n Delete,\n Patch,\n Path,\n getRoutes,\n isApiInterface,\n RouteMetadata,\n METADATA_KEYS,\n} from './decorators';\n\n// Type validators\nexport { ValidateImplementation } from './validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpVendorError,\n HttpUserError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './datetime';\n\n// Platform Headers\nexport { PlatformHeader } from './PlatformHeader';\nexport { PlatformHeadersExtension } from './PlatformHeadersExtension';\nexport { HeaderMethods, ContextReader } from './HeaderMethods';\nexport { HEADER_TYPES } from './HeaderTypes';\n\n// Logging\nexport { LogApiCall } from './LogApiCall';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-api/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAEH,4BAA4B;AAC5B,2CAasB;AAZlB,qGAAA,OAAO,OAAA;AACP,sGAAA,QAAQ,OAAA;AACR,4GAAA,cAAc,OAAA;AACd,kHAAA,oBAAoB,OAAA;AACpB,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,uGAAA,SAAS,OAAA;AACT,yGAAA,WAAW,OAAA;AACX,6HAAA,+BAA+B,OAAA;AAC/B,sGAAA,QAAQ,OAAA;AACR,2GAAA,aAAa,OAAA;AACb,2GAAA,aAAa,OAAA;AAMjB,cAAc;AACd,mCAuBkB;AAtBd,uGAAA,aAAa,OAAA;AACb,mGAAA,SAAS,OAAA;AACT,2GAAA,iBAAiB,OAAA;AACjB,+GAAA,qBAAqB,OAAA;AACrB,6GAAA,mBAAmB,OAAA;AACnB,+GAAA,qBAAqB,OAAA;AACrB,4GAAA,kBAAkB,OAAA;AAClB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,iHAAA,uBAAuB,OAAA;AACvB,iHAAA,uBAAuB,OAAA;AACvB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,0BAA0B;AAC1B,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAChB,qGAAA,WAAW,OAAA;AACX,sGAAA,YAAY,OAAA;AACZ,6GAAA,mBAAmB,OAAA;AACnB,sGAAA,YAAY,OAAA;AACZ,uGAAA,aAAa,OAAA;AACb,qGAAA,WAAW,OAAA;AAGf,iEAAiE;AACjE,uCASoB;AAJhB,uGAAA,WAAW,OAAA;AACX,oGAAA,QAAQ,OAAA;AACR,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AAGhB,mBAAmB;AACnB,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AACjC,iDAA+D;AAAtD,8GAAA,aAAa,OAAA;AACtB,6CAA6C;AAApC,2GAAA,YAAY,OAAA;AAErB,UAAU;AACV,2CAA0C;AAAjC,wGAAA,UAAU,OAAA","sourcesContent":["/**\n * @webpieces/http-api\n *\n * Core HTTP API definition package.\n * Contains decorators and utilities for defining HTTP APIs.\n *\n * This package is used by:\n * - @webpieces/http-routing (server-side): Routes HTTP requests to controllers\n * - @webpieces/http-client (client-side): Generates HTTP clients from API definitions\n *\n * Architecture:\n * ```\n * http-api (defines the contract)\n * ↑\n * ├── http-routing (server: contract → handlers)\n * └── http-client (client: contract → HTTP requests)\n * ```\n */\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n Authentication,\n AuthenticationConfig,\n getApiPath,\n getEndpoints,\n isApiPath,\n getAuthMeta,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './decorators';\n\n// Type validators\nexport { ValidateImplementation } from './validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpVendorError,\n HttpUserError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './errors';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './datetime';\n\n// Platform Headers\nexport { PlatformHeader } from './PlatformHeader';\nexport { PlatformHeadersExtension } from './PlatformHeadersExtension';\nexport { HeaderMethods, ContextReader } from './HeaderMethods';\nexport { HEADER_TYPES } from './HeaderTypes';\n\n// Logging\nexport { LogApiCall } from './LogApiCall';\n"]}