orator 5.0.1 → 6.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.
@@ -0,0 +1,45 @@
1
+ # HTTP Proxy
2
+
3
+ The `orator-http-proxy` module provides HTTP proxy pass-through for Orator. It forwards incoming requests matching specified route prefixes to a destination URL, making it easy to proxy API calls to backend services.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install orator-http-proxy
9
+ ```
10
+
11
+ ```javascript
12
+ const libOratorHTTPProxy = require('orator-http-proxy');
13
+
14
+ _Fable.serviceManager.addServiceType('OratorHTTPProxy', libOratorHTTPProxy);
15
+ _Fable.serviceManager.instantiateServiceProvider('OratorHTTPProxy',
16
+ {
17
+ DestinationURL: 'http://backend-api:3000/',
18
+ RequestPrefixList: ['/api/v1/*']
19
+ });
20
+
21
+ // After Orator is initialized, connect the proxy routes
22
+ _Fable.OratorHTTPProxy.connectProxyRoutes();
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ | Setting | Type | Default | Description |
28
+ |---------|------|---------|-------------|
29
+ | `DestinationURL` | string | `"http://127.0.0.1/"` | URL to proxy requests to |
30
+ | `RequestPrefixList` | array | `["/1.0/*"]` | Route prefixes to intercept and proxy |
31
+ | `LogLevel` | number | `0` | Logging verbosity (higher = more output) |
32
+ | `httpProxyOptions` | object | `{}` | Additional options passed to `http-proxy` |
33
+
34
+ Configuration can also be set via Fable settings with the `OratorHTTPProxy` prefix:
35
+ - `OratorHTTPProxyDestinationURL`
36
+ - `OratorHTTPProxyRequestPrefixList`
37
+ - `OratorHTTPProxyLogLevel`
38
+
39
+ ## How It Works
40
+
41
+ For each prefix in `RequestPrefixList`, the proxy registers GET, PUT, POST, and DELETE handlers on the Orator service server. When a matching request arrives, it is forwarded to the `DestinationURL` using the `http-proxy` library.
42
+
43
+ ## Related
44
+
45
+ - [orator-http-proxy documentation](https://github.com/stevenvelozo/orator-http-proxy)
@@ -0,0 +1,39 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7
+ <meta name="description" content="Documentation powered by pict-docuserve">
8
+
9
+ <title>Documentation</title>
10
+
11
+ <!-- Application Stylesheet -->
12
+ <link href="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/css/docuserve.css" rel="stylesheet">
13
+ <!-- KaTeX stylesheet for LaTeX equation rendering -->
14
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
15
+ <!-- PICT Dynamic View CSS Container -->
16
+ <style id="PICT-CSS"></style>
17
+
18
+ <!-- Load the PICT library from jsDelivr CDN -->
19
+ <script src="https://cdn.jsdelivr.net/npm/pict@1/dist/pict.min.js" type="text/javascript"></script>
20
+ <!-- Bootstrap the Application -->
21
+ <script type="text/javascript">
22
+ //<![CDATA[
23
+ Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(PictDocuserve, 2)});
24
+ //]]>
25
+ </script>
26
+ </head>
27
+ <body>
28
+ <!-- The root container for the Pict application -->
29
+ <div id="Docuserve-Application-Container"></div>
30
+
31
+ <!-- Mermaid diagram rendering -->
32
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
33
+ <script>mermaid.initialize({ startOnLoad: false, theme: 'default' });</script>
34
+ <!-- KaTeX for LaTeX equation rendering -->
35
+ <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
36
+ <!-- Load the Docuserve PICT Application Bundle from jsDelivr CDN -->
37
+ <script src="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/pict-docuserve.min.js" type="text/javascript"></script>
38
+ </body>
39
+ </html>
@@ -0,0 +1,123 @@
1
+ # IPC Server
2
+
3
+ Orator includes a built-in IPC (Inter-Process Communication) service server that handles route invocation entirely in-process, without any network traffic. This is the default service server when no external implementation (like Restify) is registered.
4
+
5
+ ## When to Use IPC
6
+
7
+ - **Unit Testing** - Test your route handlers without starting an HTTP server
8
+ - **Microservice Composition** - Call routes programmatically within the same process
9
+ - **Browser Environments** - Orator's browser build uses IPC by default since there's no server to listen on
10
+
11
+ ## Automatic Setup
12
+
13
+ If you don't register an `OratorServiceServer` with Fable, Orator creates an IPC server automatically during initialization:
14
+
15
+ ```javascript
16
+ const libFable = require('fable');
17
+ const libOrator = require('orator');
18
+
19
+ const _Fable = new libFable({ Product: 'MyService' });
20
+ _Fable.serviceManager.addServiceType('Orator', libOrator);
21
+ _Fable.serviceManager.instantiateServiceProvider('Orator');
22
+
23
+ // serviceServer is now an IPC server
24
+ _Fable.Orator.initialize(
25
+ () =>
26
+ {
27
+ console.log(_Fable.Orator.serviceServer.ServiceServerType); // "IPC"
28
+ });
29
+ ```
30
+
31
+ ## Programmatic Invocation
32
+
33
+ The key feature of the IPC server is `invoke()`, which calls registered routes programmatically:
34
+
35
+ ```javascript
36
+ // Register a route
37
+ _Fable.Orator.serviceServer.get('/api/user/:id',
38
+ (pRequest, pResponse, fNext) =>
39
+ {
40
+ pResponse.send({ id: pRequest.params.id, name: 'Example User' });
41
+ return fNext();
42
+ });
43
+
44
+ // Invoke it without HTTP
45
+ _Fable.Orator.invoke('GET', '/api/user/42', {},
46
+ (pError, pResponseData) =>
47
+ {
48
+ console.log(pResponseData); // { id: '42', name: 'Example User' }
49
+ });
50
+ ```
51
+
52
+ ## Request and Response Objects
53
+
54
+ When invoking routes via IPC, the server creates synthesized request and response objects:
55
+
56
+ **Request Object:**
57
+ - `method` - The HTTP method string
58
+ - `url` - The route path
59
+ - `guid` - A unique identifier for the request
60
+ - `params` - Parsed URL parameters
61
+
62
+ **Response Object (Synthesized):**
63
+ - `send(pData)` - Accumulates response data
64
+ - `responseData` - The aggregated response after all handlers complete
65
+
66
+ ## Pre and Post Behavior Functions
67
+
68
+ The IPC server supports middleware through pre-behavior and post-behavior functions:
69
+
70
+ ```javascript
71
+ const tmpServiceServer = _Fable.Orator.serviceServer;
72
+
73
+ // Runs before every route handler
74
+ tmpServiceServer.addPreBehaviorFunction(
75
+ (pRequest, pResponse, fNext) =>
76
+ {
77
+ pRequest.startTime = Date.now();
78
+ return fNext();
79
+ });
80
+
81
+ // Runs after every route handler
82
+ tmpServiceServer.addPostBehaviorFunction(
83
+ (pRequest, pResponse, fNext) =>
84
+ {
85
+ let tmpDuration = Date.now() - pRequest.startTime;
86
+ console.log(`Request took ${tmpDuration}ms`);
87
+ return fNext();
88
+ });
89
+ ```
90
+
91
+ The `use()` method is an alias for `addPreBehaviorFunction()`.
92
+
93
+ ## Execution Flow
94
+
95
+ When a route is invoked via IPC, the execution follows this sequence:
96
+
97
+ ```
98
+ invoke(method, route, data)
99
+
100
+ Router matches route (find-my-way)
101
+
102
+ Execute pre-behavior functions (sequential)
103
+
104
+ Execute route handler functions (sequential)
105
+
106
+ Execute post-behavior functions (sequential)
107
+
108
+ Callback with aggregated response data
109
+ ```
110
+
111
+ All stages are executed sequentially using the Fable Anticipate service for asynchronous flow control.
112
+
113
+ ## Routing
114
+
115
+ The IPC server uses [find-my-way](https://github.com/delvedor/find-my-way) for route matching, which supports parametric and wildcard routes:
116
+
117
+ ```javascript
118
+ // Parametric route
119
+ tmpServiceServer.get('/user/:id', handler);
120
+
121
+ // Wildcard route
122
+ tmpServiceServer.get('/files/*', handler);
123
+ ```
@@ -0,0 +1,95 @@
1
+ # Lifecycle Hooks
2
+
3
+ Orator provides lifecycle hooks that allow you to customize behavior at specific points during initialization and service startup. This is useful for setting up middleware, connecting to databases, or performing other setup tasks that need to happen at specific times.
4
+
5
+ ## Initialization Lifecycle
6
+
7
+ When `initialize()` is called, Orator executes these hooks in order:
8
+
9
+ ```
10
+ onBeforeInitialize() / onBeforeInitializeAsync(fNext)
11
+
12
+ onInitialize() / onInitializeAsync(fNext)
13
+
14
+ onAfterInitialize() / onAfterInitializeAsync(fNext)
15
+ ```
16
+
17
+ The `onBeforeInitializeAsync` step is where the service server is set up. If no `OratorServiceServer` is registered with Fable, the built-in IPC server is created automatically at this point.
18
+
19
+ ## Service Start Lifecycle
20
+
21
+ When `startService()` is called, Orator executes these hooks in order:
22
+
23
+ ```
24
+ [initialize() — if not already initialized]
25
+
26
+ onBeforeStartService(fNext)
27
+
28
+ onStartService(fNext) — calls serviceServer.listen()
29
+
30
+ onAfterStartService(fNext)
31
+ ```
32
+
33
+ ## Overriding Hooks
34
+
35
+ To customize the lifecycle, extend the Orator class and override any of the hook methods:
36
+
37
+ ```javascript
38
+ const libOrator = require('orator');
39
+
40
+ class MyOrator extends libOrator
41
+ {
42
+ onBeforeInitializeAsync(fNext)
43
+ {
44
+ // Call super to set up the service server
45
+ super.onBeforeInitializeAsync(
46
+ (pError) =>
47
+ {
48
+ if (pError) return fNext(pError);
49
+
50
+ // Now add your custom setup
51
+ this.log.info('Setting up custom middleware...');
52
+ this.serviceServer.use(
53
+ (pRequest, pResponse, fNext) =>
54
+ {
55
+ pRequest.customTimestamp = Date.now();
56
+ return fNext();
57
+ });
58
+
59
+ return fNext();
60
+ });
61
+ }
62
+
63
+ onAfterStartService(fNext)
64
+ {
65
+ this.log.info('Server started, performing post-startup tasks...');
66
+ return fNext();
67
+ }
68
+ }
69
+ ```
70
+
71
+ ## Sync and Async Hooks
72
+
73
+ Each lifecycle phase has both a synchronous and asynchronous variant. The synchronous version is called from within the async version by default:
74
+
75
+ ```javascript
76
+ // Synchronous hook (for simple logging, etc.)
77
+ onBeforeInitialize()
78
+ {
79
+ this.log.trace('About to initialize...');
80
+ }
81
+
82
+ // Asynchronous hook (for I/O operations)
83
+ onBeforeInitializeAsync(fNext)
84
+ {
85
+ this.onBeforeInitialize();
86
+ // ... async setup ...
87
+ return fNext();
88
+ }
89
+ ```
90
+
91
+ Override the async version when you need to perform asynchronous operations. Override the sync version for simple, non-blocking tasks.
92
+
93
+ ## Auto-Initialization
94
+
95
+ If `startService()` is called before `initialize()`, Orator will automatically run initialization first. You don't need to manually call `initialize()` unless you need to set up routes before starting the server.
@@ -0,0 +1,82 @@
1
+ # Restify Server
2
+
3
+ The Restify service server (`orator-serviceserver-restify`) is the production HTTP server implementation for Orator. It wraps [Restify](https://restify.com/), providing a full-featured HTTP API server with body parsing, middleware, and all standard HTTP verbs.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install orator-serviceserver-restify
9
+ ```
10
+
11
+ ```javascript
12
+ const libOratorServiceServerRestify = require('orator-serviceserver-restify');
13
+
14
+ _Fable.serviceManager.addServiceType('OratorServiceServer', libOratorServiceServerRestify);
15
+ _Fable.serviceManager.instantiateServiceProvider('OratorServiceServer');
16
+ ```
17
+
18
+ ## Restify Configuration
19
+
20
+ Pass Restify-specific options through the `RestifyConfiguration` setting:
21
+
22
+ ```javascript
23
+ const _Fable = new libFable({
24
+ Product: 'MyAPIServer',
25
+ ServicePort: 8080,
26
+ RestifyConfiguration: {
27
+ strictNext: true
28
+ }
29
+ });
30
+ ```
31
+
32
+ The default Restify configuration sets `maxParamLength` to `Number.MAX_SAFE_INTEGER` to avoid truncating long URL parameters.
33
+
34
+ ## Body Parsing
35
+
36
+ The Restify server uses Restify's built-in body parser plugin. Use the `WithBodyParser` convenience methods to automatically parse request bodies:
37
+
38
+ ```javascript
39
+ _Fable.Orator.serviceServer.postWithBodyParser('/api/items',
40
+ (pRequest, pResponse, fNext) =>
41
+ {
42
+ // pRequest.body contains the parsed request body
43
+ let tmpNewItem = pRequest.body;
44
+ pResponse.send({ created: true, item: tmpNewItem });
45
+ return fNext();
46
+ });
47
+ ```
48
+
49
+ ## Pre-Route Middleware
50
+
51
+ Restify distinguishes between `use` middleware (runs after routing) and `pre` middleware (runs before routing). The Restify service server exposes both:
52
+
53
+ ```javascript
54
+ const tmpServiceServer = _Fable.Orator.serviceServer;
55
+
56
+ // Runs after routing (standard middleware)
57
+ tmpServiceServer.use(
58
+ (pRequest, pResponse, fNext) =>
59
+ {
60
+ return fNext();
61
+ });
62
+
63
+ // Runs before routing (pre-middleware)
64
+ tmpServiceServer.pre(
65
+ (pRequest, pResponse, fNext) =>
66
+ {
67
+ return fNext();
68
+ });
69
+ ```
70
+
71
+ ## Accessing the Raw Restify Server
72
+
73
+ The underlying Restify server instance is available at `serviceServer.server` if you need direct access to Restify-specific features:
74
+
75
+ ```javascript
76
+ const tmpRestifyServer = _Fable.Orator.serviceServer.server;
77
+ ```
78
+
79
+ ## Related
80
+
81
+ - [orator-serviceserver-restify documentation](https://github.com/stevenvelozo/orator-serviceserver-restify)
82
+ - [Restify documentation](https://restify.com/)
@@ -0,0 +1,87 @@
1
+ # Service Servers
2
+
3
+ A service server is the component that actually handles HTTP requests (or their in-process equivalent). Orator doesn't implement HTTP handling directly -- it delegates to a service server implementation that conforms to the `orator-serviceserver-base` interface.
4
+
5
+ ## Available Implementations
6
+
7
+ | Implementation | Package | Transport | Use Case |
8
+ |---------------|---------|-----------|----------|
9
+ | **Restify** | `orator-serviceserver-restify` | HTTP/HTTPS | Production API servers |
10
+ | **IPC** | Built into `orator` | In-process | Testing, microservice composition |
11
+
12
+ ## Registering a Service Server
13
+
14
+ Service servers are registered with Fable before Orator initializes:
15
+
16
+ ```javascript
17
+ const libOratorServiceServerRestify = require('orator-serviceserver-restify');
18
+
19
+ _Fable.serviceManager.addServiceType('OratorServiceServer', libOratorServiceServerRestify);
20
+ _Fable.serviceManager.instantiateServiceProvider('OratorServiceServer');
21
+ ```
22
+
23
+ The key is the service type name `OratorServiceServer`. Orator looks for this specific name when it initializes. If it finds one registered, it uses it. If not, it falls back to the built-in IPC server.
24
+
25
+ ## Route Registration
26
+
27
+ All service servers share the same route registration API:
28
+
29
+ ```javascript
30
+ const tmpServiceServer = _Fable.Orator.serviceServer;
31
+
32
+ // Standard HTTP verbs
33
+ tmpServiceServer.get('/path', handler);
34
+ tmpServiceServer.post('/path', handler);
35
+ tmpServiceServer.put('/path', handler);
36
+ tmpServiceServer.del('/path', handler);
37
+ tmpServiceServer.patch('/path', handler);
38
+ tmpServiceServer.opts('/path', handler);
39
+ tmpServiceServer.head('/path', handler);
40
+
41
+ // With automatic body parsing
42
+ tmpServiceServer.postWithBodyParser('/path', handler);
43
+ tmpServiceServer.putWithBodyParser('/path', handler);
44
+ tmpServiceServer.delWithBodyParser('/path', handler);
45
+ tmpServiceServer.patchWithBodyParser('/path', handler);
46
+ ```
47
+
48
+ ## Handler Signature
49
+
50
+ Route handlers follow the standard `(pRequest, pResponse, fNext)` pattern:
51
+
52
+ ```javascript
53
+ tmpServiceServer.get('/api/items/:id',
54
+ (pRequest, pResponse, fNext) =>
55
+ {
56
+ let tmpItemID = pRequest.params.id;
57
+ pResponse.send({ id: tmpItemID });
58
+ return fNext();
59
+ });
60
+ ```
61
+
62
+ ## Middleware
63
+
64
+ Register middleware that runs before all route handlers:
65
+
66
+ ```javascript
67
+ tmpServiceServer.use(
68
+ (pRequest, pResponse, fNext) =>
69
+ {
70
+ // Runs before every request
71
+ return fNext();
72
+ });
73
+ ```
74
+
75
+ ## Service Server Properties
76
+
77
+ | Property | Type | Description |
78
+ |----------|------|-------------|
79
+ | `ServiceServerType` | string | Identifier for the implementation (e.g., `"Restify"`, `"IPC"`) |
80
+ | `Name` | string | Server name (from `fable.settings.Product`) |
81
+ | `URL` | string | Server URL or identifier |
82
+ | `Port` | number | Listening port |
83
+ | `Active` | boolean | Whether the server is currently listening |
84
+
85
+ ## Building Custom Service Servers
86
+
87
+ You can create your own service server implementation by extending `orator-serviceserver-base`. See the [orator-serviceserver-base documentation](https://github.com/stevenvelozo/orator-serviceserver-base) for the interface contract.
@@ -0,0 +1,75 @@
1
+ # Static File Serving
2
+
3
+ Orator includes built-in static file serving through the `addStaticRoute` method. This serves files from a local directory over HTTP, with support for subdomain-based folder routing and MIME type detection.
4
+
5
+ ## Basic Usage
6
+
7
+ ```javascript
8
+ // Serve files from ./public, defaulting to index.html
9
+ _Fable.Orator.addStaticRoute('./public/');
10
+ ```
11
+
12
+ This maps all incoming requests to files in the `./public/` directory. A request to `/styles.css` would serve `./public/styles.css`.
13
+
14
+ ## Parameters
15
+
16
+ ```javascript
17
+ _Fable.Orator.addStaticRoute(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams);
18
+ ```
19
+
20
+ | Parameter | Type | Default | Description |
21
+ |-----------|------|---------|-------------|
22
+ | `pFilePath` | string | *required* | Path to the directory to serve files from |
23
+ | `pDefaultFile` | string | `"index.html"` | Default file when no specific file is requested |
24
+ | `pRoute` | string | `"/*"` | Route pattern to match for static file requests |
25
+ | `pRouteStrip` | string | `"/"` | Prefix to strip from URL paths before looking up files |
26
+ | `pParams` | object | `{}` | Additional options passed to the `serve-static` library |
27
+
28
+ ## Route Stripping
29
+
30
+ The `pRouteStrip` parameter removes a prefix from the URL before mapping it to the filesystem. This is useful when your static files are served under a subpath:
31
+
32
+ ```javascript
33
+ // Serve /app/styles.css from ./dist/styles.css
34
+ _Fable.Orator.addStaticRoute('./dist/', 'index.html', '/app/*', '/app/');
35
+ ```
36
+
37
+ ## Subdomain Magic Hosting
38
+
39
+ Orator has a built-in feature for subdomain-based folder routing. When a request comes in with a subdomain prefix, Orator checks if a matching subfolder exists in the serve directory. If it does, files are served from that subfolder instead.
40
+
41
+ For example, with a serve path of `./sites/`:
42
+
43
+ - A request to `http://clienta.example.com/page.html` would check for `./sites/clienta/page.html`
44
+ - If `./sites/clienta/` exists, it serves from there
45
+ - If not, it falls back to `./sites/page.html`
46
+
47
+ This enables a simple multi-tenant static hosting setup without any additional configuration.
48
+
49
+ ## MIME Type Detection
50
+
51
+ Orator automatically sets the `Content-Type` header based on the file extension. It uses the `mime` library for detection and falls back to `application/octet-stream` for unknown types.
52
+
53
+ ## Example: Single Page Application
54
+
55
+ A common pattern is serving a single page application where all routes should fall back to `index.html`:
56
+
57
+ ```javascript
58
+ // Serve the SPA from ./dist, all routes map to index.html
59
+ _Fable.Orator.addStaticRoute('./dist/', 'index.html', '/*');
60
+ ```
61
+
62
+ ## Example: API Server with Static Frontend
63
+
64
+ ```javascript
65
+ // Set up API routes first
66
+ _Fable.Orator.serviceServer.get('/api/data',
67
+ (pRequest, pResponse, fNext) =>
68
+ {
69
+ pResponse.send({ value: 42 });
70
+ return fNext();
71
+ });
72
+
73
+ // Then serve static files for everything else
74
+ _Fable.Orator.addStaticRoute('./public/', 'index.html', '/*');
75
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orator",
3
- "version": "5.0.1",
3
+ "version": "6.0.0",
4
4
  "description": "Unopinionated API http server abstraction - REST or IPC",
5
5
  "main": "source/Orator.js",
6
6
  "scripts": {
@@ -51,14 +51,13 @@
51
51
  },
52
52
  "homepage": "https://github.com/stevenvelozo/orator",
53
53
  "devDependencies": {
54
- "fable": "^3.0.147",
55
- "quackage": "^1.0.36"
54
+ "fable": "^3.1.53",
55
+ "quackage": "^1.0.48"
56
56
  },
57
57
  "dependencies": {
58
- "fable-serviceproviderbase": "^3.0.15",
59
- "finalhandler": "^1.3.1",
58
+ "fable-serviceproviderbase": "^3.0.17",
60
59
  "find-my-way": "^9.1.0",
61
- "orator-serviceserver-base": "^1.0.1",
62
- "serve-static": "^1.16.2"
60
+ "orator-serviceserver-base": "^1.0.2",
61
+ "orator-static-server": "^2.0.0"
63
62
  }
64
63
  }