@rest-vir/implement-service 0.13.0 → 0.15.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.
@@ -11,10 +11,10 @@ import { type ContextInit } from './service-context-init.js';
11
11
  * @category Package : @rest-vir/implement-service
12
12
  * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
13
13
  */
14
- export type ImplementedEndpoint<Context = any, ServiceName extends string = any, SpecificEndpoint extends EndpointDefinition = GenericEndpointDefinition> = Overwrite<SpecificEndpoint, {
14
+ export type ImplementedEndpoint<Context = any, SpecificEndpoint extends EndpointDefinition = GenericEndpointDefinition, ServiceName extends string = any> = Overwrite<SpecificEndpoint, {
15
15
  service: GenericServiceImplementation;
16
16
  }> & {
17
- implementation: EndpointImplementation<Context, ServiceName>;
17
+ implementation: EndpointImplementation<Context, NoParam, ServiceName>;
18
18
  };
19
19
  /**
20
20
  * A super generic service implementation that be assigned to from any concrete service
@@ -51,9 +51,9 @@ export type EndpointImplementationOutput<ResponseDataType = unknown> = EndpointI
51
51
  * @category Package : @rest-vir/implement-service
52
52
  * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
53
53
  */
54
- export type EndpointImplementationParams<Context = any, ServiceName extends string = any, SpecificEndpoint extends EndpointDefinition | NoParam = NoParam> = {
54
+ export type EndpointImplementationParams<Context = any, SpecificEndpoint extends EndpointDefinition | NoParam = NoParam, ServiceName extends string = any> = {
55
55
  pathParams: SpecificEndpoint extends NoParam ? Readonly<Record<string, string>> : PathParams<Exclude<SpecificEndpoint, NoParam>['path']> extends string ? Readonly<Record<PathParams<Exclude<SpecificEndpoint, NoParam>['path']>, string>> : Readonly<Record<string, string>>;
56
- context: Context;
56
+ context: NoInfer<Context>;
57
57
  method: IsEqual<Extract<SpecificEndpoint, NoParam>, NoParam> extends true ? HttpMethod : ExtractKeysWithMatchingValues<Exclude<SpecificEndpoint, NoParam>['methods'], true> extends infer AvailableMethod ? IsNever<AvailableMethod> extends true ? HttpMethod : AvailableMethod : never;
58
58
  endpoint: IsEqual<Extract<SpecificEndpoint, NoParam>, NoParam> extends true ? EndpointDefinition : SpecificEndpoint;
59
59
  service: MinimalService<ServiceName>;
@@ -106,7 +106,7 @@ export type GenericEndpointImplementationParams = {
106
106
  * @category Package : @rest-vir/implement-service
107
107
  * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
108
108
  */
109
- export type EndpointImplementation<Context = any, ServiceName extends string = any, SpecificEndpoint extends EndpointDefinition | NoParam = NoParam> = IsEqual<Extract<SpecificEndpoint, NoParam>, NoParam> extends true ? (params: GenericEndpointImplementationParams) => any : (params: Readonly<EndpointImplementationParams<Context, ServiceName, SpecificEndpoint>>) => MaybePromise<EndpointImplementationOutput<WithFinalEndpointProps<SpecificEndpoint, any>['ResponseType']>>;
109
+ export type EndpointImplementation<Context = any, SpecificEndpoint extends EndpointDefinition | NoParam = NoParam, ServiceName extends string = any> = IsEqual<Extract<SpecificEndpoint, NoParam>, NoParam> extends true ? (params: GenericEndpointImplementationParams) => any : (params: Readonly<EndpointImplementationParams<NoInfer<Context>, SpecificEndpoint, ServiceName>>) => MaybePromise<EndpointImplementationOutput<WithFinalEndpointProps<SpecificEndpoint, any>['ResponseType']>>;
110
110
  /**
111
111
  * All endpoint implementations to match the service definition's endpoints.
112
112
  *
@@ -114,8 +114,8 @@ export type EndpointImplementation<Context = any, ServiceName extends string = a
114
114
  * @category Package : @rest-vir/implement-service
115
115
  * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
116
116
  */
117
- export type EndpointImplementations<Context = any, ServiceName extends string = any, EndpointsInit extends BaseServiceEndpointsInit | NoParam = NoParam> = EndpointsInit extends NoParam ? Record<EndpointPathBase, EndpointImplementation> : {
118
- [EndpointPath in keyof EndpointsInit]: EndpointsInit[EndpointPath] extends EndpointInit ? EndpointPath extends EndpointPathBase ? EndpointImplementation<Context, ServiceName, WithFinalEndpointProps<EndpointsInit[EndpointPath], EndpointPath>> : never : never;
117
+ export type EndpointImplementations<Context = any, EndpointsInit extends BaseServiceEndpointsInit | NoParam = NoParam, ServiceName extends string = any> = EndpointsInit extends NoParam ? Record<EndpointPathBase, EndpointImplementation> : {
118
+ [EndpointPath in keyof EndpointsInit]: EndpointsInit[EndpointPath] extends EndpointInit ? EndpointPath extends EndpointPathBase ? EndpointImplementation<NoInfer<Context>, WithFinalEndpointProps<EndpointsInit[EndpointPath], EndpointPath>, ServiceName> : never : never;
119
119
  };
120
120
  /**
121
121
  * Asserts that all endpoint implementations are valid.
@@ -46,7 +46,7 @@ export type ServiceImplementationInit<Context, ServiceName extends string, Endpo
46
46
  export type ServiceImplementationsParams<Context, ServiceName extends string, EndpointsInit extends BaseServiceEndpointsInit, WebSocketsInit extends BaseServiceWebSocketsInit> = (KeyCount<OmitIndexSignature<EndpointsInit>> extends 0 ? {
47
47
  endpoints?: never;
48
48
  } : {
49
- endpoints: EndpointImplementations<NoInfer<Context>, NoInfer<ServiceName>, NoInfer<EndpointsInit>>;
49
+ endpoints: EndpointImplementations<NoInfer<Context>, NoInfer<EndpointsInit>, NoInfer<ServiceName>>;
50
50
  }) & (KeyCount<OmitIndexSignature<WebSocketsInit>> extends 0 ? {
51
51
  webSockets?: never;
52
52
  } : {
@@ -56,15 +56,15 @@ export type ServiceImplementationsParams<Context, ServiceName extends string, En
56
56
  * Creates an implemented service that is fully ready to be run as a server by attaching endpoint
57
57
  * implementations to the given {@link ServiceDefinition}.
58
58
  *
59
- * This can _only_ be run in backend code.
59
+ * This should _only_ be run in backend code.
60
60
  *
61
61
  * @category Implement Service
62
62
  * @category Package : @rest-vir/implement-service
63
63
  * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
64
64
  */
65
- export declare function implementService<const ServiceName extends string, const EndpointsInit extends BaseServiceEndpointsInit, const WebSocketsInit extends BaseServiceWebSocketsInit, const Context = undefined>(
65
+ export declare function implementService<const ServiceName extends string, const EndpointsInit extends BaseServiceEndpointsInit, const WebSocketsInit extends BaseServiceWebSocketsInit, Context = undefined>(
66
66
  /** Init must be first so that TypeScript can infer the type for `Context`. */
67
- { service, createContext, logger, customHeaders, }: ServiceImplementationInit<Context, ServiceName, EndpointsInit, WebSocketsInit>, { endpoints: endpointImplementations, webSockets: webSocketImplementations, }: ServiceImplementationsParams<NoInfer<Context>, NoInfer<ServiceName>, NoInfer<EndpointsInit>, NoInfer<WebSocketsInit>>): ServiceImplementation<Context, ServiceName, EndpointsInit, WebSocketsInit>;
67
+ { service, createContext, logger, customHeaders, }: ServiceImplementationInit<Context, ServiceName, EndpointsInit, WebSocketsInit>): (implementations: ServiceImplementationsParams<NoInfer<Context>, NoInfer<ServiceName>, NoInfer<EndpointsInit>, NoInfer<WebSocketsInit>>) => ServiceImplementation<NoInfer<Context>, ServiceName, EndpointsInit, WebSocketsInit>;
68
68
  /**
69
69
  * A finalized service implementation created by {@link implementService}.
70
70
  *
@@ -72,13 +72,14 @@ export declare function implementService<const ServiceName extends string, const
72
72
  * @category Package : @rest-vir/implement-service
73
73
  * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
74
74
  */
75
- export type ServiceImplementation<Context = any, ServiceName extends string = any, EndpointsInit extends BaseServiceEndpointsInit | NoParam = NoParam, WebSocketsInit extends BaseServiceWebSocketsInit | NoParam = NoParam> = Omit<ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>, 'endpoints' | 'webSockets'> & {
75
+ export type ServiceImplementation<Context = undefined, ServiceName extends string = any, EndpointsInit extends BaseServiceEndpointsInit | NoParam = NoParam, WebSocketsInit extends BaseServiceWebSocketsInit | NoParam = NoParam> = Omit<ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>, 'endpoints' | 'webSockets'> & {
76
76
  endpoints: {
77
- [EndpointPath in keyof ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>['endpoints']]: EndpointPath extends EndpointPathBase ? ImplementedEndpoint<Context, ServiceName, ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>['endpoints'][EndpointPath]> : never;
77
+ [EndpointPath in keyof ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>['endpoints']]: EndpointPath extends EndpointPathBase ? ImplementedEndpoint<Context, ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>['endpoints'][EndpointPath], ServiceName> : never;
78
78
  };
79
79
  webSockets: {
80
80
  [WebSocketPath in keyof ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>['webSockets']]: WebSocketPath extends EndpointPathBase ? ImplementedWebSocket<Context, ServiceName, ServiceDefinition<ServiceName, EndpointsInit, WebSocketsInit>['webSockets'][WebSocketPath]> : never;
81
81
  };
82
+ ContextType: Context;
82
83
  createContext: ContextInit<Context, ServiceName, EndpointsInit, WebSocketsInit> | undefined;
83
84
  logger: ServiceLogger;
84
85
  customHeaders: string[];
@@ -7,7 +7,7 @@ import { assertValidWebSocketImplementations, } from './implement-web-socket.js'
7
7
  * Creates an implemented service that is fully ready to be run as a server by attaching endpoint
8
8
  * implementations to the given {@link ServiceDefinition}.
9
9
  *
10
- * This can _only_ be run in backend code.
10
+ * This should _only_ be run in backend code.
11
11
  *
12
12
  * @category Implement Service
13
13
  * @category Package : @rest-vir/implement-service
@@ -15,50 +15,61 @@ import { assertValidWebSocketImplementations, } from './implement-web-socket.js'
15
15
  */
16
16
  export function implementService(
17
17
  /** Init must be first so that TypeScript can infer the type for `Context`. */
18
- { service, createContext, logger, customHeaders, }, { endpoints: endpointImplementations, webSockets: webSocketImplementations, }) {
19
- assertValidEndpointImplementations(service, endpointImplementations || {});
20
- assertValidWebSocketImplementations(service, webSocketImplementations || {});
21
- const endpoints = mapObjectValues(service.endpoints, (endpointPath, endpoint) => {
22
- const implementation = endpointImplementations?.[endpointPath];
23
- assert.isDefined(implementation);
24
- assert.isNotString(implementation);
25
- /**
26
- * Note: this return object is actually wrong. The service property will not be correct as
27
- * the `endpoint` here only has the minimal service. Below, after `serviceImplementation` is
28
- * created, we attach the correct service to all endpoints.
29
- */
30
- return {
31
- ...endpoint,
32
- implementation,
18
+ { service, createContext, logger, customHeaders, }) {
19
+ return ({ endpoints: endpointImplementations, webSockets: webSocketImplementations }) => {
20
+ assertValidEndpointImplementations(service, endpointImplementations || {});
21
+ assertValidWebSocketImplementations(service, webSocketImplementations || {});
22
+ const endpoints = mapObjectValues(service.endpoints, (endpointPath, endpoint) => {
23
+ const implementation = endpointImplementations?.[endpointPath];
24
+ assert.isDefined(implementation);
25
+ assert.isNotString(implementation);
26
+ /**
27
+ * Note: this return object is actually wrong. The service property will not be correct
28
+ * as the `endpoint` here only has the minimal service. Below, after
29
+ * `serviceImplementation` is created, we attach the correct service to all endpoints.
30
+ */
31
+ return {
32
+ ...endpoint,
33
+ implementation,
34
+ };
35
+ });
36
+ const webSockets = mapObjectValues(service.webSockets, (webSocketPath, webSocketImplementation) => {
37
+ const implementation = webSocketImplementations?.[webSocketPath];
38
+ assert.isDefined(implementation);
39
+ assert.isNotString(webSocketImplementation);
40
+ /**
41
+ * Note: this return object is actually wrong. The service property will not be
42
+ * correct as the WebSocket here only has the minimal service. Below, after
43
+ * `serviceImplementation` is created, we attach the correct service to all
44
+ * WebSockets.
45
+ */
46
+ return {
47
+ ...webSocketImplementation,
48
+ implementation,
49
+ };
50
+ });
51
+ const serviceImplementation = {
52
+ ...service,
53
+ customHeaders: customHeaders || [],
54
+ endpoints,
55
+ webSockets,
56
+ createContext,
57
+ logger: createServiceLogger(logger),
33
58
  };
34
- });
35
- const webSockets = mapObjectValues(service.webSockets, (webSocketPath, webSocketImplementation) => {
36
- const implementation = webSocketImplementations?.[webSocketPath];
37
- assert.isDefined(implementation);
38
- assert.isNotString(webSocketImplementation);
39
- /**
40
- * Note: this return object is actually wrong. The service property will not be correct
41
- * as the WebSocket here only has the minimal service. Below, after
42
- * `serviceImplementation` is created, we attach the correct service to all WebSockets.
43
- */
44
- return {
45
- ...webSocketImplementation,
46
- implementation,
47
- };
48
- });
49
- const serviceImplementation = {
50
- ...service,
51
- customHeaders: customHeaders || [],
52
- endpoints,
53
- webSockets,
54
- createContext,
55
- logger: createServiceLogger(logger),
59
+ Object.defineProperties(serviceImplementation, {
60
+ ContextType: {
61
+ enumerable: false,
62
+ get() {
63
+ throw new Error('.ContextType should not be used as a value.');
64
+ },
65
+ },
66
+ });
67
+ Object.values(endpoints).forEach((endpoint) => {
68
+ endpoint.service = serviceImplementation;
69
+ });
70
+ Object.values(webSockets).forEach((webSocket) => {
71
+ webSocket.service = serviceImplementation;
72
+ });
73
+ return serviceImplementation;
56
74
  };
57
- Object.values(endpoints).forEach((endpoint) => {
58
- endpoint.service = serviceImplementation;
59
- });
60
- Object.values(webSockets).forEach((webSocket) => {
61
- webSocket.service = serviceImplementation;
62
- });
63
- return serviceImplementation;
64
75
  }
@@ -1,17 +1,17 @@
1
1
  import { HttpMethod, MaybePromise, Values } from '@augment-vir/common';
2
2
  import { type BaseServiceEndpointsInit, type BaseServiceWebSocketsInit, type EndpointDefinition, type EndpointPathBase, type MinimalService, type NoParam, type WebSocketDefinition, type WithFinalEndpointProps, type WithFinalWebSocketProps } from '@rest-vir/define-service';
3
3
  import { type IncomingHttpHeaders } from 'node:http';
4
- import { type RequireExactlyOne } from 'type-fest';
4
+ import type { RequireExactlyOne } from 'type-fest';
5
5
  import { type ServerRequest, type ServerResponse } from '../util/data.js';
6
6
  import { EndpointImplementationErrorOutput, type RunningServerInfo } from './implement-endpoint.js';
7
7
  /**
8
- * User-defined service implementation Context generator.
8
+ * Output of {@link ContextInit}.
9
9
  *
10
10
  * @category Internal
11
11
  * @category Package : @rest-vir/implement-service
12
12
  * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
13
13
  */
14
- export type ContextInit<Context, ServiceName extends string, EndpointsInit extends BaseServiceEndpointsInit | NoParam, WebSocketsInit extends BaseServiceWebSocketsInit | NoParam> = (params: Readonly<ContextInitParameters<ServiceName, EndpointsInit, WebSocketsInit>>) => MaybePromise<RequireExactlyOne<{
14
+ export type ContextInitOutput<Context> = RequireExactlyOne<{
15
15
  /** The context created for this request. */
16
16
  context: Context;
17
17
  /**
@@ -19,7 +19,15 @@ export type ContextInit<Context, ServiceName extends string, EndpointsInit exten
19
19
  * with the specified status code and other options.
20
20
  */
21
21
  reject: EndpointImplementationErrorOutput;
22
- }>>;
22
+ }>;
23
+ /**
24
+ * User-defined service implementation Context generator.
25
+ *
26
+ * @category Internal
27
+ * @category Package : @rest-vir/implement-service
28
+ * @package [`@rest-vir/implement-service`](https://www.npmjs.com/package/@rest-vir/implement-service)
29
+ */
30
+ export type ContextInit<Context, ServiceName extends string, EndpointsInit extends BaseServiceEndpointsInit | NoParam, WebSocketsInit extends BaseServiceWebSocketsInit | NoParam> = (params: Readonly<ContextInitParameters<ServiceName, EndpointsInit, WebSocketsInit>>) => MaybePromise<ContextInitOutput<Context>>;
23
31
  /**
24
32
  * Parameters for {@link ContextInit}.
25
33
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rest-vir/implement-service",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Implement a service defined by @rest-vir/define-service.",
5
5
  "keywords": [
6
6
  "rest",
@@ -39,16 +39,16 @@
39
39
  "test:update": "npm test update"
40
40
  },
41
41
  "dependencies": {
42
- "@augment-vir/assert": "^31.11.0",
43
- "@augment-vir/common": "^31.11.0",
44
- "@rest-vir/define-service": "^0.13.0",
42
+ "@augment-vir/assert": "^31.14.1",
43
+ "@augment-vir/common": "^31.14.1",
44
+ "@rest-vir/define-service": "^0.15.0",
45
45
  "@types/ws": "^8.18.1",
46
- "fastify": "^5.3.0",
46
+ "fastify": "^5.3.2",
47
47
  "path-to-regexp": "^8.2.0",
48
48
  "type-fest": "^4.40.0"
49
49
  },
50
50
  "devDependencies": {
51
- "@augment-vir/test": "^31.11.0",
51
+ "@augment-vir/test": "^31.14.1",
52
52
  "@types/node": "^22.14.1",
53
53
  "c8": "^10.1.3",
54
54
  "istanbul-smart-text-reporter": "^1.1.5",