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.59",
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.23.9",
54
- "@babel/preset-env": "^7.23.9",
55
- "browserify": "^17.0.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.28",
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
- // --- Serve examples ---
387
+ // --- Bookstore API initialization ---
384
388
 
385
- serveExamples(pExamplesFolder, pPort, fCallback)
389
+ initializeBookstoreAPI(pPort, pDataFolder, fCallback)
386
390
  {
387
- let tmpExamplesFolder = libPath.resolve(pExamplesFolder || './example_applications');
388
- let tmpProjectName = this.fable.AppData.Package.name || 'unknown-project';
389
- let tmpPort = pPort || this.hashProjectNameToPort(tmpProjectName);
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
- this.log.info(`Scanning for example applications in [${tmpExamplesFolder}] ...`);
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 tmpExamples = this.gatherServableExamples(tmpExamplesFolder);
409
+ let tmpHarnessFable = new libFable(tmpSettings);
394
410
 
395
- if (tmpExamples.length < 1)
396
- {
397
- this.log.warn(`No servable examples found in [${tmpExamplesFolder}]. Looking for subfolders with dist/index.html or index.html.`);
398
- return fCallback();
399
- }
411
+ // Register the bookstore schema provider
412
+ tmpHarnessFable.serviceManager.addServiceType('HarnessSchemaProvider', libBookstoreSchema);
413
+ tmpHarnessFable.serviceManager.instantiateServiceProvider('HarnessSchemaProvider');
400
414
 
401
- this.log.info(`Found ${tmpExamples.length} servable example(s).`);
415
+ // Register the SQLite provider configurator
416
+ tmpHarnessFable.serviceManager.addServiceType('MeadowProviderConfigurator', libSQLiteProvider);
417
+ tmpHarnessFable.serviceManager.instantiateServiceProvider('MeadowProviderConfigurator');
402
418
 
403
- let tmpIndexHTML = this.generateIndexHTML(tmpProjectName, tmpExamples, tmpPort);
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(tmpIndexHTML);
446
+ pResponse.end(pIndexHTML);
414
447
  return;
415
448
  }
416
449
 
417
- let tmpFilePath = libPath.join(tmpExamplesFolder, decodeURIComponent(tmpRequestURL));
450
+ let tmpFilePath = libPath.join(pExamplesFolder, decodeURIComponent(tmpRequestURL));
418
451
 
419
- if (!tmpFilePath.startsWith(tmpExamplesFolder))
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(tmpPort,
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 ${tmpPort} is already in use. Try specifying a different port with -p.`);
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
- // Keep the process running (don't call fCallback -- server is long-lived)
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