orator 6.0.4 → 6.1.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/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.0",
4
4
  "description": "Unopinionated API http server abstraction - REST or IPC",
5
5
  "main": "source/Orator.js",
6
6
  "scripts": {
@@ -51,13 +51,14 @@
51
51
  },
52
52
  "homepage": "https://github.com/stevenvelozo/orator",
53
53
  "devDependencies": {
54
- "fable": "^3.1.57",
55
- "quackage": "^1.0.59"
54
+ "fable": "^3.1.71",
55
+ "pict-docuserve": "^0.1.5",
56
+ "quackage": "^1.1.2"
56
57
  },
57
58
  "dependencies": {
58
59
  "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"
60
+ "find-my-way": "^9.5.0",
61
+ "orator-serviceserver-base": "^1.0.7",
62
+ "orator-static-server": "^2.1.2"
62
63
  }
63
64
  }
package/source/Orator.js CHANGED
@@ -415,6 +415,38 @@ class Orator extends libFableServiceProviderBase
415
415
 
416
416
  return this.fable.OratorStaticServer.addStaticRoute(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams);
417
417
  }
418
+
419
+ /**
420
+ * Register a static-file route with a per-file CDN fallback map.
421
+ *
422
+ * Behaves like addStaticRoute for every local hit. For any request whose
423
+ * relative path is a key in pFallbackMap and whose file isn't present
424
+ * on disk, responds with 302 to the mapped URL instead of a 404. Any
425
+ * request whose relative path isn't in the map behaves exactly as
426
+ * addStaticRoute would (local hit → stream, local miss → 404).
427
+ *
428
+ * @param {string} pFilePath The path on disk that we are serving files from.
429
+ * @param {string?} pDefaultFile (optional) The default file served if no specific file is requested.
430
+ * @param {string?} pRoute (optional) The route matcher that will be used. Defaults to everything.
431
+ * @param {string?} pRouteStrip (optional) If provided, this prefix will be removed from URL paths before being served.
432
+ * @param {object?} pParams (optional) Additional parameters to pass to serve-static.
433
+ * @param {Object<string,string>?} pFallbackMap (optional) Map of relative path (under the route prefix) to absolute URL for CDN fallback.
434
+ * @return {boolean} true if the handler was successfully installed, otherwise false.
435
+ */
436
+ addStaticRouteWithFallbacks(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams, pFallbackMap)
437
+ {
438
+ if (!this.fable.serviceManager.servicesMap.hasOwnProperty('OratorStaticServer'))
439
+ {
440
+ this.fable.serviceManager.addServiceType('OratorStaticServer', libOratorStaticServer);
441
+ }
442
+ if (!this.fable.OratorStaticServer)
443
+ {
444
+ this.fable.serviceManager.instantiateServiceProvider('OratorStaticServer', {}, 'OratorStaticServer-AutoInit');
445
+ }
446
+
447
+ return this.fable.OratorStaticServer.addStaticRouteWithFallbacks(
448
+ pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams, pFallbackMap);
449
+ }
418
450
  }
419
451
 
420
452
  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();