orator-static-server 2.0.3 → 2.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.
@@ -1,4 +1,4 @@
1
- # Orator Static Server <small>1</small>
1
+ # Orator Static Server
2
2
 
3
3
  > Static file serving for Orator API servers
4
4
 
@@ -0,0 +1,7 @@
1
+ {
2
+ "Name": "orator-static-server",
3
+ "Version": "2.0.4",
4
+ "Description": "Static file serving for Orator API servers.",
5
+ "GeneratedAt": "2026-04-10T17:23:24.461Z",
6
+ "GitCommit": "96c22b3"
7
+ }
@@ -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 Static Server v2.0.4 Documentation Static file serving for Orator API servers.">
8
8
 
9
- <title>Documentation</title>
9
+ <title>Orator Static Server v2.0.4 Documentation</title>
10
10
 
11
11
  <!-- Application Stylesheet -->
12
12
  <link href="css/docuserve.css" rel="stylesheet">
@@ -1,5 +1,5 @@
1
1
  {
2
- "Generated": "2026-02-18T03:41:38.903Z",
2
+ "Generated": "2026-04-10T17:23:24.205Z",
3
3
  "GitHubOrg": "stevenvelozo",
4
4
  "DefaultBranch": "master",
5
5
  "Groups": [
@@ -20,6 +20,25 @@
20
20
  }
21
21
  ]
22
22
  },
23
+ {
24
+ "Name": "Docs",
25
+ "Key": "docs",
26
+ "Description": "",
27
+ "Modules": [
28
+ {
29
+ "Name": "css",
30
+ "Repo": "css",
31
+ "Group": "docs",
32
+ "Branch": "master",
33
+ "HasDocs": true,
34
+ "HasCover": false,
35
+ "Sidebar": [],
36
+ "DocFiles": [
37
+ "css/docuserve.css"
38
+ ]
39
+ }
40
+ ]
41
+ },
23
42
  {
24
43
  "Name": "Test",
25
44
  "Key": "test",
@@ -1,5 +1,5 @@
1
1
  {
2
- "Generated": "2026-02-18T03:41:38.985Z",
2
+ "Generated": "2026-04-10T17:23:24.439Z",
3
3
  "DocumentCount": 0,
4
4
  "LunrIndex": {
5
5
  "version": "2.3.9",
@@ -14,8 +14,8 @@ Split host: ["clienta", "example", "com"]
14
14
  First segment: "clienta"
15
15
 
16
16
  Check: does ./sites/clienta/ exist?
17
- ├── Yes Serve from ./sites/clienta/page.html
18
- └── No Serve from ./sites/page.html
17
+ ├── Yes -> Serve from ./sites/clienta/page.html
18
+ └── No -> Serve from ./sites/page.html
19
19
  ```
20
20
 
21
21
  ## Setup
@@ -29,13 +29,13 @@ _Fable.Orator.addStaticRoute('./sites/');
29
29
 
30
30
  ```
31
31
  sites/
32
- ├── index.html Served for requests without subdomain match
32
+ ├── index.html <- Served for requests without subdomain match
33
33
  ├── styles.css
34
34
  ├── clienta/
35
- │ ├── index.html Served for clienta.example.com
35
+ │ ├── index.html <- Served for clienta.example.com
36
36
  │ └── styles.css
37
37
  ├── clientb/
38
- │ ├── index.html Served for clientb.example.com
38
+ │ ├── index.html <- Served for clientb.example.com
39
39
  │ └── styles.css
40
40
  └── shared/
41
41
  └── logo.png
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "orator-static-server",
3
- "version": "2.0.3",
3
+ "version": "2.1.1",
4
4
  "description": "Static file serving for Orator API servers.",
5
5
  "main": "source/Orator-Static-Server.js",
6
6
  "scripts": {
7
- "test": "./node_modules/mocha/bin/_mocha --exit -u tdd -R spec",
8
- "coverage": "./node_modules/.bin/nyc --reporter=lcov --reporter=text-lcov ./node_modules/mocha/bin/_mocha -- -u tdd -R spec",
7
+ "test": "npx quack test",
8
+ "coverage": "npx quack coverage",
9
9
  "start": "node debug/Harness.js",
10
- "tests": "npx mocha -u tdd --exit -R spec --grep"
10
+ "tests": "npx quack test -g"
11
11
  },
12
12
  "mocha": {
13
13
  "diff": true,
@@ -38,17 +38,16 @@
38
38
  },
39
39
  "homepage": "https://github.com/stevenvelozo/orator-static-server#readme",
40
40
  "dependencies": {
41
- "fable-serviceproviderbase": "^3.0.18",
42
- "finalhandler": "^1.3.1",
43
- "mime": "^3.0.0",
44
- "serve-static": "^1.16.2"
41
+ "fable-serviceproviderbase": "^3.0.19",
42
+ "finalhandler": "^2.1.1",
43
+ "mime": "^4.1.0",
44
+ "serve-static": "^2.2.1"
45
45
  },
46
46
  "devDependencies": {
47
- "chai": "^4.3.7",
48
- "fable": "^3.1.57",
49
- "mocha": "^10.2.0",
50
- "orator": "^6.0.1",
51
- "orator-serviceserver-restify": "^2.0.7",
52
- "quackage": "^1.0.51"
47
+ "fable": "^3.1.71",
48
+ "orator": "^6.0.4",
49
+ "orator-serviceserver-restify": "^2.0.10",
50
+ "pict-docuserve": "^0.1.5",
51
+ "quackage": "^1.1.2"
53
52
  }
54
53
  }
@@ -183,6 +183,127 @@ class OratorStaticServer extends libFableServiceProviderBase
183
183
  });
