UNPKG

12.4 kBMarkdownView Raw
1# API Reference
2
3## ModuleProxy
4
5```typescript
6interface ModuleProxy<T> {}
7```
8
9Once Alar is imported to the project, this interface will be presented under
10the global scope, and can be used everywhere.
11
12The interface has the following properties and methods (deprecated items are not
13listed):
14
15- `name: string` The name (with namespace) of the module.
16- `path: string` The path (without extension) of the module.
17- `exports: any` The very `exports` object of the module.
18- `proto: T` The very prototype of the module.
19- `ctor: typeof T extends Function ? T : new (...args: any[]) => T` The very
20 class constructor of the module.
21- `create(...args: any[]): T` Creates a new instance of the module.
22- `instance(route?: any): T` Gets the local singleton or a remote instance of
23 the module, if connected to one or more remote instances, the module proxy
24 will automatically calculate the `route` and direct the traffic to the
25 corresponding remote instance. If the given route matches the server ID of
26 any remote service, the corresponding instance will be returned instead.
27- `fallbackToLocal(enable: boolean): this` If the module is registered as a
28 remote service, however, none of the RPC channel is available, allow calls
29 to fallback to the local instance, which is the default behavior, this
30 method is used to disable (pass `false`) and re-enable (pass `true`) this
31 behavior.
32
33**NOTE: RPC calling will serialize all input and output data, those data that**
34**cannot be serialized will be lost during transmission.**
35
36**NOTE: properties cannot be accessed remotely, if trying so, `null` or**
37**`undefined` will be returned instead, so it's better to declare properties**
38**`protected` or `private` in any service class that may potentially served**
39**remotely.**
40
41**CHANGE: Since v5.0, every method called from `instance()` is wrapped**
42**asynchronous, regardless of local call or remote call.**
43
44**CHANGE: Since v5.0, a module class with parameters must use the signature**
45**`ModuleProxy<typeof T>` in order to provide correct type hint for**
46**`create()` function.**
47
48**CHANGE: Since v5.4, the module now can be called as a function, and**
49**it acts just the same as calling `instance()` function.**
50
51**CHANGE: Since v5.5, the module now can be called as a class constructor,**
52**and it acts just the same as calling `create()` function.**
53
54**CHANGE: Since v6.0 `noLocal()` method has be deprecated in favor of**
55**`fallbackToLocal(false)`.**
56
57**CHANGE: v6.0 fix the forced asynchronous behavior added in v5.0, now only**
58**remote calls and the calls fell back to local are forced asynchronous.**
59
60# ModuleProxy (class)
61
62```typescript
63class ModuleProxy {
64 constructor(name: string, path: string, loader?: ModuleLoader);
65}
66```
67
68This class must be imported in order to create a root module proxy, and the root
69module should be declared as a namespace under the global scope, in TypeScript,
70the following steps must be walked through for Alar to work in a project.
71
72```typescript
73import { ModuleProxy } from "alar";
74
75// This statement creates a root module and assign it to the global scope in
76// NodeJS.
77export const App = global["app"] = new ModuleProxy("app", __dirname);
78
79// This declaration merging creates a namespace app under the global scope in
80// TypeScript, so you can use it everywhere for type hint and type check.
81declare global {
82 namespace app { }
83}
84```
85
86This class has the following extra properties and methods:
87
88- `local: symbol` If passed to the `ModuleProxy<T>.instance()`, the method will
89 always return the local instance.
90- `serve(config: string | RpcOptions, immediate?: boolean): Promise<RpcServer>`
91 Serves an RPC service according to the given configuration. `immediate` sets
92 whether to open the channel immediately after creating the server, it's set
93 `true` by default. However, if you want to do some preparations and register
94 modules before serving, set it to `false`, and call `RpcServer.open()`
95 manually.
96- `connect(config: string | ClientOptions, immediate?: boolean): Promise<RpcClient>`
97 Connects an RPC service according to the given configuration. `immediate`
98 sets whether to open the channel immediately after creating the client, it's
99 set `true` by default. However, if you want to do some preparations and
100 register modules before connecting, set it to `false`, and call
101 `RpcClient.open()` manually.
102- `resolve(path: string): string` Resolves the given path to a module name.
103- `watch(listener?: (event: "change" | "unlink", filename: string)): FSWatcher`
104 Watches file change and reload the corresponding module.
105 - `listener` if provided, it will be called after the module cache has been
106 cleared.
107 - `FSWatcher` is a type exposed by
108 [chokidar](https://github.com/paulmillr/chokidar).
109- `setLoader(loader: ModuleLoader): void` Sets a custom loader to resolve the
110 module.
111
112**CHANGE: Since v5.4, class `ModuleProxy` now takes a third optional parameter**
113**to set the loader when instantiating.**
114
115**CHANGE: Since v6.0, `ModuleProxy.serve()` and `ModuleProxy.connect()` now**
116**take a second argument to suggest whether the channel should open immediately.**
117
118## ModuleLoader
119
120```typescript
121export interface ModuleLoader {
122 extension: string | string[],
123 load(filename: string): any;
124 unload(filename: string): void;
125}
126```
127
128By default, Alar supports JavaScript modules and (TypeScript modules in
129**ts-node**), By setting a custom loader, a ModuleProxy instance can resolve any
130kind of module wanted. (NOTE: The loader must provide cache support.)
131
132- `extension` Extension name of the module file, by default, it's `.js` (or `.ts`
133 in ts-node).
134- `load(filename: string): any` Loads module from the given file or cache.
135- `unload(filename: string): void` Unloads the module in the cache if the file
136 is modified.
137
138```typescript
139// Add a loader to resolve JSON modules.
140const json = new alar.ModuleProxy("json", __dirname + "/json");
141
142json.setLoader({
143 cache: {},
144 extension: ".json",
145 load(filename) {
146 return this.cache[filename] || (
147 this.cache[filename] = JSON.parse(fs.readFileSync(filename, "utf8"))
148 );
149 },
150 unload(filename) {
151 delete this.cache[filename];
152 }
153});
154```
155
156## createModuleProxy
157
158```ts
159export function createModuleProxy(
160 name: string,
161 path: string,
162 loader?: ModuleLoader,
163 singletons?: { [name: string]: any },
164 root?: ModuleProxy // class ModuleProxy, not the interface
165): ModuleProxy
166```
167
168Creates a module proxy manually. This function is used under the hood of Alar
169framework, however, if you want to create a module proxy whose file path is
170outside the root proxy, you can use this function to do so.
171
172## RpcOptions
173
174```typescript
175interface RpcOptions {
176 [x: string]: any,
177 host?: string;
178 port?: number;
179 path?: string;
180 secret?: string;
181 id?: string;
182 codec?: "CLONE" | "JSON" | "BSON" | "FRON"
183}
184```
185
186If `path` is provided (equivalent to `ModuleProxy.serve(config: string)` and
187`ModuleProxy.connect(config: string)`), the RPC channel will be bound to an IPC
188channel. Otherwise, the RPC channel will be bound to a network channel according
189to the `host` and `port`.
190
191`secret` is used as a password for authentication, if used, the client must
192provide it as well in order to grant permission to connect.
193
194The `id` property is a little ambiguous. On the server-side, if omitted, it will
195fall back to `dsn`, used for the client routing requests. On the client-side, if
196omitted, a random string will be generated, used for the server publishing
197topics.
198
199The `codec` property sets in what format should the data be transferred. Since
200v5.2, Alar uses a new codec `CLONE` by default, it's based on `JSON` however
201with a structured clone of the original data, that means it supports more types
202than JSON do, like Date, RegExp, TypedArray, etc. For more information, see
203[@hyurl/structured-clone](https://github.com/hyurl/structured-clone).
204
205If set `BSON` or `FRON`, the following corresponding packages must be installed.
206
207- BSON: [bson](https://github.com/mongodb/js-bson)
208 or [bson-ext](https://github.com/mongodb-js/bson-ext);
209- FRON: [fron](https://github.com/hyurl/fron)
210
211## RpcChannel
212
213```typescript
214abstract class RpcChannel implements RpcOptions { }
215```
216
217This abstract class just indicates the RPC channel that allows modules to
218communicate remotely. methods `ModuleProxy.serve()` and `ModuleProxy.connect()`
219return its server and client implementations accordingly.
220
221The following properties and methods work in both implementations:
222
223- `id: string` The unique ID of the server or the client.
224- `dsn: string` Gets the data source name according to the configuration.
225- `open(): Promise<this>` Opens the channel. This method will be called
226 automatically by `ModuleProxy.serve()` and `ModuleProxy.connect()` if their
227 `immediate` argument is set `true`.
228- `close(): Promise<this>` Closes the channel.
229- `register<T>(mod: ModuleProxy<T>): this` Registers a module to the channel.
230- `onError(handler: (err: Error) => void)` Binds an error handler invoked
231 whenever an error occurred in asynchronous operations which can't be caught
232 during run-time.
233
234## RpcServer
235
236```typescript
237class RpcServer extends RpcChannel { }
238```
239
240The server implementation of the RPC channel.
241
242- `publish(topic: string, data: any, clients?: string[]): boolean` Publishes
243 data to the corresponding topic, if `clients` are provided, the topic will
244 only be published to them.
245- `getClients(): string[]` Returns all IDs of clients that connected to the
246 server.
247
248**CHANGE: Prior to v6.0, RpcServer.init() is used to perform initiation for**
249**registered modules, now the initiation process has been merged to**
250**`RpcServer.open()`.**
251
252## ClientOptions
253
254```typescript
255interface ClientOptions extends RpcOptions {
256 timeout?: number;
257 pingInterval?: number;
258 serverId?: string;
259}
260```
261
262By default `timeout` is set `5000`ms, it is used to force a timeout error when
263an RPC request fires and doesn't get a response after a long time.
264
265The client uses `pingInterval` to set a timer of ping function so that to
266ensure the connection is alive. If the server doesn't response when pinging, the
267client will consider the server is down and will destroy and retry the
268connection.
269
270By default, the `serverId` is automatically set according to the `dsn` of the
271server, and updated after finishing the connect. However, if an ID is set when
272serving the RPC server, it would be better to set `serverId` to that ID as well.
273
274### About Reconnection
275
276When the client detects the server is down or malfunction, it will destroy the
277connection positively and retry connect. Since v5.4, this feature uses an
278exponential back-off mechanism to retry connect rapidly until about 30 minutes
279timeout before considering the server is down permanently, and close the
280channel after that.
281
282**NOTE: prior to v5.4, reconnection tries timeout is 2 minutes.**
283
284## RpcClient
285
286```typescript
287class RpcClient extends RpcChannel implements ClientOptions { }
288```
289
290The client implementation of the RPC channel.
291
292- `connecting: boolean` Whether the channel is in connecting state.
293- `connected: boolean` Whether the channel is connected.
294- `closed: boolean` Whether the channel is closed.
295- `pause(): boolean` Pauses the channel and redirect traffic to other channels.
296- `resume(): boolean` Resumes the channel and continue handling traffic.
297- `subscribe(topic: string, handle: Subscriber): this` Subscribes a handle
298 function to the corresponding topic.
299- `unsubscribe(topic: string, handle?: Subscriber): boolean` Unsubscribes the
300 handle function or all handlers from the corresponding topic.
301
302The `Subscriber` is a type of
303
304```typescript
305type Subscriber = (data: any) => void | Promise<void>;
306```
307
308## Pub-Sub Model between the server and clients
309
310When the server publishes a message, all clients subscribe to the topic
311will receive the data and invoke their handlers, this mechanism is often used
312for the server to broadcast data to its clients.