quackage 1.0.59 → 1.0.61
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.
|
@@ -90,6 +90,15 @@ libGulp.task('minified',
|
|
|
90
90
|
debug: true
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
// Ignore modules that should not be bundled (e.g. WASM loaders loaded via <script> tag)
|
|
94
|
+
if (Array.isArray(_CONFIG.BrowserifyIgnore))
|
|
95
|
+
{
|
|
96
|
+
for (var i = 0; i < _CONFIG.BrowserifyIgnore.length; i++)
|
|
97
|
+
{
|
|
98
|
+
tmpBrowserify.ignore(_CONFIG.BrowserifyIgnore[i]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
93
102
|
return tmpBrowserify.bundle()
|
|
94
103
|
.pipe(libVinylSourceStream(_CONFIG.LibraryMinifiedFileName))
|
|
95
104
|
.pipe(libVinylBuffer())
|
|
@@ -113,6 +122,15 @@ libGulp.task('debug',
|
|
|
113
122
|
debug: true
|
|
114
123
|
});
|
|
115
124
|
|
|
125
|
+
// Ignore modules that should not be bundled (e.g. WASM loaders loaded via <script> tag)
|
|
126
|
+
if (Array.isArray(_CONFIG.BrowserifyIgnore))
|
|
127
|
+
{
|
|
128
|
+
for (var i = 0; i < _CONFIG.BrowserifyIgnore.length; i++)
|
|
129
|
+
{
|
|
130
|
+
tmpBrowserify.ignore(_CONFIG.BrowserifyIgnore[i]);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
116
134
|
return tmpBrowserify.bundle()
|
|
117
135
|
.pipe(libVinylSourceStream(_CONFIG.LibraryUniminifiedFileName))
|
|
118
136
|
.pipe(libVinylBuffer())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quackage",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.61",
|
|
4
4
|
"description": "Building. Testing. Quacking. Reloading.",
|
|
5
5
|
"main": "source/Quackage-CLIProgram.js",
|
|
6
6
|
"scripts": {
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
},
|
|
51
51
|
"homepage": "https://github.com/stevenvelozo/quackage",
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@babel/core": "^7.
|
|
54
|
-
"@babel/preset-env": "^7.
|
|
55
|
-
"browserify": "^17.0.
|
|
53
|
+
"@babel/core": "^7.29.0",
|
|
54
|
+
"@babel/preset-env": "^7.29.0",
|
|
55
|
+
"browserify": "^17.0.1",
|
|
56
56
|
"chai": "4.3.10",
|
|
57
57
|
"copy-files-from-to": "^3.11.0",
|
|
58
58
|
"gulp": "^4.0.2",
|
|
@@ -65,8 +65,9 @@
|
|
|
65
65
|
"mocha": "10.4.0",
|
|
66
66
|
"npm-check-updates": "^18.0.1",
|
|
67
67
|
"nyc": "^15.1.0",
|
|
68
|
-
"pict-docuserve": "^0.0.
|
|
68
|
+
"pict-docuserve": "^0.0.32",
|
|
69
69
|
"pict-service-commandlineutility": "^1.0.19",
|
|
70
|
+
"retold-harness": "^1.1.2",
|
|
70
71
|
"vinyl-buffer": "^1.0.1",
|
|
71
72
|
"vinyl-source-stream": "^2.0.0"
|
|
72
73
|
},
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"LibraryObjectName": "{~PascalCaseIdentifier:AppData.Package.name~}",
|
|
7
7
|
"LibraryOutputFolder": "{~Data:AppData.CWD~}/dist/",
|
|
8
8
|
"LibraryUniminifiedFileName": "{~Data:AppData.Package.name~}.{~Data:Record.BuildFileLabel~}js",
|
|
9
|
-
"LibraryMinifiedFileName": "{~Data:AppData.Package.name~}.{~Data:Record.BuildFileLabel~}min.js"
|
|
9
|
+
"LibraryMinifiedFileName": "{~Data:AppData.Package.name~}.{~Data:Record.BuildFileLabel~}min.js",
|
|
10
|
+
"BrowserifyIgnore": []
|
|
10
11
|
},
|
|
11
12
|
"WatchSettings": {
|
|
12
13
|
"MonitorFolders": ["./html", "./css", "./assets"],
|
|
@@ -3,6 +3,10 @@ const libFS = require('fs');
|
|
|
3
3
|
const libPath = require('path');
|
|
4
4
|
const libHTTP = require('http');
|
|
5
5
|
|
|
6
|
+
const libFable = require('fable');
|
|
7
|
+
const libBookstoreSchema = require('retold-harness/source/schemas/Retold-Harness-Service-Schema-Bookstore.js');
|
|
8
|
+
const libSQLiteProvider = require('retold-harness/source/providers/Retold-Harness-Service-Provider-SQLite.js');
|
|
9
|
+
|
|
6
10
|
class QuackageExampleService extends libPict.ServiceProviderBase
|
|
7
11
|
{
|
|
8
12
|
constructor(pFable, pManifest, pServiceHash)
|
|
@@ -380,28 +384,57 @@ ${tmpExampleListItems} </ul>
|
|
|
380
384
|
});
|
|
381
385
|
}
|
|
382
386
|
|
|
383
|
-
// ---
|
|
387
|
+
// --- Bookstore API initialization ---
|
|
384
388
|
|
|
385
|
-
|
|
389
|
+
initializeBookstoreAPI(pPort, pDataFolder, fCallback)
|
|
386
390
|
{
|
|
387
|
-
let
|
|
388
|
-
let
|
|
389
|
-
|
|
391
|
+
let tmpSQLitePath = libPath.join(pDataFolder, '.data', 'bookstore.sqlite');
|
|
392
|
+
let tmpDataDir = libPath.dirname(tmpSQLitePath);
|
|
393
|
+
if (!libFS.existsSync(tmpDataDir))
|
|
394
|
+
{
|
|
395
|
+
libFS.mkdirSync(tmpDataDir, { recursive: true });
|
|
396
|
+
}
|
|
390
397
|
|
|
391
|
-
|
|
398
|
+
let tmpSettings =
|
|
399
|
+
{
|
|
400
|
+
"Product": "QuackageExampleServer",
|
|
401
|
+
"ProductVersion": "1.0.0",
|
|
402
|
+
"APIServerPort": pPort,
|
|
403
|
+
"SQLite":
|
|
404
|
+
{
|
|
405
|
+
"SQLiteFilePath": tmpSQLitePath
|
|
406
|
+
}
|
|
407
|
+
};
|
|
392
408
|
|
|
393
|
-
let
|
|
409
|
+
let tmpHarnessFable = new libFable(tmpSettings);
|
|
394
410
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return fCallback();
|
|
399
|
-
}
|
|
411
|
+
// Register the bookstore schema provider
|
|
412
|
+
tmpHarnessFable.serviceManager.addServiceType('HarnessSchemaProvider', libBookstoreSchema);
|
|
413
|
+
tmpHarnessFable.serviceManager.instantiateServiceProvider('HarnessSchemaProvider');
|
|
400
414
|
|
|
401
|
-
|
|
415
|
+
// Register the SQLite provider configurator
|
|
416
|
+
tmpHarnessFable.serviceManager.addServiceType('MeadowProviderConfigurator', libSQLiteProvider);
|
|
417
|
+
tmpHarnessFable.serviceManager.instantiateServiceProvider('MeadowProviderConfigurator');
|
|
402
418
|
|
|
403
|
-
|
|
419
|
+
// Skip the default web UI route -- we will register our own routes after initialization
|
|
420
|
+
tmpHarnessFable.MeadowProviderConfigurator.serveWebUI = (fStepCallback) => { return fStepCallback(); };
|
|
421
|
+
|
|
422
|
+
// Initialize the harness (connects DB, creates tables, seeds data, registers API endpoints, starts Orator)
|
|
423
|
+
tmpHarnessFable.MeadowProviderConfigurator.initializeHarness(
|
|
424
|
+
(pError) =>
|
|
425
|
+
{
|
|
426
|
+
if (pError)
|
|
427
|
+
{
|
|
428
|
+
return fCallback(pError);
|
|
429
|
+
}
|
|
430
|
+
return fCallback(null, tmpHarnessFable);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// --- Serve static files via a raw HTTP server (fallback when retold-harness unavailable) ---
|
|
404
435
|
|
|
436
|
+
serveStaticHTTP(pExamplesFolder, pIndexHTML, pPort, fCallback)
|
|
437
|
+
{
|
|
405
438
|
let tmpServer = libHTTP.createServer(
|
|
406
439
|
(pRequest, pResponse) =>
|
|
407
440
|
{
|
|
@@ -410,13 +443,13 @@ ${tmpExampleListItems} </ul>
|
|
|
410
443
|
if (tmpRequestURL === '/' || tmpRequestURL === '/index.html')
|
|
411
444
|
{
|
|
412
445
|
pResponse.writeHead(200, { 'Content-Type': 'text/html' });
|
|
413
|
-
pResponse.end(
|
|
446
|
+
pResponse.end(pIndexHTML);
|
|
414
447
|
return;
|
|
415
448
|
}
|
|
416
449
|
|
|
417
|
-
let tmpFilePath = libPath.join(
|
|
450
|
+
let tmpFilePath = libPath.join(pExamplesFolder, decodeURIComponent(tmpRequestURL));
|
|
418
451
|
|
|
419
|
-
if (!tmpFilePath.startsWith(
|
|
452
|
+
if (!tmpFilePath.startsWith(pExamplesFolder))
|
|
420
453
|
{
|
|
421
454
|
pResponse.writeHead(403);
|
|
422
455
|
pResponse.end('Forbidden');
|
|
@@ -451,19 +484,10 @@ ${tmpExampleListItems} </ul>
|
|
|
451
484
|
}
|
|
452
485
|
});
|
|
453
486
|
|
|
454
|
-
tmpServer.listen(
|
|
487
|
+
tmpServer.listen(pPort,
|
|
455
488
|
() =>
|
|
456
489
|
{
|
|
457
|
-
this.log.info(
|
|
458
|
-
this.log.info(` Example server running at http://localhost:${tmpPort}/`);
|
|
459
|
-
this.log.info(` Project: ${tmpProjectName}`);
|
|
460
|
-
this.log.info(` Serving ${tmpExamples.length} example(s):`);
|
|
461
|
-
for (let i = 0; i < tmpExamples.length; i++)
|
|
462
|
-
{
|
|
463
|
-
this.log.info(` - ${tmpExamples[i].DisplayName}: http://localhost:${tmpPort}/${tmpExamples[i].RelativePath}`);
|
|
464
|
-
}
|
|
465
|
-
this.log.info(`##############################################`);
|
|
466
|
-
this.log.info(`Press Ctrl+C to stop.`);
|
|
490
|
+
this.log.info(` Static-only server running at http://localhost:${pPort}/`);
|
|
467
491
|
});
|
|
468
492
|
|
|
469
493
|
tmpServer.on('error',
|
|
@@ -471,7 +495,7 @@ ${tmpExampleListItems} </ul>
|
|
|
471
495
|
{
|
|
472
496
|
if (pError.code === 'EADDRINUSE')
|
|
473
497
|
{
|
|
474
|
-
this.log.error(`Port ${
|
|
498
|
+
this.log.error(`Port ${pPort} is already in use. Try specifying a different port with -p.`);
|
|
475
499
|
}
|
|
476
500
|
else
|
|
477
501
|
{
|
|
@@ -479,8 +503,113 @@ ${tmpExampleListItems} </ul>
|
|
|
479
503
|
}
|
|
480
504
|
return fCallback(pError);
|
|
481
505
|
});
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// --- Serve examples ---
|
|
509
|
+
|
|
510
|
+
serveExamples(pExamplesFolder, pPort, fCallback)
|
|
511
|
+
{
|
|
512
|
+
let tmpExamplesFolder = libPath.resolve(pExamplesFolder || './example_applications');
|
|
513
|
+
let tmpProjectName = this.fable.AppData.Package.name || 'unknown-project';
|
|
514
|
+
let tmpPort = pPort || this.hashProjectNameToPort(tmpProjectName);
|
|
515
|
+
|
|
516
|
+
this.log.info(`Scanning for example applications in [${tmpExamplesFolder}] ...`);
|
|
482
517
|
|
|
483
|
-
|
|
518
|
+
let tmpExamples = this.gatherServableExamples(tmpExamplesFolder);
|
|
519
|
+
|
|
520
|
+
if (tmpExamples.length < 1)
|
|
521
|
+
{
|
|
522
|
+
this.log.warn(`No servable examples found in [${tmpExamplesFolder}]. Looking for subfolders with dist/index.html or index.html.`);
|
|
523
|
+
return fCallback();
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
this.log.info(`Found ${tmpExamples.length} servable example(s).`);
|
|
527
|
+
|
|
528
|
+
let tmpIndexHTML = this.generateIndexHTML(tmpProjectName, tmpExamples, tmpPort);
|
|
529
|
+
|
|
530
|
+
// Initialize the bookstore API (retold-harness + SQLite)
|
|
531
|
+
this.log.info(`Initializing bookstore API with SQLite ...`);
|
|
532
|
+
this.initializeBookstoreAPI(tmpPort, tmpExamplesFolder,
|
|
533
|
+
(pError, pHarnessFable) =>
|
|
534
|
+
{
|
|
535
|
+
if (pError)
|
|
536
|
+
{
|
|
537
|
+
this.log.warn(`Bookstore API initialization failed: ${pError}`);
|
|
538
|
+
this.log.warn(`Falling back to static-only file server.`);
|
|
539
|
+
this.serveStaticHTTP(tmpExamplesFolder, tmpIndexHTML, tmpPort, fCallback);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// The Orator/Restify server is now running with all Meadow API endpoints.
|
|
544
|
+
// Add static file serving routes to it.
|
|
545
|
+
let tmpRestifyServer = pHarnessFable.OratorServiceServer.server;
|
|
546
|
+
|
|
547
|
+
// Serve the example index page at /
|
|
548
|
+
tmpRestifyServer.get('/',
|
|
549
|
+
(pRequest, pResponse, fNext) =>
|
|
550
|
+
{
|
|
551
|
+
pResponse.writeHead(200, { 'Content-Type': 'text/html' });
|
|
552
|
+
pResponse.end(tmpIndexHTML);
|
|
553
|
+
return fNext();
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Serve static example files as a catch-all (registered last so Meadow API routes take priority)
|
|
557
|
+
tmpRestifyServer.get('/*',
|
|
558
|
+
(pRequest, pResponse, fNext) =>
|
|
559
|
+
{
|
|
560
|
+
let tmpRequestURL = pRequest.url.split('?')[0];
|
|
561
|
+
let tmpFilePath = libPath.join(tmpExamplesFolder, decodeURIComponent(tmpRequestURL));
|
|
562
|
+
|
|
563
|
+
if (!tmpFilePath.startsWith(tmpExamplesFolder))
|
|
564
|
+
{
|
|
565
|
+
pResponse.writeHead(403);
|
|
566
|
+
pResponse.end('Forbidden');
|
|
567
|
+
return fNext(false);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (libFS.existsSync(tmpFilePath) && libFS.statSync(tmpFilePath).isDirectory())
|
|
571
|
+
{
|
|
572
|
+
tmpFilePath = libPath.join(tmpFilePath, 'index.html');
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (!libFS.existsSync(tmpFilePath))
|
|
576
|
+
{
|
|
577
|
+
pResponse.writeHead(404);
|
|
578
|
+
pResponse.end('Not Found');
|
|
579
|
+
return fNext(false);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
let tmpExtension = libPath.extname(tmpFilePath).toLowerCase();
|
|
583
|
+
let tmpMimeType = this.getMimeType(tmpExtension);
|
|
584
|
+
|
|
585
|
+
try
|
|
586
|
+
{
|
|
587
|
+
let tmpContent = libFS.readFileSync(tmpFilePath);
|
|
588
|
+
pResponse.writeHead(200, { 'Content-Type': tmpMimeType });
|
|
589
|
+
pResponse.end(tmpContent);
|
|
590
|
+
}
|
|
591
|
+
catch (pReadError)
|
|
592
|
+
{
|
|
593
|
+
pResponse.writeHead(500);
|
|
594
|
+
pResponse.end('Internal Server Error');
|
|
595
|
+
}
|
|
596
|
+
return fNext(false);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
this.log.info(`##############################################`);
|
|
600
|
+
this.log.info(` Example server running at http://localhost:${tmpPort}/`);
|
|
601
|
+
this.log.info(` Project: ${tmpProjectName}`);
|
|
602
|
+
this.log.info(` Bookstore API: http://localhost:${tmpPort}/1.0/`);
|
|
603
|
+
this.log.info(` Serving ${tmpExamples.length} example(s):`);
|
|
604
|
+
for (let i = 0; i < tmpExamples.length; i++)
|
|
605
|
+
{
|
|
606
|
+
this.log.info(` - ${tmpExamples[i].DisplayName}: http://localhost:${tmpPort}/${tmpExamples[i].RelativePath}`);
|
|
607
|
+
}
|
|
608
|
+
this.log.info(`##############################################`);
|
|
609
|
+
this.log.info(`Press Ctrl+C to stop.`);
|
|
610
|
+
|
|
611
|
+
// Keep the process running (don't call fCallback -- server is long-lived)
|
|
612
|
+
});
|
|
484
613
|
}
|
|
485
614
|
}
|
|
486
615
|
|