orator 5.0.2 → 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.
- package/docs/index.html +36 -48
- package/package.json +6 -7
- package/source/Orator.js +38 -97
- package/test/Orator_static_serving_tests.js +79 -46
package/docs/index.html
CHANGED
|
@@ -1,51 +1,39 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
<!-- Docsify v4 -->
|
|
40
|
-
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
|
41
|
-
<!-- Syntax highlighting -->
|
|
42
|
-
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-javascript.min.js"></script>
|
|
43
|
-
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
|
|
44
|
-
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
|
|
45
|
-
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-markdown.min.js"></script>
|
|
46
|
-
<!-- Search plugin -->
|
|
47
|
-
<script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js"></script>
|
|
48
|
-
<!-- Copy code plugin -->
|
|
49
|
-
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
|
|
50
|
-
</body>
|
|
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>
|
|
51
39
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orator",
|
|
3
|
-
"version": "
|
|
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.1.
|
|
55
|
-
"quackage": "^1.0.
|
|
54
|
+
"fable": "^3.1.53",
|
|
55
|
+
"quackage": "^1.0.48"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"fable-serviceproviderbase": "^3.0.
|
|
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.
|
|
62
|
-
"
|
|
60
|
+
"orator-serviceserver-base": "^1.0.2",
|
|
61
|
+
"orator-static-server": "^2.0.0"
|
|
63
62
|
}
|
|
64
63
|
}
|
package/source/Orator.js
CHANGED
|
@@ -11,9 +11,7 @@ const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
|
11
11
|
|
|
12
12
|
const libDefaultOratorServiceServer = require('./Orator-Default-ServiceServer.js');
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
const libFinalHandler = require('finalhandler');
|
|
16
|
-
const libMime = require('mime');
|
|
14
|
+
const libOratorStaticServer = require('orator-static-server');
|
|
17
15
|
|
|
18
16
|
const defaultOratorConfiguration = require('./Orator-Default-Configuration.js');
|
|
19
17
|
|
|
@@ -59,21 +57,6 @@ class Orator extends libFableServiceProviderBase
|
|
|
59
57
|
{
|
|
60
58
|
this.options.Product = defaultOratorConfiguration.Product;
|
|
61
59
|
}
|
|
62
|
-
|
|
63
|
-
// This is here because libMime has a breaking change from v1 to v2 and the lookup function was update to be getType per https://stackoverflow.com/a/60741078
|
|
64
|
-
// We don't want to introspect properties on this library every single time we need to check mime types.
|
|
65
|
-
// Therefore we are setting this boolean here and using it to branch.
|
|
66
|
-
this.oldLibMime = false;
|
|
67
|
-
if ('lookup' in libMime)
|
|
68
|
-
{
|
|
69
|
-
this.oldLibMime = true;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Ensure FilePersistence is available for the magic subdomain subfolder check in addStaticRoute
|
|
73
|
-
if (!this.fable.FilePersistence)
|
|
74
|
-
{
|
|
75
|
-
this.fable.serviceManager.instantiateServiceProvider('FilePersistence');
|
|
76
|
-
}
|
|
77
60
|
}
|
|
78
61
|
|
|
79
62
|
/**
|
|
@@ -368,29 +351,46 @@ class Orator extends libFableServiceProviderBase
|
|
|
368
351
|
* End of Legacy Orator Functions
|
|
369
352
|
*/
|
|
370
353
|
|
|
371
|
-
|
|
354
|
+
/*
|
|
355
|
+
* Backwards Compatibility: setMimeHeader and oldLibMime
|
|
356
|
+
*
|
|
357
|
+
* These used to live directly on Orator but now live on OratorStaticServer.
|
|
358
|
+
* These thin wrappers delegate to the OratorStaticServer instance so that
|
|
359
|
+
* any code calling orator.setMimeHeader() or reading orator.oldLibMime
|
|
360
|
+
* continues to work without changes.
|
|
361
|
+
*************************************************************************/
|
|
362
|
+
/**
|
|
363
|
+
* @deprecated Use fable.OratorStaticServer.oldLibMime instead.
|
|
364
|
+
*/
|
|
365
|
+
get oldLibMime()
|
|
372
366
|
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
if (this.oldLibMime)
|
|
376
|
-
{
|
|
377
|
-
tmpHeader = libMime.lookup(pFileName);
|
|
378
|
-
}
|
|
379
|
-
else
|
|
367
|
+
if (this.fable.OratorStaticServer)
|
|
380
368
|
{
|
|
381
|
-
|
|
369
|
+
return this.fable.OratorStaticServer.oldLibMime;
|
|
382
370
|
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
383
373
|
|
|
384
|
-
|
|
374
|
+
/**
|
|
375
|
+
* @deprecated Use fable.OratorStaticServer.setMimeHeader() instead.
|
|
376
|
+
*/
|
|
377
|
+
setMimeHeader(pFileName, pResponse)
|
|
378
|
+
{
|
|
379
|
+
if (!this.fable.OratorStaticServer)
|
|
385
380
|
{
|
|
386
|
-
|
|
381
|
+
// Force auto-registration so the method is available
|
|
382
|
+
if (!this.fable.serviceManager.servicesMap.hasOwnProperty('OratorStaticServer'))
|
|
383
|
+
{
|
|
384
|
+
this.fable.serviceManager.addServiceType('OratorStaticServer', libOratorStaticServer);
|
|
385
|
+
}
|
|
386
|
+
this.fable.serviceManager.instantiateServiceProvider('OratorStaticServer', {}, 'OratorStaticServer-AutoInit');
|
|
387
387
|
}
|
|
388
|
-
|
|
389
|
-
pResponse.setHeader('Content-Type', tmpHeader);
|
|
388
|
+
return this.fable.OratorStaticServer.setMimeHeader(pFileName, pResponse);
|
|
390
389
|
}
|
|
391
390
|
|
|
392
391
|
/**
|
|
393
392
|
* Serve a static folder, optionally with magic subdomain masking.
|
|
393
|
+
* Delegates to the OratorStaticServer service provider.
|
|
394
394
|
*
|
|
395
395
|
* @param {string} pFilePath The path on disk that we are serving files from.
|
|
396
396
|
* @param {string?} pDefaultFile (optional) The default file served if no specific file is requested.
|
|
@@ -401,78 +401,19 @@ class Orator extends libFableServiceProviderBase
|
|
|
401
401
|
*/
|
|
402
402
|
addStaticRoute(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams)
|
|
403
403
|
{
|
|
404
|
-
if
|
|
404
|
+
// Auto-register the OratorStaticServer service type if it hasn't been registered yet
|
|
405
|
+
if (!this.fable.serviceManager.servicesMap.hasOwnProperty('OratorStaticServer'))
|
|
405
406
|
{
|
|
406
|
-
|
|
407
|
-
return false;
|
|
407
|
+
this.fable.serviceManager.addServiceType('OratorStaticServer', libOratorStaticServer);
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
-
//
|
|
411
|
-
|
|
412
|
-
const tmpRouteStrip = (typeof(pRouteStrip) === 'undefined') ? '/' : pRouteStrip;
|
|
413
|
-
|
|
414
|
-
// Default to serving index.html
|
|
415
|
-
const tmpDefaultFile = (typeof(pDefaultFile) === 'undefined') ? 'index.html' : pDefaultFile;
|
|
416
|
-
|
|
417
|
-
this.fable.log.info('Orator mapping static route to files: '+tmpRoute+' ==> '+pFilePath+' '+tmpDefaultFile);
|
|
418
|
-
|
|
419
|
-
// Try the service server's built-in serveStatic first (e.g. restify's serveStaticFiles plugin).
|
|
420
|
-
// This handles MIME types, caching headers, and streaming correctly without our manual intervention.
|
|
421
|
-
if (typeof(this.serviceServer.serveStatic) === 'function')
|
|
410
|
+
// Auto-instantiate a default OratorStaticServer instance if none exists
|
|
411
|
+
if (!this.fable.OratorStaticServer)
|
|
422
412
|
{
|
|
423
|
-
|
|
424
|
-
if (this.serviceServer.serveStatic(tmpRoute, tmpServeStaticOptions))
|
|
425
|
-
{
|
|
426
|
-
return true;
|
|
427
|
-
}
|
|
413
|
+
this.fable.serviceManager.instantiateServiceProvider('OratorStaticServer', {}, 'OratorStaticServer-AutoInit');
|
|
428
414
|
}
|
|
429
415
|
|
|
430
|
-
|
|
431
|
-
// service servers that don't have a built-in serveStatic implementation).
|
|
432
|
-
this.serviceServer.get(tmpRoute,
|
|
433
|
-
(pRequest, pResponse, fNext) =>
|
|
434
|
-
{
|
|
435
|
-
// See if there is a magic subdomain put at the beginning of a request.
|
|
436
|
-
// If there is, then we need to see if there is a subfolder and add that to the file path
|
|
437
|
-
let tmpHostSet = pRequest.headers.host.split('.');
|
|
438
|
-
let tmpPotentialSubfolderMagicHost = false;
|
|
439
|
-
let servePath = pFilePath;
|
|
440
|
-
// Check if there are more than one host in the host header (this will be 127 a lot)
|
|
441
|
-
if (tmpHostSet.length > 1)
|
|
442
|
-
{
|
|
443
|
-
tmpPotentialSubfolderMagicHost = tmpHostSet[0];
|
|
444
|
-
}
|
|
445
|
-
if (tmpPotentialSubfolderMagicHost)
|
|
446
|
-
{
|
|
447
|
-
// Check if the subfolder exists -- this is only one dimensional for now
|
|
448
|
-
let tmpPotentialSubfolder = servePath + tmpPotentialSubfolderMagicHost;
|
|
449
|
-
if (this.fable.FilePersistence.libFS.existsSync(tmpPotentialSubfolder))
|
|
450
|
-
{
|
|
451
|
-
// If it does, then we need to add it to the file path
|
|
452
|
-
servePath = `${tmpPotentialSubfolder}/`;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
pRequest.url = pRequest.url.split('?')[0].substr(tmpRouteStrip.length) || '/';
|
|
456
|
-
pRequest.path = function()
|
|
457
|
-
{
|
|
458
|
-
return pRequest.url;
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
// When the URL is a directory (e.g. '/' or '/docs/'), use the default file for MIME detection
|
|
462
|
-
// so the browser gets text/html instead of application/octet-stream
|
|
463
|
-
let tmpMimeTarget = pRequest.url;
|
|
464
|
-
if (tmpMimeTarget.endsWith('/') || tmpMimeTarget.indexOf('.') < 0)
|
|
465
|
-
{
|
|
466
|
-
tmpMimeTarget = tmpDefaultFile;
|
|
467
|
-
}
|
|
468
|
-
this.setMimeHeader(tmpMimeTarget, pResponse);
|
|
469
|
-
|
|
470
|
-
const tmpServe = libServeStatic(servePath, Object.assign({ index: tmpDefaultFile }, pParams));
|
|
471
|
-
tmpServe(pRequest, pResponse, libFinalHandler(pRequest, pResponse));
|
|
472
|
-
// TODO: This may break things if a post request send handler is setup...
|
|
473
|
-
//fNext();
|
|
474
|
-
});
|
|
475
|
-
return true;
|
|
416
|
+
return this.fable.OratorStaticServer.addStaticRoute(pFilePath, pDefaultFile, pRoute, pRouteStrip, pParams);
|
|
476
417
|
}
|
|
477
418
|
}
|
|
478
419
|
|
|
@@ -264,13 +264,17 @@ suite
|
|
|
264
264
|
tmpOrator.initialize(
|
|
265
265
|
() =>
|
|
266
266
|
{
|
|
267
|
+
// Trigger auto-registration of OratorStaticServer
|
|
268
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
269
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
270
|
+
|
|
267
271
|
let tmpCapturedHeaders = {};
|
|
268
272
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
269
273
|
|
|
270
|
-
|
|
274
|
+
tmpStaticServer.setMimeHeader('index.html', tmpMockResponse);
|
|
271
275
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
272
276
|
|
|
273
|
-
|
|
277
|
+
tmpStaticServer.setMimeHeader('/path/to/page.html', tmpMockResponse);
|
|
274
278
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
275
279
|
|
|
276
280
|
tmpOrator.log.info('HTML MIME type correctly detected');
|
|
@@ -290,10 +294,13 @@ suite
|
|
|
290
294
|
tmpOrator.initialize(
|
|
291
295
|
() =>
|
|
292
296
|
{
|
|
297
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
298
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
299
|
+
|
|
293
300
|
let tmpCapturedHeaders = {};
|
|
294
301
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
295
302
|
|
|
296
|
-
|
|
303
|
+
tmpStaticServer.setMimeHeader('style.css', tmpMockResponse);
|
|
297
304
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/css');
|
|
298
305
|
|
|
299
306
|
tmpOrator.log.info('CSS MIME type correctly detected');
|
|
@@ -313,10 +320,13 @@ suite
|
|
|
313
320
|
tmpOrator.initialize(
|
|
314
321
|
() =>
|
|
315
322
|
{
|
|
323
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
324
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
325
|
+
|
|
316
326
|
let tmpCapturedHeaders = {};
|
|
317
327
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
318
328
|
|
|
319
|
-
|
|
329
|
+
tmpStaticServer.setMimeHeader('data.json', tmpMockResponse);
|
|
320
330
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/json');
|
|
321
331
|
|
|
322
332
|
tmpOrator.log.info('JSON MIME type correctly detected');
|
|
@@ -336,10 +346,13 @@ suite
|
|
|
336
346
|
tmpOrator.initialize(
|
|
337
347
|
() =>
|
|
338
348
|
{
|
|
349
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
350
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
351
|
+
|
|
339
352
|
let tmpCapturedHeaders = {};
|
|
340
353
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
341
354
|
|
|
342
|
-
|
|
355
|
+
tmpStaticServer.setMimeHeader('app.js', tmpMockResponse);
|
|
343
356
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/javascript');
|
|
344
357
|
|
|
345
358
|
tmpOrator.log.info('JavaScript MIME type correctly detected');
|
|
@@ -359,19 +372,22 @@ suite
|
|
|
359
372
|
tmpOrator.initialize(
|
|
360
373
|
() =>
|
|
361
374
|
{
|
|
375
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
376
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
377
|
+
|
|
362
378
|
let tmpCapturedHeaders = {};
|
|
363
379
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
364
380
|
|
|
365
|
-
|
|
381
|
+
tmpStaticServer.setMimeHeader('photo.png', tmpMockResponse);
|
|
366
382
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/png');
|
|
367
383
|
|
|
368
|
-
|
|
384
|
+
tmpStaticServer.setMimeHeader('logo.jpg', tmpMockResponse);
|
|
369
385
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/jpeg');
|
|
370
386
|
|
|
371
|
-
|
|
387
|
+
tmpStaticServer.setMimeHeader('icon.gif', tmpMockResponse);
|
|
372
388
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/gif');
|
|
373
389
|
|
|
374
|
-
|
|
390
|
+
tmpStaticServer.setMimeHeader('vector.svg', tmpMockResponse);
|
|
375
391
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/svg+xml');
|
|
376
392
|
|
|
377
393
|
tmpOrator.log.info('Image MIME types correctly detected');
|
|
@@ -391,19 +407,22 @@ suite
|
|
|
391
407
|
tmpOrator.initialize(
|
|
392
408
|
() =>
|
|
393
409
|
{
|
|
410
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
411
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
412
|
+
|
|
394
413
|
let tmpCapturedHeaders = {};
|
|
395
414
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
396
415
|
|
|
397
|
-
|
|
416
|
+
tmpStaticServer.setMimeHeader('document.pdf', tmpMockResponse);
|
|
398
417
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/pdf');
|
|
399
418
|
|
|
400
|
-
|
|
419
|
+
tmpStaticServer.setMimeHeader('archive.zip', tmpMockResponse);
|
|
401
420
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/zip');
|
|
402
421
|
|
|
403
|
-
|
|
422
|
+
tmpStaticServer.setMimeHeader('data.xml', tmpMockResponse);
|
|
404
423
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/xml');
|
|
405
424
|
|
|
406
|
-
|
|
425
|
+
tmpStaticServer.setMimeHeader('readme.txt', tmpMockResponse);
|
|
407
426
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/plain');
|
|
408
427
|
|
|
409
428
|
tmpOrator.log.info('Document and archive MIME types correctly detected');
|
|
@@ -423,13 +442,16 @@ suite
|
|
|
423
442
|
tmpOrator.initialize(
|
|
424
443
|
() =>
|
|
425
444
|
{
|
|
445
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
446
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
447
|
+
|
|
426
448
|
let tmpCapturedHeaders = {};
|
|
427
449
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
428
450
|
|
|
429
|
-
|
|
451
|
+
tmpStaticServer.setMimeHeader('mystery.xyz123', tmpMockResponse);
|
|
430
452
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/octet-stream');
|
|
431
453
|
|
|
432
|
-
|
|
454
|
+
tmpStaticServer.setMimeHeader('noextension', tmpMockResponse);
|
|
433
455
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('application/octet-stream');
|
|
434
456
|
|
|
435
457
|
tmpOrator.log.info('Unknown MIME types fall back to application/octet-stream');
|
|
@@ -449,13 +471,16 @@ suite
|
|
|
449
471
|
tmpOrator.initialize(
|
|
450
472
|
() =>
|
|
451
473
|
{
|
|
474
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
475
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
476
|
+
|
|
452
477
|
let tmpCapturedHeaders = {};
|
|
453
478
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
454
479
|
|
|
455
|
-
|
|
480
|
+
tmpStaticServer.setMimeHeader('/assets/css/main.css', tmpMockResponse);
|
|
456
481
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/css');
|
|
457
482
|
|
|
458
|
-
|
|
483
|
+
tmpStaticServer.setMimeHeader('/deep/nested/path/to/image.png', tmpMockResponse);
|
|
459
484
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('image/png');
|
|
460
485
|
|
|
461
486
|
tmpOrator.log.info('MIME type detected correctly from paths with directories');
|
|
@@ -645,6 +670,10 @@ suite
|
|
|
645
670
|
tmpOrator.initialize(
|
|
646
671
|
() =>
|
|
647
672
|
{
|
|
673
|
+
// Trigger auto-registration of OratorStaticServer
|
|
674
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
675
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
676
|
+
|
|
648
677
|
// Test the MIME detection logic directly with a mock response
|
|
649
678
|
let tmpCapturedHeaders = {};
|
|
650
679
|
let tmpMockResponse = { setHeader: function(pName, pValue) { tmpCapturedHeaders[pName] = pValue; } };
|
|
@@ -657,7 +686,7 @@ suite
|
|
|
657
686
|
{
|
|
658
687
|
tmpMimeTarget = 'index.html';
|
|
659
688
|
}
|
|
660
|
-
|
|
689
|
+
tmpStaticServer.setMimeHeader(tmpMimeTarget, tmpMockResponse);
|
|
661
690
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
662
691
|
|
|
663
692
|
// A URL like '/somepath' (no extension, no slash) should also fall back
|
|
@@ -667,7 +696,7 @@ suite
|
|
|
667
696
|
{
|
|
668
697
|
tmpMimeTarget = 'index.html';
|
|
669
698
|
}
|
|
670
|
-
|
|
699
|
+
tmpStaticServer.setMimeHeader(tmpMimeTarget, tmpMockResponse);
|
|
671
700
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/html');
|
|
672
701
|
|
|
673
702
|
// A URL with an extension should use its own extension
|
|
@@ -677,7 +706,7 @@ suite
|
|
|
677
706
|
{
|
|
678
707
|
tmpMimeTarget = 'index.html';
|
|
679
708
|
}
|
|
680
|
-
|
|
709
|
+
tmpStaticServer.setMimeHeader(tmpMimeTarget, tmpMockResponse);
|
|
681
710
|
Expect(tmpCapturedHeaders['Content-Type']).to.equal('text/css');
|
|
682
711
|
|
|
683
712
|
tmpOrator.log.info('Directory and extensionless MIME detection verified');
|
|
@@ -695,7 +724,7 @@ suite
|
|
|
695
724
|
{
|
|
696
725
|
test
|
|
697
726
|
(
|
|
698
|
-
'
|
|
727
|
+
'addStaticRoute should auto-instantiate FilePersistence if not present',
|
|
699
728
|
(fDone) =>
|
|
700
729
|
{
|
|
701
730
|
let tmpFable = new libFable(defaultFableSettings);
|
|
@@ -704,19 +733,24 @@ suite
|
|
|
704
733
|
|
|
705
734
|
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
706
735
|
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
736
|
+
tmpOrator.initialize(
|
|
737
|
+
() =>
|
|
738
|
+
{
|
|
739
|
+
// FilePersistence is auto-instantiated by OratorStaticServer.addStaticRoute
|
|
740
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
707
741
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
742
|
+
Expect(tmpFable.FilePersistence).to.be.an('object');
|
|
743
|
+
Expect(tmpFable.FilePersistence.libFS).to.be.an('object');
|
|
744
|
+
Expect(tmpFable.FilePersistence.libFS.existsSync).to.be.a('function');
|
|
745
|
+
tmpOrator.log.info('FilePersistence auto-instantiated by addStaticRoute');
|
|
746
|
+
return fDone();
|
|
747
|
+
});
|
|
714
748
|
}
|
|
715
749
|
);
|
|
716
750
|
|
|
717
751
|
test
|
|
718
752
|
(
|
|
719
|
-
'
|
|
753
|
+
'addStaticRoute should not re-instantiate FilePersistence if already present',
|
|
720
754
|
(fDone) =>
|
|
721
755
|
{
|
|
722
756
|
let tmpFable = new libFable(defaultFableSettings);
|
|
@@ -726,11 +760,16 @@ suite
|
|
|
726
760
|
|
|
727
761
|
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
728
762
|
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
763
|
+
tmpOrator.initialize(
|
|
764
|
+
() =>
|
|
765
|
+
{
|
|
766
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
729
767
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
768
|
+
// Should still be the same instance, not a new one
|
|
769
|
+
Expect(tmpFable.FilePersistence).to.equal(tmpOriginal);
|
|
770
|
+
tmpOrator.log.info('FilePersistence preserved when already present');
|
|
771
|
+
return fDone();
|
|
772
|
+
});
|
|
734
773
|
}
|
|
735
774
|
);
|
|
736
775
|
}
|
|
@@ -1089,7 +1128,7 @@ suite
|
|
|
1089
1128
|
(fDone) =>
|
|
1090
1129
|
{
|
|
1091
1130
|
let tmpFable = new libFable(defaultFableSettings);
|
|
1092
|
-
// FilePersistence is now auto-instantiated by
|
|
1131
|
+
// FilePersistence is now auto-instantiated by OratorStaticServer.addStaticRoute
|
|
1093
1132
|
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1094
1133
|
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1095
1134
|
tmpOrator.startService();
|
|
@@ -1123,7 +1162,7 @@ suite
|
|
|
1123
1162
|
(fDone) =>
|
|
1124
1163
|
{
|
|
1125
1164
|
let tmpFable = new libFable(defaultFableSettings);
|
|
1126
|
-
// FilePersistence is now auto-instantiated by
|
|
1165
|
+
// FilePersistence is now auto-instantiated by OratorStaticServer.addStaticRoute
|
|
1127
1166
|
tmpFable.serviceManager.addServiceType('Orator', libOrator);
|
|
1128
1167
|
let tmpOrator = tmpFable.serviceManager.instantiateServiceProvider('Orator', {});
|
|
1129
1168
|
tmpOrator.startService();
|
|
@@ -1299,19 +1338,13 @@ suite
|
|
|
1299
1338
|
tmpOrator.initialize(
|
|
1300
1339
|
() =>
|
|
1301
1340
|
{
|
|
1341
|
+
// Trigger auto-registration of OratorStaticServer
|
|
1342
|
+
tmpOrator.addStaticRoute(_StaticContentPath);
|
|
1343
|
+
let tmpStaticServer = tmpFable.OratorStaticServer;
|
|
1344
|
+
|
|
1302
1345
|
// The oldLibMime flag should be a boolean
|
|
1303
|
-
Expect(
|
|
1304
|
-
|
|
1305
|
-
let libMime = require('mime');
|
|
1306
|
-
if ('lookup' in libMime)
|
|
1307
|
-
{
|
|
1308
|
-
Expect(tmpOrator.oldLibMime).to.equal(true);
|
|
1309
|
-
}
|
|
1310
|
-
else
|
|
1311
|
-
{
|
|
1312
|
-
Expect(tmpOrator.oldLibMime).to.equal(false);
|
|
1313
|
-
}
|
|
1314
|
-
tmpOrator.log.info(`oldLibMime flag is ${tmpOrator.oldLibMime}`);
|
|
1346
|
+
Expect(tmpStaticServer.oldLibMime).to.be.a('boolean');
|
|
1347
|
+
tmpOrator.log.info(`oldLibMime flag is ${tmpStaticServer.oldLibMime}`);
|
|
1315
1348
|
return fDone();
|
|
1316
1349
|
});
|
|
1317
1350
|
}
|