@treatwell/moleculer-call-wrapper 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Treatwell
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # moleculer-call-wrapper
2
+
3
+ [![](https://cdn1.treatwell.net/images/view/v2.i1756348.w200.h50.x4965194E.jpeg)](https://treatwell.com/tech)
4
+
5
+ [![npm](https://img.shields.io/npm/v/@treatwell/moleculer-call-wrapper?style=flat-square)](https://www.npmjs.com/package/@treatwell/moleculer-call-wrapper)
6
+
7
+ This plugin intends to generate a TS file exporting a `call` function to replace `ctx.call` in your [moleculer](https://github.com/moleculerjs/moleculer) project
8
+ when using the [`@treatwell/moleculer-essentials`](https://github.com/treatwell/moleculer-essentials) package.
9
+
10
+ ## Purpose
11
+
12
+ In _moleculer_, when you want to call an action from another service, you use `ctx.call('service.action', params)`.
13
+ With TypeScript, you don't have any type safety on the action name or the params you pass to it.
14
+
15
+ By using `@treatwell/moleculer-essentials`, you can safely define your actions with types, but
16
+ you still don't have type safety when calling them.
17
+
18
+ This package solves this by generating a `call` function with the correct types for each action in your project.
19
+ Here is an example of how it looks like:
20
+
21
+ ```ts
22
+ // test.service.ts
23
+ import type { Context } from 'moleculer';
24
+ import { wrapService } from '@treatwell/moleculer-essentials';
25
+
26
+ export default wrapService({
27
+ name: 'test',
28
+ actions: {
29
+ simpleaction: {
30
+ async handler(ctx: Context<void>): Promise<void> {
31
+ // ...
32
+ },
33
+ },
34
+ withparams: {
35
+ async handler(ctx: Context<{ id: number }>): Promise<void> {},
36
+ },
37
+
38
+ withresponse: {
39
+ async handler(ctx: Context<void>): Promise<{ name: string }> {
40
+ return { name: 'test' };
41
+ },
42
+ },
43
+
44
+ withboth: {
45
+ async handler(ctx: Context<{ id: number }>): Promise<{ name: string }> {
46
+ return { name: 'test' };
47
+ },
48
+ },
49
+
50
+ withtemplate: {
51
+ async handler<T extends 'test' | 'other'>(
52
+ ctx: Context<{ id: number; type: T }>,
53
+ ): Promise<{ name: T }> {
54
+ return { name: 'test' as T };
55
+ },
56
+ },
57
+ },
58
+ });
59
+ ```
60
+
61
+ ```ts
62
+ /* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unused-vars */
63
+ import type * as m from 'moleculer';
64
+
65
+ interface Actions {
66
+ 'test.withparams': [{ id: number }, unknown];
67
+ 'test.withboth': [{ id: number }, { name: string }];
68
+ }
69
+ interface ActionsU {
70
+ 'test.simpleaction': void;
71
+ 'test.withresponse': { name: string };
72
+ }
73
+
74
+ export function call<N extends keyof Actions>(
75
+ ctx: m.Context,
76
+ action: N,
77
+ params: Actions[N][0],
78
+ meta?: m.CallingOptions,
79
+ ): Promise<Actions[N][1]>;
80
+ export function call<N extends keyof ActionsU>(
81
+ ctx: m.Context,
82
+ action: N,
83
+ params?: undefined,
84
+ meta?: m.CallingOptions,
85
+ ): Promise<ActionsU[N]>;
86
+ export function call(
87
+ ctx: m.Context,
88
+ action: string,
89
+ params: unknown,
90
+ meta?: m.CallingOptions,
91
+ ): Promise<unknown> {
92
+ return ctx.call(action, params, meta);
93
+ }
94
+
95
+ export function callT<
96
+ T extends 'test' | 'other',
97
+ N extends string = 'test.withtemplate',
98
+ >(
99
+ ctx: m.Context,
100
+ action: N,
101
+ params: N extends 'test.withtemplate' ? { id: number; type: T } : never,
102
+ meta?: m.CallingOptions,
103
+ ): Promise<{ name: T }>;
104
+ export function callT(
105
+ ctx: m.Context,
106
+ action: string,
107
+ params: unknown,
108
+ meta?: m.CallingOptions,
109
+ ): Promise<unknown> {
110
+ return ctx.call(action, params, meta);
111
+ }
112
+ ```
113
+
114
+ Then you can just use it like this:
115
+
116
+ ```ts
117
+ import { call, callT } from './call';
118
+ // ...
119
+
120
+ const res = await call(ctx, 'test.withresponse');
121
+
122
+ const tRes = await callT(ctx, 'test.withtemplate', {
123
+ id: 1,
124
+ type: 'test',
125
+ });
126
+ ```
127
+
128
+ ## Installation
129
+
130
+ Install `moleculer-call-wrapper` with your package manager:
131
+
132
+ ```bash
133
+ yarn add -D @treatwell/moleculer-call-wrapper
134
+ ```
135
+
136
+ ## Usage
137
+
138
+ To generate the wrapper file, you need to call the `createWrapperCall` function exported by the package and provide:
139
+
140
+ - `wrapperPath`: the path where you want to generate the file (e.g. `src/call.ts`)
141
+ - `services`: An array of _moleculer_ services (result from `broker.loadService(file)` for example)
142
+ - `svcFiles`: An array of those services file paths (**MUST** be in the same order as `services`)
143
+ - `additionalBuiltins`: An array of functions allowing you to add additional actions manually (see below)
144
+
145
+ Example:
146
+
147
+ ```ts
148
+ import * as path from 'path';
149
+ import * as fs from 'fs';
150
+ import fg from 'fast-glob';
151
+ import {
152
+ createServiceBroker,
153
+ HealthCheckMiddleware,
154
+ createLoggerConfig,
155
+ defaultLogger,
156
+ getMetadataFromService,
157
+ isServiceSelected,
158
+ Selector,
159
+ } from '@treatwell/moleculer-essentials';
160
+
161
+ async function run() {
162
+ // In your case, you would probably use glob or similar to find your service files
163
+ const serviceFiles = ['src/services/test.service.ts'];
164
+
165
+ const broker = createServiceBroker({});
166
+ const services = serviceFiles.map(f => broker.loadService(f));
167
+
168
+ // This should be done on dev mode only, not in production
169
+ if (process.env.MOLECULER_CALL_WRAPPER === 'yes') {
170
+ import('@treatwell/moleculer-call-wrapper')
171
+ .then(async ({ createWrapperCall }) => {
172
+ return createWrapperCall(
173
+ './lib/call.ts',
174
+ services,
175
+ serviceFiles,
176
+ additionalBuiltins,
177
+ );
178
+ })
179
+ .catch(err => {
180
+ broker.logger.error('Error while creating call wrapper', err);
181
+ });
182
+ }
183
+
184
+ await broker.start();
185
+ }
186
+
187
+ run().catch(err => {
188
+ defaultLogger.error('Error while starting server', { err });
189
+ process.exit(1);
190
+ });
191
+ ```
192
+
193
+ ## Builtins
194
+
195
+ Mixins can't be understood by the plugin natively. As a workaround,
196
+ it will try to match known mixins and generate related types for them.
197
+
198
+ It **only** concerns mixins that generates actions.
199
+
200
+ Mixins (namely the DatabaseMixin) present in the `@treatwell/moleculer-essentials` package are automatically included.
201
+
202
+ ### Create your own builtins
203
+
204
+ To create your own builtins, you can take a look at this [file](./src/builtins/db-mixin-v2.ts).
205
+
206
+ The idea is to first check if the service is using the related mixin.
207
+ If it is, the builtin will have to:
208
+
209
+ - Fill the related action (in the `actions` array) with TS types.
210
+ - Add any imports used in those TS types with the `addDepToImports` function to the `context.imports` map.
211
+
212
+ To help you with TS factory and AST, you can use https://ts-ast-viewer.com/
213
+
214
+ ## License
215
+
216
+ [MIT](https://choosealicense.com/licenses/mit/)