orator 6.0.4 → 6.1.1

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/docs/_cover.md CHANGED
@@ -1,4 +1,4 @@
1
- # Orator <small>5</small>
1
+ # Orator
2
2
 
3
3
  > An unopinionated API server abstraction for REST and IPC services
4
4
 
@@ -0,0 +1,7 @@
1
+ {
2
+ "Name": "orator",
3
+ "Version": "6.0.4",
4
+ "Description": "Unopinionated API http server abstraction - REST or IPC",
5
+ "GeneratedAt": "2026-04-10T17:23:32.626Z",
6
+ "GitCommit": "36cbb4d"
7
+ }
@@ -84,9 +84,9 @@ _Fable.Orator.invoke('GET', '/api/data', {},
84
84
  The service server base class uses a template method pattern for route registration. The base class validates inputs, and derived classes implement the `do*` methods with actual behavior:
85
85
 
86
86
  ```
87
- Base: get(pRoute, ...) validates pRoute calls doGet(pRoute, ...)
88
- └── Restify: doGet(pRoute, ...) this.server.get(pRoute, ...)
89
- └── IPC: doGet(pRoute, ...) this.addRouteProcessor('GET', pRoute, ...)
87
+ Base: get(pRoute, ...) -> validates pRoute -> calls doGet(pRoute, ...)
88
+ └── Restify: doGet(pRoute, ...) -> this.server.get(pRoute, ...)
89
+ └── IPC: doGet(pRoute, ...) -> this.addRouteProcessor('GET', pRoute, ...)
90
90
  ```
91
91
 
92
92
  This pattern allows derived classes to focus on implementation without duplicating validation logic.
@@ -1,73 +1,327 @@
1
1
  /* ============================================================================
2
- Pict Docuserve - Base Styles
2
+ Pict Docuserve - Base Styles & Theme Variables
3
3
  ============================================================================ */
4
4
 
5
- /* Reset and base */
6
- *, *::before, *::after {
5
+ /* ----------------------------------------------------------------------------
6
+ Theme variables light defaults on :root.
7
+ Dark mode applies when either:
8
+ (a) the user explicitly selected dark via <html data-theme="dark">
9
+ (b) the user hasn't chosen anything AND the system prefers dark
10
+ An explicit <html data-theme="light"> pins the light palette regardless.
11
+ ---------------------------------------------------------------------------- */
12
+
13
+ :root
14
+ {
15
+ /* Surfaces */
16
+ --docuserve-bg: #FDFBF7;
17
+ --docuserve-bg-elevated: #FFFFFF;
18
+ --docuserve-border: #DDD6CA;
19
+ --docuserve-border-soft: #EAE3D8;
20
+
21
+ /* Text */
22
+ --docuserve-text: #2A241E;
23
+ --docuserve-text-strong: #3D3229;
24
+ --docuserve-text-muted: #5E5549;
25
+ --docuserve-text-dim: #8A7F72;
26
+
27
+ /* Accent / links */
28
+ --docuserve-accent: #2E7D74;
29
+ --docuserve-accent-hover: #236660;
30
+
31
+ /* Top bar */
32
+ --docuserve-topbar-bg: #3D3229;
33
+ --docuserve-topbar-text: #E8E0D4;
34
+ --docuserve-topbar-text-muted: #B5AA9A;
35
+ --docuserve-topbar-text-dim: #8A7F72;
36
+ --docuserve-topbar-hover-bg: #524438;
37
+ --docuserve-topbar-version-bg: rgba(255, 255, 255, 0.06);
38
+ --docuserve-topbar-version-border: rgba(255, 255, 255, 0.08);
39
+ --docuserve-topbar-version-text: #B5AA9A;
40
+
41
+ /* Sidebar */
42
+ --docuserve-sidebar-bg: #FAF7F1;
43
+ --docuserve-sidebar-border: #DDD6CA;
44
+ --docuserve-sidebar-border-soft: #E5DED1;
45
+ --docuserve-sidebar-text: #423D37;
46
+ --docuserve-sidebar-group-title: #3D3229;
47
+ --docuserve-sidebar-module-text: #5E5549;
48
+ --docuserve-sidebar-hover-bg: #EAE3D8;
49
+ --docuserve-sidebar-hover-text: #2E7D74;
50
+ --docuserve-sidebar-active-bg: #E5DED1;
51
+ --docuserve-sidebar-active-text: #2E7D74;
52
+ --docuserve-sidebar-search-bg: #FFFFFF;
53
+ --docuserve-sidebar-search-border: #DDD6CA;
54
+
55
+ /* Inline code */
56
+ --docuserve-inline-code-bg: #F0ECE4;
57
+ --docuserve-inline-code-text: #9E3A50;
58
+
59
+ /* Code block panel */
60
+ --docuserve-code-bg: #F6F3EE;
61
+ --docuserve-code-border: #E5DED1;
62
+ --docuserve-code-gutter-bg: #EFEAE0;
63
+ --docuserve-code-gutter-border: #DDD6CA;
64
+ --docuserve-code-gutter-text: #A59986;
65
+ --docuserve-code-text: #2A241E;
66
+
67
+ /* Syntax tokens — low-chroma dark-on-light palette */
68
+ --docuserve-tok-keyword: #A03472;
69
+ --docuserve-tok-string: #1A6640;
70
+ --docuserve-tok-number: #B25A00;
71
+ --docuserve-tok-comment: #8A7F72;
72
+ --docuserve-tok-operator: #2E7D74;
73
+ --docuserve-tok-punctuation: #2A241E;
74
+ --docuserve-tok-function: #2A5DB0;
75
+ --docuserve-tok-property: #9E3A50;
76
+ --docuserve-tok-tag: #9E3A50;
77
+ --docuserve-tok-attr-name: #B25A00;
78
+ --docuserve-tok-attr-value: #1A6640;
79
+
80
+ /* Tables, blockquotes, mermaid */
81
+ --docuserve-table-header-bg: #F5F0E8;
82
+ --docuserve-table-row-alt-bg: #F9F6F0;
83
+ --docuserve-blockquote-bg: #F7F5F0;
84
+ --docuserve-blockquote-border: #2E7D74;
85
+ --docuserve-blockquote-text: #5E5549;
86
+ --docuserve-mermaid-bg: #FFFFFF;
87
+
88
+ /* Scrollbars */
89
+ --docuserve-scrollbar-track: #F5F0E8;
90
+ --docuserve-scrollbar-thumb: #D4CCBE;
91
+ --docuserve-scrollbar-thumb-hover: #B5AA9A;
92
+ }
93
+
94
+ @media (prefers-color-scheme: dark)
95
+ {
96
+ :root:not([data-theme="light"])
97
+ {
98
+ --docuserve-bg: #15120F;
99
+ --docuserve-bg-elevated: #1B1814;
100
+ --docuserve-border: #2F2823;
101
+ --docuserve-border-soft: #26211C;
102
+
103
+ --docuserve-text: #E8E0D4;
104
+ --docuserve-text-strong: #F2ECE0;
105
+ --docuserve-text-muted: #B5AA9A;
106
+ --docuserve-text-dim: #7A6F62;
107
+
108
+ --docuserve-accent: #5DB8A8;
109
+ --docuserve-accent-hover: #7FCCB8;
110
+
111
+ --docuserve-topbar-bg: #1A1612;
112
+ --docuserve-topbar-text: #E8E0D4;
113
+ --docuserve-topbar-text-muted: #B5AA9A;
114
+ --docuserve-topbar-text-dim: #7A6F62;
115
+ --docuserve-topbar-hover-bg: #2A241E;
116
+ --docuserve-topbar-version-bg: rgba(255, 255, 255, 0.05);
117
+ --docuserve-topbar-version-border: rgba(255, 255, 255, 0.09);
118
+ --docuserve-topbar-version-text: #B5AA9A;
119
+
120
+ --docuserve-sidebar-bg: #1B1814;
121
+ --docuserve-sidebar-border: #2F2823;
122
+ --docuserve-sidebar-border-soft: #26211C;
123
+ --docuserve-sidebar-text: #C9C0B3;
124
+ --docuserve-sidebar-group-title: #F2ECE0;
125
+ --docuserve-sidebar-module-text: #B5AA9A;
126
+ --docuserve-sidebar-hover-bg: #2A241E;
127
+ --docuserve-sidebar-hover-text: #7FCCB8;
128
+ --docuserve-sidebar-active-bg: #2F2823;
129
+ --docuserve-sidebar-active-text: #7FCCB8;
130
+ --docuserve-sidebar-search-bg: #26211C;
131
+ --docuserve-sidebar-search-border: #2F2823;
132
+
133
+ --docuserve-inline-code-bg: #2A241E;
134
+ --docuserve-inline-code-text: #E8B07A;
135
+
136
+ --docuserve-code-bg: #1E1A17;
137
+ --docuserve-code-border: #2F2823;
138
+ --docuserve-code-gutter-bg: #17130F;
139
+ --docuserve-code-gutter-border: #2F2823;
140
+ --docuserve-code-gutter-text: #6A6052;
141
+ --docuserve-code-text: #E8E0D4;
142
+
143
+ --docuserve-tok-keyword: #C678DD;
144
+ --docuserve-tok-string: #98C379;
145
+ --docuserve-tok-number: #D19A66;
146
+ --docuserve-tok-comment: #7F848E;
147
+ --docuserve-tok-operator: #56B6C2;
148
+ --docuserve-tok-punctuation: #E8E0D4;
149
+ --docuserve-tok-function: #61AFEF;
150
+ --docuserve-tok-property: #E06C75;
151
+ --docuserve-tok-tag: #E06C75;
152
+ --docuserve-tok-attr-name: #D19A66;
153
+ --docuserve-tok-attr-value: #98C379;
154
+
155
+ --docuserve-table-header-bg: #26211C;
156
+ --docuserve-table-row-alt-bg: #1F1B17;
157
+ --docuserve-blockquote-bg: #1F1B17;
158
+ --docuserve-blockquote-border: #5DB8A8;
159
+ --docuserve-blockquote-text: #C9C0B3;
160
+ --docuserve-mermaid-bg: #E8E0D4;
161
+
162
+ --docuserve-scrollbar-track: #1B1814;
163
+ --docuserve-scrollbar-thumb: #3A322B;
164
+ --docuserve-scrollbar-thumb-hover: #524438;
165
+ }
166
+ }
167
+
168
+ :root[data-theme="dark"]
169
+ {
170
+ --docuserve-bg: #15120F;
171
+ --docuserve-bg-elevated: #1B1814;
172
+ --docuserve-border: #2F2823;
173
+ --docuserve-border-soft: #26211C;
174
+
175
+ --docuserve-text: #E8E0D4;
176
+ --docuserve-text-strong: #F2ECE0;
177
+ --docuserve-text-muted: #B5AA9A;
178
+ --docuserve-text-dim: #7A6F62;
179
+
180
+ --docuserve-accent: #5DB8A8;
181
+ --docuserve-accent-hover: #7FCCB8;
182
+
183
+ --docuserve-topbar-bg: #1A1612;
184
+ --docuserve-topbar-text: #E8E0D4;
185
+ --docuserve-topbar-text-muted: #B5AA9A;
186
+ --docuserve-topbar-text-dim: #7A6F62;
187
+ --docuserve-topbar-hover-bg: #2A241E;
188
+ --docuserve-topbar-version-bg: rgba(255, 255, 255, 0.05);
189
+ --docuserve-topbar-version-border: rgba(255, 255, 255, 0.09);
190
+ --docuserve-topbar-version-text: #B5AA9A;
191
+
192
+ --docuserve-sidebar-bg: #1B1814;
193
+ --docuserve-sidebar-border: #2F2823;
194
+ --docuserve-sidebar-border-soft: #26211C;
195
+ --docuserve-sidebar-text: #C9C0B3;
196
+ --docuserve-sidebar-group-title: #F2ECE0;
197
+ --docuserve-sidebar-module-text: #B5AA9A;
198
+ --docuserve-sidebar-hover-bg: #2A241E;
199
+ --docuserve-sidebar-hover-text: #7FCCB8;
200
+ --docuserve-sidebar-active-bg: #2F2823;
201
+ --docuserve-sidebar-active-text: #7FCCB8;
202
+ --docuserve-sidebar-search-bg: #26211C;
203
+ --docuserve-sidebar-search-border: #2F2823;
204
+
205
+ --docuserve-inline-code-bg: #2A241E;
206
+ --docuserve-inline-code-text: #E8B07A;
207
+
208
+ --docuserve-code-bg: #1E1A17;
209
+ --docuserve-code-border: #2F2823;
210
+ --docuserve-code-gutter-bg: #17130F;
211
+ --docuserve-code-gutter-border: #2F2823;
212
+ --docuserve-code-gutter-text: #6A6052;
213
+ --docuserve-code-text: #E8E0D4;
214
+
215
+ --docuserve-tok-keyword: #C678DD;
216
+ --docuserve-tok-string: #98C379;
217
+ --docuserve-tok-number: #D19A66;
218
+ --docuserve-tok-comment: #7F848E;
219
+ --docuserve-tok-operator: #56B6C2;
220
+ --docuserve-tok-punctuation: #E8E0D4;
221
+ --docuserve-tok-function: #61AFEF;
222
+ --docuserve-tok-property: #E06C75;
223
+ --docuserve-tok-tag: #E06C75;
224
+ --docuserve-tok-attr-name: #D19A66;
225
+ --docuserve-tok-attr-value: #98C379;
226
+
227
+ --docuserve-table-header-bg: #26211C;
228
+ --docuserve-table-row-alt-bg: #1F1B17;
229
+ --docuserve-blockquote-bg: #1F1B17;
230
+ --docuserve-blockquote-border: #5DB8A8;
231
+ --docuserve-blockquote-text: #C9C0B3;
232
+ --docuserve-mermaid-bg: #E8E0D4;
233
+
234
+ --docuserve-scrollbar-track: #1B1814;
235
+ --docuserve-scrollbar-thumb: #3A322B;
236
+ --docuserve-scrollbar-thumb-hover: #524438;
237
+ }
238
+
239
+ /* ----------------------------------------------------------------------------
240
+ Reset and base
241
+ ---------------------------------------------------------------------------- */
242
+
243
+ *, *::before, *::after
244
+ {
7
245
  box-sizing: border-box;
8
246
  }
9
247
 
10
- html, body {
248
+ html, body
249
+ {
11
250
  margin: 0;
12
251
  padding: 0;
13
252
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
14
253
  font-size: 16px;
15
254
  line-height: 1.5;
16
- color: #423D37;
17
- background-color: #fff;
255
+ color: var(--docuserve-text);
256
+ background-color: var(--docuserve-bg);
18
257
  -webkit-font-smoothing: antialiased;
19
258
  -moz-osx-font-smoothing: grayscale;
259
+ transition: background-color 0.15s ease, color 0.15s ease;
20
260
  }
21
261
 
22
262
  /* Typography */
23
- h1, h2, h3, h4, h5, h6 {
263
+ h1, h2, h3, h4, h5, h6
264
+ {
24
265
  margin-top: 0;
25
266
  line-height: 1.3;
267
+ color: var(--docuserve-text-strong);
26
268
  }
27
269
 
28
- a {
29
- color: #2E7D74;
270
+ a
271
+ {
272
+ color: var(--docuserve-accent);
30
273
  text-decoration: none;
31
274
  }
32
275
 
33
- a:hover {
34
- color: #256861;
276
+ a:hover
277
+ {
278
+ color: var(--docuserve-accent-hover);
35
279
  }
36
280
 
37
281
  /* Application container */
38
- #Docuserve-Application-Container {
282
+ #Docuserve-Application-Container
283
+ {
39
284
  min-height: 100vh;
40
285
  }
41
286
 
42
287
  /* Utility: scrollbar styling */
43
- ::-webkit-scrollbar {
288
+ ::-webkit-scrollbar
289
+ {
44
290
  width: 8px;
291
+ height: 8px;
45
292
  }
46
293
 
47
- ::-webkit-scrollbar-track {
48
- background: #F5F0E8;
294
+ ::-webkit-scrollbar-track
295
+ {
296
+ background: var(--docuserve-scrollbar-track);
49
297
  }
50
298
 
51
- ::-webkit-scrollbar-thumb {
52
- background: #D4CCBE;
299
+ ::-webkit-scrollbar-thumb
300
+ {
301
+ background: var(--docuserve-scrollbar-thumb);
53
302
  border-radius: 4px;
54
303
  }
55
304
 
56
- ::-webkit-scrollbar-thumb:hover {
57
- background: #B5AA9A;
305
+ ::-webkit-scrollbar-thumb:hover
306
+ {
307
+ background: var(--docuserve-scrollbar-thumb-hover);
58
308
  }
59
309
 
60
310
  /* Responsive adjustments */
61
- @media (max-width: 768px) {
62
- html {
311
+ @media (max-width: 768px)
312
+ {
313
+ html
314
+ {
63
315
  font-size: 14px;
64
316
  }
65
317
 
66
- #Docuserve-Sidebar-Container {
318
+ #Docuserve-Sidebar-Container
319
+ {
67
320
  display: none;
68
321
  }
69
322
 
70
- .docuserve-body {
323
+ .docuserve-body
324
+ {
71
325
  flex-direction: column;
72
326
  }
73
327
  }
package/docs/index.html CHANGED
@@ -4,9 +4,9 @@
4
4
  <meta charset="utf-8">
5
5
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
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">
7
+ <meta name="description" content="Orator v6.0.4 Documentation Unopinionated API http server abstraction - REST or IPC">
8
8
 
9
- <title>Documentation</title>
9
+ <title>Orator v6.0.4 Documentation</title>
10
10
 
11
11
  <!-- Application Stylesheet -->
12
12
  <link href="css/docuserve.css" rel="stylesheet">
@@ -21,11 +21,11 @@ The `onBeforeInitializeAsync` step is where the service server is set up. If no
21
21
  When `startService()` is called, Orator executes these hooks in order:
22
22
 
23
23
  ```
24
- [initialize() if not already initialized]
24
+ [initialize() -- if not already initialized]
25
25
 
26
26
  onBeforeStartService(fNext)
27
27
 
28
- onStartService(fNext) calls serviceServer.listen()
28
+ onStartService(fNext) -- calls serviceServer.listen()
29
29
 
30
30
  onAfterStartService(fNext)
31
31
  ```
@@ -1,5 +1,5 @@
1
1
  {
2
- "Generated": "2026-02-18T03:37:10.798Z",
2
+ "Generated": "2026-04-10T17:23:32.342Z",
3
3
  "GitHubOrg": "stevenvelozo",
4
4
  "DefaultBranch": "master",
5
5
  "Groups": [
@@ -64,6 +64,25 @@
64
64
  }
65
65
  ]
66
66
  },
67
+ {
68
+ "Name": "Docs",
69
+ "Key": "docs",
70
+ "Description": "",
71
+ "Modules": [
72
+ {
73
+ "Name": "css",
74
+ "Repo": "css",
75
+ "Group": "docs",
76
+ "Branch": "master",
77
+ "HasDocs": true,
78
+ "HasCover": false,
79
+ "Sidebar": [],
80
+ "DocFiles": [
81
+ "css/docuserve.css"
82
+ ]
83
+ }
84
+ ]
85
+ },
67
86
  {
68
87
  "Name": "Test",
69
88
  "Key": "test",
@@ -0,0 +1,19 @@
1
+ {
2
+ "Generated": "2026-04-10T17:23:32.596Z",
3
+ "DocumentCount": 0,
4
+ "LunrIndex": {
5
+ "version": "2.3.9",
6
+ "fields": [
7
+ "title",
8
+ "module",
9
+ "group",
10
+ "body"
11
+ ],
12
+ "fieldVectors": [],
13
+ "invertedIndex": [],
14
+ "pipeline": [
15
+ "stemmer"
16
+ ]
17
+ },
18
+ "Documents": {}
19
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orator",
3
- "version": "6.0.4",
3
+ "version": "6.1.1",
4
4
  "description": "Unopinionated API http server abstraction - REST or IPC",
5
5
  "main": "source/Orator.js",
6
6
  "scripts": {
@@ -15,7 +15,8 @@
15
15
  "docker-dev-shell": "docker exec -it orator-dev /bin/bash"
16
16
  },
17
17
  "browser": {
18
- "./source/Orator-Default-ServiceServer.js": "./source/Orator-Default-ServiceServer-Web.js"
18
+ "./source/Orator-Default-ServiceServer.js": "./source/Orator-Default-ServiceServer-Web.js",
19
+ "orator-static-server": false
19
20
  },
20
21
  "repository": {
21
22
  "type": "git",
@@ -51,13 +52,14 @@
51
52
  },
52
53
  "homepage": "https://github.com/stevenvelozo/orator",
53
54
  "devDependencies": {
54
- "fable": "^3.1.57",
55
- "quackage": "^1.0.59"
55
+ "fable": "^3.1.71",
56
+ "pict-docuserve": "^0.1.5",
57
+ "quackage": "^1.1.3"
56
58
  },
57
59
  "dependencies": {
58
60
  "fable-serviceproviderbase": "^3.0.19",
59
- "find-my-way": "^9.4.0",
60
- "orator-serviceserver-base": "^1.0.6",
61
- "orator-static-server": "^2.0.4"
61
+ "find-my-way": "^9.5.0",
62
+ "orator-serviceserver-base": "^1.0.7",
63
+ "orator-static-server": "^2.1.3"
62
64
  }
63
65
  }
package/source/Orator.js CHANGED
@@ -11,7 +11,19 @@ const libFableServiceProviderBase = require('fable-serviceproviderbase');
11
11
 
12
12
  const libDefaultOratorServiceServer = require('./Orator-Default-ServiceServer.js');
13
13
 
14
- const libOratorStaticServer = require('orator-static-server');
14
+ // orator-static-server is lazy-loaded so that browser bundles using orator's
15
+ // IPC mode don't pay the cost of (or trip over) its server-only deps. Only
16
+ // setMimeHeader / addStaticRoute / addStaticRouteWithFallbacks need it, and
17
+ // none of those are reachable from IPC-only consumers.
18
+ let libOratorStaticServer = null;
19
+ const getOratorStaticServer = () =>
20
+ {
21
+ if (!libOratorStaticServer)
22
+ {
23
+ libOratorStaticServer = require('orator-static-server');
24
+ }
25
+ return libOratorStaticServer;
26
+ };
15
27
 
16
28
  const defaultOratorConfiguration = require('./Orator-Default-Configuration.js');
17
29
 
@@ -381,7 +393,7 @@ class Orator extends libFableServiceProviderBase
381
393
  // Force auto-registration so the method is available
382
394
  if (!this.fable.serviceManager.servicesMap.hasOwnProperty('OratorStaticServer'))
383
395
  {
384
- this.fable.serviceManager.addServiceType('OratorStaticServer', libOratorStaticServer);
396
+ this.fable.serviceManager.addServiceType('OratorStaticServer', getOratorStaticServer());
385
397
  }
386
398
  this.fable.serviceManager.instantiateServiceProvider('OratorStaticServer', {}, 'OratorStaticServer-AutoInit');
387
399
  }
@@ -404,7 +416,7 @@ class Orator extends libFableServiceProviderBase
404
416
  // Auto-register the OratorStaticServer service type if it hasn't been registered yet
405
417
  if (!this.fable.serviceManager.servicesMap.hasOwnProperty('OratorStaticServer'))
406
418
  {
407
- this.fable.serviceManager.addServiceType('OratorStaticServer', libOratorStaticServer);
419
+ this.fable.serviceManager.addServiceType('OratorStaticServer', getOratorStaticServer());
408
420
  }
409
421
 
410
422
  // Auto-instantiate a default OratorStaticServer instance if none exists
@@ -415,6 +427,38 @@ class Orator extends libFableServiceProviderBase
415
427
 
416
428
  return this.fable.OratorStaticServer.addStaticRoute(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams);
417
429
  }
430
+
431
+ /**
432
+ * Register a static-file route with a per-file CDN fallback map.
433
+ *
434
+ * Behaves like addStaticRoute for every local hit. For any request whose
435
+ * relative path is a key in pFallbackMap and whose file isn't present
436
+ * on disk, responds with 302 to the mapped URL instead of a 404. Any
437
+ * request whose relative path isn't in the map behaves exactly as
438
+ * addStaticRoute would (local hit → stream, local miss → 404).
439
+ *
440
+ * @param {string} pFilePath The path on disk that we are serving files from.
441
+ * @param {string?} pDefaultFile (optional) The default file served if no specific file is requested.
442
+ * @param {string?} pRoute (optional) The route matcher that will be used. Defaults to everything.
443
+ * @param {string?} pRouteStrip (optional) If provided, this prefix will be removed from URL paths before being served.
444
+ * @param {object?} pParams (optional) Additional parameters to pass to serve-static.
445
+ * @param {Object<string,string>?} pFallbackMap (optional) Map of relative path (under the route prefix) to absolute URL for CDN fallback.
446
+ * @return {boolean} true if the handler was successfully installed, otherwise false.
447
+ */
448
+ addStaticRouteWithFallbacks(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams, pFallbackMap)
449
+ {
450
+ if (!this.fable.serviceManager.servicesMap.hasOwnProperty('OratorStaticServer'))
451
+ {
452
+ this.fable.serviceManager.addServiceType('OratorStaticServer', getOratorStaticServer());
453
+ }
454
+ if (!this.fable.OratorStaticServer)
455
+ {
456
+ this.fable.serviceManager.instantiateServiceProvider('OratorStaticServer', {}, 'OratorStaticServer-AutoInit');
457
+ }
458
+
459
+ return this.fable.OratorStaticServer.addStaticRouteWithFallbacks(
460
+ pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams, pFallbackMap);
461
+ }
418
462
  }
419
463
 
420
464
  module.exports = Orator;
@@ -353,7 +353,8 @@ suite
353
353
  let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
354
354
 
355
355
  tmpStaticServer.setMimeHeader('app.js', tmpMockResponse);
356
- Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/javascript');
356
+ // mime v4 returns the IANA-canonical 'text/javascript'; earlier versions returned 'application/javascript'.
357
+ Expect(tmpCapturedHeaders['Content-Type']).to.be.oneOf(['text/javascript', 'application/javascript']);
357
358
 
358
359
  tmpOrator.log.info('JavaScript MIME type correctly detected');
359
360
  return fDone();