184
184
  return true;
185
185
  }
186
+
187
+ /**
188
+ * Like addStaticRoute, but any file listed in pFallbackMap that isn't
189
+ * present on disk produces a 302 redirect to the mapped URL instead of
190
+ * a 404. Useful for offering CDN fallbacks on runtime assets that are
191
+ * optionally committed alongside the build output.
192
+ *
193
+ * addStaticRouteWithFallbacks(
194
+ * './web-application/', null, '/pict/*', '/pict/', null,
195
+ * { 'pict.min.js': 'https://unpkg.com/pict/dist/pict.min.js' });
196
+ *
197
+ * Files present locally are served normally (MIME types, caching, etc.
198
+ * all via serve-static). Files absent locally that ARE in the map are
199
+ * redirected. Files absent locally that are NOT in the map 404 as usual.
200
+ *
201
+ * This variant does not use the service server's built-in serveStatic
202
+ * plugin (e.g. restify.plugins.serveStatic) because that plugin does
203
+ * not expose a "not found" hook; it always finalises the response.
204
+ *
205
+ * @param {string} pFilePath - The path on disk to serve files from.
206
+ * @param {string} [pDefaultFile='index.html'] - The default file for directory requests.
207
+ * @param {string} [pRoute='/*'] - The route pattern to match.
208
+ * @param {string} [pRouteStrip='/'] - URL prefix to strip before filesystem lookup.
209
+ * @param {object} [pParams={}] - Additional parameters passed to serve-static.
210
+ * @param {Object<string,string>} [pFallbackMap={}] - Map of relative-path (under the route prefix) to absolute URL.
211
+ * @returns {boolean} true if the route was successfully installed.
212
+ */
213
+ addStaticRouteWithFallbacks(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams, pFallbackMap)
214
+ {
215
+ if (!this.fable.Orator)
216
+ {
217
+ this.log.error('OratorStaticServer requires an Orator instance to be registered with Fable.');
218
+ return false;
219
+ }
220
+ if (typeof(pFilePath) !== 'string')
221
+ {
222
+ this.fable.log.error('A file path must be passed in as part of the server.');
223
+ return false;
224
+ }
225
+
226
+ const tmpRoute = (typeof(pRoute) === 'undefined') ? '/*' : pRoute;
227
+ const tmpRouteStrip = (typeof(pRouteStrip) === 'undefined') ? '/' : pRouteStrip;
228
+ const tmpDefaultFile = (typeof(pDefaultFile) === 'undefined') ? 'index.html' : pDefaultFile;
229
+ const tmpFallbackMap = pFallbackMap || {};
230
+
231
+ let tmpOrator = this.fable.Orator;
232
+
233
+ this.fable.log.info('Orator mapping static+fallback route to files: '
234
+ + tmpRoute + ' ==> ' + pFilePath + ' ' + tmpDefaultFile
235
+ + ' (fallback entries: ' + Object.keys(tmpFallbackMap).length + ')');
236
+
237
+ if (!this.fable.FilePersistence)
238
+ {
239
+ this.fable.serviceManager.instantiateServiceProvider('FilePersistence');
240
+ }
241
+
242
+ tmpOrator.serviceServer.get(tmpRoute,
243
+ (pRequest, pResponse, fNext) =>
244
+ {
245
+ // Capture the relative path BEFORE the URL rewrite so we can
246
+ // look it up in the fallback map on a miss.
247
+ let tmpRelative = (pRequest.url || '').split('?')[0];
248
+ if (tmpRelative.indexOf(tmpRouteStrip) === 0)
249
+ {
250
+ tmpRelative = tmpRelative.slice(tmpRouteStrip.length);
251
+ }
252
+ try { tmpRelative = decodeURIComponent(tmpRelative); }
253
+ catch (pError) { /* non-fatal; will fall through to 404 */ }
254
+
255
+ // Magic subdomain subfolder check (mirrors addStaticRoute)
256
+ let tmpHostSet = pRequest.headers.host.split('.');
257
+ let tmpPotentialSubfolderMagicHost = false;
258
+ let servePath = pFilePath;
259
+ if (tmpHostSet.length > 1)
260
+ {
261
+ tmpPotentialSubfolderMagicHost = tmpHostSet[0];
262
+ }
263
+ if (tmpPotentialSubfolderMagicHost)
264
+ {
265
+ let tmpPotentialSubfolder = servePath + tmpPotentialSubfolderMagicHost;
266
+ if (this.fable.FilePersistence.libFS.existsSync(tmpPotentialSubfolder))
267
+ {
268
+ servePath = `${tmpPotentialSubfolder}/`;
269
+ }
270
+ }
271
+
272
+ pRequest.url = pRequest.url.split('?')[0].substr(tmpRouteStrip.length) || '/';
273
+ pRequest.path = function() { return pRequest.url; };
274
+
275
+ const tmpServe = libServeStatic(servePath, Object.assign({ index: tmpDefaultFile }, pParams));
276
+ tmpServe(pRequest, pResponse,
277
+ (pError) =>
278
+ {
279
+ // serve-static invokes this when it cannot serve a local
280
+ // file (missing, method not allowed, etc.). We get a
281
+ // chance to redirect before the default final handler
282
+ // would otherwise 404.
283
+ if (!pError && Object.prototype.hasOwnProperty.call(tmpFallbackMap, tmpRelative))
284
+ {
285
+ let tmpTarget = tmpFallbackMap[tmpRelative];
286
+ pResponse.statusCode = 302;
287
+ pResponse.setHeader('Location', tmpTarget);
288
+ pResponse.setHeader('Content-Type', 'text/plain; charset=utf-8');
289
+ pResponse.end('Redirecting to ' + tmpTarget + '\n');
290
+ return;
291
+ }
292
+ return libFinalHandler(pRequest, pResponse)(pError);
293
+ });
294
+ });
295
+
296
+ this.routes.push(
297
+ {
298
+ filePath: pFilePath,
299
+ defaultFile: tmpDefaultFile,
300
+ route: tmpRoute,
301
+ routeStrip: tmpRouteStrip,
302
+ params: pParams || {},
303
+ fallbackMap: tmpFallbackMap,
304
+ });
305
+ return true;
306
+ }
186
307
  }
187
308
 
188
309
  module.exports = OratorStaticServer;