@sasjs/cli 6.30.0 → 6.31.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.
Files changed (23) hide show
  1. package/build/commands/build/internal/mockSite/assets/api-console.css +365 -0
  2. package/build/commands/build/internal/mockSite/assets/api-console.js +762 -0
  3. package/build/commands/build/internal/mockSite/assets/api-index.html +83 -0
  4. package/build/commands/build/internal/mockSite/assets/boot.js +23 -1
  5. package/build/commands/build/internal/mockSite/assets/persist.js +76 -5
  6. package/build/commands/build/internal/mockSite/assets/runtime-core.js +413 -50
  7. package/build/commands/build/internal/mockSite/assets/sw.js +156 -15
  8. package/build/commands/build/internal/mockSite/mockSite.d.ts +25 -0
  9. package/build/commands/build/internal/mockSite/mockSite.js +311 -24
  10. package/build/commands/build/internal/mockSite/spec/apiConsole.spec.d.ts +1 -0
  11. package/build/commands/build/internal/mockSite/spec/apiConsole.spec.js +1371 -0
  12. package/build/commands/build/internal/mockSite/spec/apiConsoleBasePath.spec.d.ts +1 -0
  13. package/build/commands/build/internal/mockSite/spec/apiConsoleBasePath.spec.js +303 -0
  14. package/build/commands/build/internal/mockSite/spec/boot.spec.js +50 -3
  15. package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +217 -3
  16. package/build/commands/build/internal/mockSite/spec/persist.spec.js +179 -1
  17. package/build/commands/build/internal/mockSite/spec/runtime-core.spec.js +546 -0
  18. package/build/commands/build/internal/mockSite/spec/sw.spec.js +407 -53
  19. package/build/commands/version/spec/version.spec.js +11 -1
  20. package/build/commands/version/version.d.ts +5 -3
  21. package/build/commands/version/version.js +17 -7
  22. package/build/types/mockWebBasePath.d.ts +9 -0
  23. package/package.json +13 -32
@@ -86,6 +86,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
86
86
  return (mod && mod.__esModule) ? mod : { "default": mod };
87
87
  };
88
88
  Object.defineProperty(exports, "__esModule", { value: true });
89
+ var fs_1 = __importDefault(require("fs"));
89
90
  var path_1 = __importDefault(require("path"));
90
91
  var api_1 = require("@sasjs/api");
91
92
  var os_1 = __importDefault(require("os"));
@@ -147,7 +148,11 @@ var loadWorker = function (base, options) {
147
148
  }
148
149
  };
149
150
  global.self = self;
151
+ // what the worker pulled into itself, so a test can assert that a script it
152
+ // does not need at boot is not fetched at boot
153
+ var imported = [];
150
154
  global.importScripts = function (path) {
155
+ imported.push(path);
151
156
  if (path.includes('md5'))
152
157
  return;
153
158
  if (path.includes('api-browser'))
@@ -185,7 +190,7 @@ var loadWorker = function (base, options) {
185
190
  };
186
191
  require('../assets/sw.js');
187
192
  global.fetch = jest.fn(function (input) { return __awaiter(void 0, void 0, void 0, function () {
188
- var target, fixture, fixture;
193
+ var target, fixture, fixture, shards, piece, id, requested, shards_1;
189
194
  var _a;
190
195
  return __generator(this, function (_b) {
191
196
  target = typeof input === 'string' ? input : (input && input.url) || String(input);
@@ -222,7 +227,29 @@ var loadWorker = function (base, options) {
222
227
  userId: mockSite_1.WEBOUT_SENTINELS.userId,
223
228
  apiServerUrl: mockSite_1.WEBOUT_SENTINELS.apiServerUrl
224
229
  })
225
- })];
230
+ })
231
+ // a sharded drive, when a test sets one up: the index answers the snapshot
232
+ // URL and each piece answers its own file, so the worker's fetch of a shard
233
+ // is the same request it makes against a static host
234
+ ];
235
+ shards = global.__sasjsShards;
236
+ if (shards && target.includes('vfs-snapshot')) {
237
+ piece = /vfs-snapshot-(\d+)\.json/.exec(target);
238
+ if (piece) {
239
+ id = Number(piece[1]);
240
+ requested = (global.__sasjsShardFetches =
241
+ global.__sasjsShardFetches || []);
242
+ requested.push(id);
243
+ return [2 /*return*/, jsonResponse({ format: mockSite_1.DRIVE_FORMAT, files: shards[id] || {} })];
244
+ }
245
+ return [2 /*return*/, jsonResponse(shards.index)];
246
+ }
247
+ if (target.includes('mock-config')) {
248
+ shards_1 = global.__sasjsShards;
249
+ return [2 /*return*/, jsonResponse(__assign({ appLoc: appLoc }, (shards_1
250
+ ? { snapshotShards: (shards_1.index.shards || []).length }
251
+ : {})))];
252
+ }
226
253
  return [2 /*return*/, target.includes('vfs-seed')
227
254
  ? jsonResponse({
228
255
  files: (_a = {},
@@ -266,6 +293,7 @@ var loadWorker = function (base, options) {
266
293
  dispatch: dispatch,
267
294
  message: message,
268
295
  lifecycle: lifecycle,
296
+ imported: imported,
269
297
  self: self,
270
298
  refuseWrites: function (value) {
271
299
  refusingWrites = value;
@@ -308,6 +336,8 @@ describe('service worker host', function () {
308
336
  delete global.importScripts;
309
337
  delete global.fetch;
310
338
  delete global.__sasjsIdentity;
339
+ delete global.__sasjsShards;
340
+ delete global.__sasjsShardFetches;
311
341
  delete global.__mockHostClients;
312
342
  });
313
343
  var seeded = function () {
@@ -1412,6 +1442,161 @@ describe('service worker host', function () {
1412
1442
  }
1413
1443
  });
1414
1444
  }); });
1445
+ /**
1446
+ * The linter is not part of the install.
1447
+ *
1448
+ * The page cannot make its first request until the worker has installed, so
1449
+ * anything the worker imports at boot is on the critical path of every
1450
+ * visit. The linter answers two routes; it is fetched when one of them is
1451
+ * called.
1452
+ */
1453
+ it('fetches the linter on the first code route, not at boot', function () { return __awaiter(void 0, void 0, void 0, function () {
1454
+ var worker, response;
1455
+ return __generator(this, function (_a) {
1456
+ switch (_a.label) {
1457
+ case 0:
1458
+ worker = seeded();
1459
+ expect(worker.imported.some(function (path) { return path.includes('lint-browser'); })).toBe(false);
1460
+ return [4 /*yield*/, worker.dispatch('/SASjsApi/code/lint', runRequest({ code: 'data a;\nrun;\n' }))];
1461
+ case 1:
1462
+ response = _a.sent();
1463
+ expect(response.status).toBe(200);
1464
+ expect(worker.imported.some(function (path) { return path.includes('lint-browser'); })).toBe(true);
1465
+ return [2 /*return*/];
1466
+ }
1467
+ });
1468
+ }); });
1469
+ /**
1470
+ * A route that resolves a path from the drive is not left with a hole.
1471
+ *
1472
+ * The rules an edited file is linted against are found by walking up from
1473
+ * the file, so the request names neither path. The read that fails says
1474
+ * which piece it needs, and the request is answered again with it - the body
1475
+ * re-read from the copy taken before the first attempt.
1476
+ */
1477
+ it('fetches the piece a route resolves from the drive, not the one it names', function () { return __awaiter(void 0, void 0, void 0, function () {
1478
+ var worker, longLine, response, _a, _b, _c;
1479
+ return __generator(this, function (_d) {
1480
+ switch (_d.label) {
1481
+ case 0:
1482
+ ;
1483
+ global.__sasjsShards = {
1484
+ index: {
1485
+ format: mockSite_1.DRIVE_FORMAT,
1486
+ sharded: true,
1487
+ boot: 0,
1488
+ entries: {
1489
+ '/sasjs/drive/files/.sasjslint': { shard: 1, bytes: 20 },
1490
+ '/sasjs/drive/files/Public/app/test/prog.sas': {
1491
+ shard: 0,
1492
+ bytes: 12
1493
+ }
1494
+ },
1495
+ shards: [
1496
+ { id: 0, file: 'host/vfs-snapshot-0.json' },
1497
+ { id: 1, file: 'host/vfs-snapshot-1.json' }
1498
+ ]
1499
+ },
1500
+ 0: { '/sasjs/drive/files/Public/app/test/prog.sas': 'data a; run;' },
1501
+ 1: {
1502
+ '/sasjs/drive/files/.sasjslint': JSON.stringify({
1503
+ maxLineLength: 200
1504
+ })
1505
+ }
1506
+ };
1507
+ global.__sasjsShardFetches = [];
1508
+ worker = loadWorker();
1509
+ return [4 /*yield*/, worker.runtime.load({})];
1510
+ case 1:
1511
+ _d.sent();
1512
+ longLine = 'x'.repeat(150);
1513
+ return [4 /*yield*/, worker.dispatch('/SASjsApi/code/lint', runRequest({
1514
+ code: "data _null_;\n".concat(longLine, "\nrun;\n"),
1515
+ filePath: '/Public/app/test/prog.sas'
1516
+ }))];
1517
+ case 2:
1518
+ response = _d.sent();
1519
+ expect(response.status).toBe(200);
1520
+ // the line is over the linter's default 80 and under this drive's 200, so
1521
+ // the answer proves the rules file was read - from a piece the request
1522
+ // never named
1523
+ _a = expect;
1524
+ _c = (_b = JSON).stringify;
1525
+ return [4 /*yield*/, response.json()];
1526
+ case 3:
1527
+ // the line is over the linter's default 80 and under this drive's 200, so
1528
+ // the answer proves the rules file was read - from a piece the request
1529
+ // never named
1530
+ _a.apply(void 0, [_c.apply(_b, [_d.sent()])]).not.toContain('Line exceeds');
1531
+ expect(global.__sasjsShardFetches).toEqual([0, 1]);
1532
+ return [2 /*return*/];
1533
+ }
1534
+ });
1535
+ }); });
1536
+ /**
1537
+ * A request that needs a second piece is answered, body and all.
1538
+ *
1539
+ * The first retry fetches the piece the failed read named; a request that
1540
+ * reads two pieces one after another reaches the second retry, which fetches
1541
+ * the rest. Either way the body has to arrive again - a Request's body can
1542
+ * only be read once, so the first attempt keeps an unread copy and each
1543
+ * attempt after it is made from a copy of that.
1544
+ */
1545
+ it('replays a request that needs two pieces, with its body intact', function () { return __awaiter(void 0, void 0, void 0, function () {
1546
+ var near, far, worker, longLine, response, findings, _a, _b;
1547
+ var _c, _d, _e;
1548
+ return __generator(this, function (_f) {
1549
+ switch (_f.label) {
1550
+ case 0:
1551
+ near = '/sasjs/drive/files/Public/app/test/.sasjslint';
1552
+ far = '/sasjs/drive/files/.sasjslint';
1553
+ global.__sasjsShards = {
1554
+ index: {
1555
+ format: mockSite_1.DRIVE_FORMAT,
1556
+ sharded: true,
1557
+ boot: 0,
1558
+ entries: (_c = {},
1559
+ _c[near] = { shard: 1, bytes: 20 },
1560
+ _c[far] = { shard: 2, bytes: 20 },
1561
+ _c),
1562
+ shards: [
1563
+ { id: 0, file: 'host/vfs-snapshot-0.json' },
1564
+ { id: 1, file: 'host/vfs-snapshot-1.json' },
1565
+ { id: 2, file: 'host/vfs-snapshot-2.json' }
1566
+ ]
1567
+ },
1568
+ 0: {},
1569
+ // the nearest rules file is unreadable as a config, so the walk goes on
1570
+ // to the drive root's - a second piece, in a second shard
1571
+ 1: (_d = {}, _d[near] = 'not json', _d),
1572
+ 2: (_e = {}, _e[far] = JSON.stringify({ maxLineLength: 200 }), _e)
1573
+ };
1574
+ global.__sasjsShardFetches = [];
1575
+ worker = loadWorker();
1576
+ return [4 /*yield*/, worker.runtime.load({})];
1577
+ case 1:
1578
+ _f.sent();
1579
+ longLine = 'x'.repeat(150);
1580
+ return [4 /*yield*/, worker.dispatch('/SASjsApi/code/lint', runRequest({
1581
+ code: "data _null_;\n".concat(longLine, "\nrun;\n"),
1582
+ filePath: '/Public/app/test/prog.sas'
1583
+ }))];
1584
+ case 2:
1585
+ response = _f.sent();
1586
+ expect(response.status).toBe(200);
1587
+ _b = (_a = JSON).stringify;
1588
+ return [4 /*yield*/, response.json()];
1589
+ case 3:
1590
+ findings = _b.apply(_a, [_f.sent()]);
1591
+ // the code arrived on the second attempt and the root rules applied: both
1592
+ // halves of the claim
1593
+ expect(findings).toContain('Doxygen');
1594
+ expect(findings).not.toContain('Line exceeds');
1595
+ expect(global.__sasjsShardFetches).toEqual([0, 1, 2]);
1596
+ return [2 /*return*/];
1597
+ }
1598
+ });
1599
+ }); });
1415
1600
  /**
1416
1601
  * The rules are the drive's, not the linter's defaults.
1417
1602
  *
@@ -2264,6 +2449,168 @@ describe('service worker host', function () {
2264
2449
  }
2265
2450
  });
2266
2451
  }); });
2452
+ /**
2453
+ * A sharded drive supplies the piece a request names, and only that one.
2454
+ *
2455
+ * The build splits a captured drive so the first page does not download every
2456
+ * app's bundle. That makes it the host's job to fetch the piece holding the
2457
+ * path a route reads - here a drive file, named in `_filePath` - and to leave
2458
+ * the pieces nobody asked for on the site.
2459
+ */
2460
+ it('fetches the piece a drive request names, and leaves the rest', function () { return __awaiter(void 0, void 0, void 0, function () {
2461
+ var small, large, worker, response, _a;
2462
+ var _b, _c, _d;
2463
+ return __generator(this, function (_e) {
2464
+ switch (_e.label) {
2465
+ case 0:
2466
+ small = '/sasjs/drive/files/config.json';
2467
+ large = '/sasjs/drive/files/data/large.csv';
2468
+ global.__sasjsShards = {
2469
+ index: {
2470
+ format: mockSite_1.DRIVE_FORMAT,
2471
+ sharded: true,
2472
+ boot: 0,
2473
+ entries: (_b = {},
2474
+ _b[small] = { shard: 0, bytes: 2 },
2475
+ _b[large] = { shard: 1, bytes: 64 },
2476
+ _b),
2477
+ shards: [
2478
+ { id: 0, file: 'host/vfs-snapshot-0.json' },
2479
+ { id: 1, file: 'host/vfs-snapshot-1.json' }
2480
+ ]
2481
+ },
2482
+ 0: (_c = {}, _c[small] = '{}', _c),
2483
+ 1: (_d = {}, _d[large] = 'a,b\n1,2\n3,4\n', _d)
2484
+ };
2485
+ global.__sasjsShardFetches = [];
2486
+ worker = loadWorker();
2487
+ return [4 /*yield*/, worker.runtime.load({})
2488
+ // the boot shard arrived with the drive; the second piece did not
2489
+ ];
2490
+ case 1:
2491
+ _e.sent();
2492
+ // the boot shard arrived with the drive; the second piece did not
2493
+ expect(global.__sasjsShardFetches).toEqual([0]);
2494
+ return [4 /*yield*/, worker.dispatch('/SASjsApi/drive/file?_filePath=' + encodeURIComponent('/data/large.csv'))];
2495
+ case 2:
2496
+ response = _e.sent();
2497
+ expect(response.status).toBe(200);
2498
+ _a = expect;
2499
+ return [4 /*yield*/, response.text()];
2500
+ case 3:
2501
+ _a.apply(void 0, [_e.sent()]).toContain('a,b');
2502
+ expect(global.__sasjsShardFetches).toEqual([0, 1]);
2503
+ // a second read of the same piece is answered from memory
2504
+ return [4 /*yield*/, worker.dispatch('/SASjsApi/drive/file?_filePath=' + encodeURIComponent('/data/large.csv'))];
2505
+ case 4:
2506
+ // a second read of the same piece is answered from memory
2507
+ _e.sent();
2508
+ expect(global.__sasjsShardFetches).toEqual([0, 1]);
2509
+ return [2 /*return*/];
2510
+ }
2511
+ });
2512
+ }); });
2513
+ /**
2514
+ * An export is the whole drive, not the part in memory.
2515
+ *
2516
+ * `window.SASjsMock.download()` writes the file a project ships as
2517
+ * `mock-snapshot.json`, so a sharded drive must be fetched before it is
2518
+ * dumped - otherwise the next build ships a snapshot that has quietly lost
2519
+ * every app the visitor never opened.
2520
+ */
2521
+ it('exports every piece of a sharded drive, not only the loaded ones', function () { return __awaiter(void 0, void 0, void 0, function () {
2522
+ var small, large, worker, _a, replies, pending, exported;
2523
+ var _b, _c, _d;
2524
+ return __generator(this, function (_e) {
2525
+ switch (_e.label) {
2526
+ case 0:
2527
+ small = '/sasjs/drive/files/config.json';
2528
+ large = '/sasjs/drive/files/data/large.csv';
2529
+ global.__sasjsShards = {
2530
+ index: {
2531
+ format: mockSite_1.DRIVE_FORMAT,
2532
+ sharded: true,
2533
+ boot: 0,
2534
+ entries: (_b = {},
2535
+ _b[small] = { shard: 0, bytes: 2 },
2536
+ _b[large] = { shard: 1, bytes: 8 },
2537
+ _b),
2538
+ shards: [
2539
+ { id: 0, file: 'host/vfs-snapshot-0.json' },
2540
+ { id: 1, file: 'host/vfs-snapshot-1.json' }
2541
+ ]
2542
+ },
2543
+ 0: (_c = {}, _c[small] = '{}', _c),
2544
+ 1: (_d = {}, _d[large] = 'a,b\n1,2\n', _d)
2545
+ };
2546
+ global.__sasjsShardFetches = [];
2547
+ worker = loadWorker();
2548
+ return [4 /*yield*/, worker.runtime.load({})];
2549
+ case 1:
2550
+ _e.sent();
2551
+ _a = worker.message({ type: 'sasjs-snapshot' }), replies = _a.replies, pending = _a.pending;
2552
+ return [4 /*yield*/, pending
2553
+ // the piece nobody read was fetched for the export
2554
+ ];
2555
+ case 2:
2556
+ _e.sent();
2557
+ // the piece nobody read was fetched for the export
2558
+ expect(global.__sasjsShardFetches).toEqual([0, 1]);
2559
+ exported = replies.find(function (reply) { return reply.type === 'sasjs-snapshot'; });
2560
+ expect(Object.keys(exported.files).sort()).toEqual([large, small].sort());
2561
+ return [2 /*return*/];
2562
+ }
2563
+ });
2564
+ }); });
2565
+ /**
2566
+ * The console's own files are the page, not an API call.
2567
+ *
2568
+ * `/api/...` is the API's short spelling, so the worker answers it - except for
2569
+ * the four files the console is made of, which the static host serves. Getting
2570
+ * that wrong answers the page's own script with an API 404, and the console
2571
+ * renders nothing.
2572
+ */
2573
+ it('leaves the API console to the static host, and still answers /api calls', function () { return __awaiter(void 0, void 0, void 0, function () {
2574
+ var worker, source, listed, files, files_1, files_1_1, name_1, log, response;
2575
+ var e_2, _a;
2576
+ return __generator(this, function (_b) {
2577
+ switch (_b.label) {
2578
+ case 0:
2579
+ worker = seeded();
2580
+ source = fs_1.default.readFileSync(path_1.default.join(__dirname, '..', 'assets', 'sw.js'), 'utf8');
2581
+ listed = /const API_CONSOLE_FILES = \[([^\]]*)\]/.exec(source);
2582
+ expect(listed).not.toBeNull();
2583
+ files = listed[1]
2584
+ .split(',')
2585
+ .map(function (entry) { return entry.trim().replace(/^'|'$/g, ''); })
2586
+ .filter(Boolean);
2587
+ expect(files.length).toBeGreaterThan(3);
2588
+ expect(worker.dispatch('/api/')).toBeUndefined();
2589
+ try {
2590
+ for (files_1 = __values(files), files_1_1 = files_1.next(); !files_1_1.done; files_1_1 = files_1.next()) {
2591
+ name_1 = files_1_1.value;
2592
+ expect(worker.dispatch("/api/".concat(name_1))).toBeUndefined();
2593
+ }
2594
+ }
2595
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
2596
+ finally {
2597
+ try {
2598
+ if (files_1_1 && !files_1_1.done && (_a = files_1.return)) _a.call(files_1);
2599
+ }
2600
+ finally { if (e_2) throw e_2.error; }
2601
+ }
2602
+ return [4 /*yield*/, worker.dispatch('/api/admin/logs/missing.log')];
2603
+ case 1:
2604
+ log = _b.sent();
2605
+ expect(log.status).not.toBeUndefined();
2606
+ return [4 /*yield*/, worker.dispatch('/api/user')];
2607
+ case 2:
2608
+ response = _b.sent();
2609
+ expect(response.status).toBe(200);
2610
+ return [2 /*return*/];
2611
+ }
2612
+ });
2613
+ }); });
2267
2614
  it('answers 404 for a drive file the drive does not hold', function () { return __awaiter(void 0, void 0, void 0, function () {
2268
2615
  var dispatch, response, _a;
2269
2616
  return __generator(this, function (_b) {
@@ -2283,8 +2630,8 @@ describe('service worker host', function () {
2283
2630
  });
2284
2631
  }); });
2285
2632
  it('publishes, lists, serves and removes an app through the package surface', function () { return __awaiter(void 0, void 0, void 0, function () {
2286
- var dispatch, _a, _b, path_2, listing, _c, e_2_1, _d, _e, path_3, landing, e_3_1, root, archive, form, published, app, _f, _g, path_4, listing, _h, e_4_1, _j, _k, path_5, page, _l, e_5_1, folder, _m, refused, missing, removed, _o, after, _p, gone;
2287
- var e_2, _q, e_3, _r, e_4, _s, e_5, _t;
2633
+ var dispatch, _a, _b, path_2, listing, _c, e_3_1, _d, _e, path_3, landing, e_4_1, root, archive, form, published, app, _f, _g, path_4, listing, _h, e_5_1, _j, _k, path_5, page, _l, e_6_1, folder, _m, refused, missing, removed, _o, after, _p, gone;
2634
+ var e_3, _q, e_4, _r, e_5, _s, e_6, _t;
2288
2635
  return __generator(this, function (_u) {
2289
2636
  switch (_u.label) {
2290
2637
  case 0:
@@ -2311,14 +2658,14 @@ describe('service worker host', function () {
2311
2658
  return [3 /*break*/, 2];
2312
2659
  case 6: return [3 /*break*/, 9];
2313
2660
  case 7:
2314
- e_2_1 = _u.sent();
2315
- e_2 = { error: e_2_1 };
2661
+ e_3_1 = _u.sent();
2662
+ e_3 = { error: e_3_1 };
2316
2663
  return [3 /*break*/, 9];
2317
2664
  case 8:
2318
2665
  try {
2319
2666
  if (_b && !_b.done && (_q = _a.return)) _q.call(_a);
2320
2667
  }
2321
- finally { if (e_2) throw e_2.error; }
2668
+ finally { if (e_3) throw e_3.error; }
2322
2669
  return [7 /*endfinally*/];
2323
2670
  case 9:
2324
2671
  _u.trys.push([9, 14, 15, 16]);
@@ -2338,14 +2685,14 @@ describe('service worker host', function () {
2338
2685
  return [3 /*break*/, 10];
2339
2686
  case 13: return [3 /*break*/, 16];
2340
2687
  case 14:
2341
- e_3_1 = _u.sent();
2342
- e_3 = { error: e_3_1 };
2688
+ e_4_1 = _u.sent();
2689
+ e_4 = { error: e_4_1 };
2343
2690
  return [3 /*break*/, 16];
2344
2691
  case 15:
2345
2692
  try {
2346
2693
  if (_e && !_e.done && (_r = _d.return)) _r.call(_d);
2347
2694
  }
2348
- finally { if (e_3) throw e_3.error; }
2695
+ finally { if (e_4) throw e_4.error; }
2349
2696
  return [7 /*endfinally*/];
2350
2697
  case 16: return [4 /*yield*/, dispatch('/AppStream')];
2351
2698
  case 17:
@@ -2398,14 +2745,14 @@ describe('service worker host', function () {
2398
2745
  return [3 /*break*/, 22];
2399
2746
  case 26: return [3 /*break*/, 29];
2400
2747
  case 27:
2401
- e_4_1 = _u.sent();
2402
- e_4 = { error: e_4_1 };
2748
+ e_5_1 = _u.sent();
2749
+ e_5 = { error: e_5_1 };
2403
2750
  return [3 /*break*/, 29];
2404
2751
  case 28:
2405
2752
  try {
2406
2753
  if (_g && !_g.done && (_s = _f.return)) _s.call(_f);
2407
2754
  }
2408
- finally { if (e_4) throw e_4.error; }
2755
+ finally { if (e_5) throw e_5.error; }
2409
2756
  return [7 /*endfinally*/];
2410
2757
  case 29:
2411
2758
  _u.trys.push([29, 35, 36, 37]);
@@ -2432,14 +2779,14 @@ describe('service worker host', function () {
2432
2779
  return [3 /*break*/, 30];
2433
2780
  case 34: return [3 /*break*/, 37];
2434
2781
  case 35:
2435
- e_5_1 = _u.sent();
2436
- e_5 = { error: e_5_1 };
2782
+ e_6_1 = _u.sent();
2783
+ e_6 = { error: e_6_1 };
2437
2784
  return [3 /*break*/, 37];
2438
2785
  case 36:
2439
2786
  try {
2440
2787
  if (_k && !_k.done && (_t = _j.return)) _t.call(_j);
2441
2788
  }
2442
- finally { if (e_5) throw e_5.error; }
2789
+ finally { if (e_6) throw e_6.error; }
2443
2790
  return [7 /*endfinally*/];
2444
2791
  case 37: return [4 /*yield*/, dispatch("/app/".concat(app.id))];
2445
2792
  case 38:
@@ -2835,16 +3182,23 @@ describe('service worker host', function () {
2835
3182
  expect(seed.replies[0]).toEqual(expect.objectContaining({ type: 'sasjs-seed-ack' }));
2836
3183
  runtime.load(files);
2837
3184
  snapshot = message({ type: 'sasjs-snapshot' });
3185
+ // the reply waits for the drive to be whole, so a sharded site's export is
3186
+ // not the part of it that happens to be in memory
3187
+ return [4 /*yield*/, snapshot.pending];
3188
+ case 3:
3189
+ // the reply waits for the drive to be whole, so a sharded site's export is
3190
+ // not the part of it that happens to be in memory
3191
+ _c.sent();
2838
3192
  expect(snapshot.replies[0].type).toBe('sasjs-snapshot');
2839
3193
  expect(Object.keys(snapshot.replies[0].files).length).toBe(1);
2840
3194
  reset = message({ type: 'sasjs-reset' });
2841
3195
  return [4 /*yield*/, reset.pending];
2842
- case 3:
3196
+ case 4:
2843
3197
  _c.sent();
2844
3198
  expect(reset.replies[0]).toEqual(expect.objectContaining({ type: 'sasjs-reset-ack' }));
2845
3199
  stats = message({ type: 'sasjs-stats' });
2846
3200
  return [4 /*yield*/, stats.pending];
2847
- case 4:
3201
+ case 5:
2848
3202
  _c.sent();
2849
3203
  expect(stats.replies[0]).toEqual(expect.objectContaining({ type: 'sasjs-stats' }));
2850
3204
  return [2 /*return*/];
@@ -3381,7 +3735,7 @@ describe('service worker host', function () {
3381
3735
  }); });
3382
3736
  });
3383
3737
  describe('conformance corpus', function () {
3384
- var e_6, _a, e_7, _b;
3738
+ var e_7, _a, e_8, _b;
3385
3739
  /**
3386
3740
  * The corpus is what "the same API" means: the cases @sasjs/api runs against
3387
3741
  * its own services, and against a live sasjs/server, run here against this
@@ -3390,7 +3744,7 @@ describe('service worker host', function () {
3390
3744
  * used to have - and it is a test rather than a review.
3391
3745
  */
3392
3746
  var httpFor = function (testCase) {
3393
- var e_8, _a;
3747
+ var e_9, _a;
3394
3748
  var _b;
3395
3749
  var _c = __read(testCase.route.split(' '), 2), method = _c[0], routePath = _c[1];
3396
3750
  var query = new URLSearchParams();
@@ -3401,12 +3755,12 @@ describe('service worker host', function () {
3401
3755
  query.set(key, String(value));
3402
3756
  }
3403
3757
  }
3404
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
3758
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
3405
3759
  finally {
3406
3760
  try {
3407
3761
  if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
3408
3762
  }
3409
- finally { if (e_8) throw e_8.error; }
3763
+ finally { if (e_9) throw e_9.error; }
3410
3764
  }
3411
3765
  var suffix = query.toString() ? "?".concat(query) : '';
3412
3766
  var init = { method: method };
@@ -3437,8 +3791,8 @@ describe('service worker host', function () {
3437
3791
  * does.
3438
3792
  */
3439
3793
  var seedDrive = function (runtime, testCase) { return __awaiter(void 0, void 0, void 0, function () {
3440
- var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents, e_9_1;
3441
- var e_10, _g, e_9, _h;
3794
+ var text, _a, _b, _c, file, contents, _d, _e, _f, file, contents, e_10_1;
3795
+ var e_11, _g, e_10, _h;
3442
3796
  return __generator(this, function (_j) {
3443
3797
  switch (_j.label) {
3444
3798
  case 0:
@@ -3450,12 +3804,12 @@ describe('service worker host', function () {
3450
3804
  text["/sasjs/drive/files".concat(file)] = contents;
3451
3805
  }
3452
3806
  }
3453
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
3807
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
3454
3808
  finally {
3455
3809
  try {
3456
3810
  if (_b && !_b.done && (_g = _a.return)) _g.call(_a);
3457
3811
  }
3458
- finally { if (e_10) throw e_10.error; }
3812
+ finally { if (e_11) throw e_11.error; }
3459
3813
  }
3460
3814
  return [4 /*yield*/, runtime.load(text)];
3461
3815
  case 1:
@@ -3478,14 +3832,14 @@ describe('service worker host', function () {
3478
3832
  return [3 /*break*/, 3];
3479
3833
  case 6: return [3 /*break*/, 9];
3480
3834
  case 7:
3481
- e_9_1 = _j.sent();
3482
- e_9 = { error: e_9_1 };
3835
+ e_10_1 = _j.sent();
3836
+ e_10 = { error: e_10_1 };
3483
3837
  return [3 /*break*/, 9];
3484
3838
  case 8:
3485
3839
  try {
3486
3840
  if (_e && !_e.done && (_h = _d.return)) _h.call(_d);
3487
3841
  }
3488
- finally { if (e_9) throw e_9.error; }
3842
+ finally { if (e_10) throw e_10.error; }
3489
3843
  return [7 /*endfinally*/];
3490
3844
  case 9: return [2 /*return*/];
3491
3845
  }
@@ -3596,12 +3950,12 @@ describe('service worker host', function () {
3596
3950
  _loop_1(testCase);
3597
3951
  }
3598
3952
  }
3599
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
3953
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
3600
3954
  finally {
3601
3955
  try {
3602
3956
  if (DRIVE_CORPUS_1_1 && !DRIVE_CORPUS_1_1.done && (_a = DRIVE_CORPUS_1.return)) _a.call(DRIVE_CORPUS_1);
3603
3957
  }
3604
- finally { if (e_6) throw e_6.error; }
3958
+ finally { if (e_7) throw e_7.error; }
3605
3959
  }
3606
3960
  /** The app a removal case names, as the corpus states it. */
3607
3961
  var CORPUS_APP = {
@@ -3670,7 +4024,7 @@ describe('service worker host', function () {
3670
4024
  return __generator(this, function (_a) {
3671
4025
  files = {};
3672
4026
  walk = function (members, prefix) {
3673
- var e_11, _a;
4027
+ var e_12, _a;
3674
4028
  var _b;
3675
4029
  try {
3676
4030
  for (var _c = __values(members || []), _d = _c.next(); !_d.done; _d = _c.next()) {
@@ -3685,12 +4039,12 @@ describe('service worker host', function () {
3685
4039
  : String((_b = member.code) !== null && _b !== void 0 ? _b : '');
3686
4040
  }
3687
4041
  }
3688
- catch (e_11_1) { e_11 = { error: e_11_1 }; }
4042
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
3689
4043
  finally {
3690
4044
  try {
3691
4045
  if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
3692
4046
  }
3693
- finally { if (e_11) throw e_11.error; }
4047
+ finally { if (e_12) throw e_12.error; }
3694
4048
  }
3695
4049
  };
3696
4050
  walk(tree === null || tree === void 0 ? void 0 : tree.members, '');
@@ -3707,7 +4061,7 @@ describe('service worker host', function () {
3707
4061
  */
3708
4062
  var deployRequest = function (testCase) { return __awaiter(void 0, void 0, void 0, function () {
3709
4063
  var _a, requestPath, init, body, archive, form, _b, _c, _d, field, value;
3710
- var e_12, _e;
4064
+ var e_13, _e;
3711
4065
  return __generator(this, function (_f) {
3712
4066
  switch (_f.label) {
3713
4067
  case 0:
@@ -3726,12 +4080,12 @@ describe('service worker host', function () {
3726
4080
  form.set(field, String(value));
3727
4081
  }
3728
4082
  }
3729
- catch (e_12_1) { e_12 = { error: e_12_1 }; }
4083
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
3730
4084
  finally {
3731
4085
  try {
3732
4086
  if (_c && !_c.done && (_e = _b.return)) _e.call(_b);
3733
4087
  }
3734
- finally { if (e_12) throw e_12.error; }
4088
+ finally { if (e_13) throw e_13.error; }
3735
4089
  }
3736
4090
  form.set('file', new Blob([new Uint8Array(archive)]), 'deploy.zip');
3737
4091
  return [2 /*return*/, { requestPath: requestPath, init: { method: 'POST', body: form } }];
@@ -3740,8 +4094,8 @@ describe('service worker host', function () {
3740
4094
  }); };
3741
4095
  var _loop_2 = function (testCase) {
3742
4096
  it(testCase.id, function () { return __awaiter(void 0, void 0, void 0, function () {
3743
- var worker, _a, requestPath, init, response, listing, ids, _b, _c, path_6, file, e_13_1, _d, _e, path_7, file, e_14_1;
3744
- var e_13, _f, e_14, _g;
4097
+ var worker, _a, requestPath, init, response, listing, ids, _b, _c, path_6, file, e_14_1, _d, _e, path_7, file, e_15_1;
4098
+ var e_14, _f, e_15, _g;
3745
4099
  var _h, _j, _k;
3746
4100
  return __generator(this, function (_l) {
3747
4101
  switch (_l.label) {
@@ -3793,14 +4147,14 @@ describe('service worker host', function () {
3793
4147
  return [3 /*break*/, 9];
3794
4148
  case 12: return [3 /*break*/, 15];
3795
4149
  case 13:
3796
- e_13_1 = _l.sent();
3797
- e_13 = { error: e_13_1 };
4150
+ e_14_1 = _l.sent();
4151
+ e_14 = { error: e_14_1 };
3798
4152
  return [3 /*break*/, 15];
3799
4153
  case 14:
3800
4154
  try {
3801
4155
  if (_c && !_c.done && (_f = _b.return)) _f.call(_b);
3802
4156
  }
3803
- finally { if (e_13) throw e_13.error; }
4157
+ finally { if (e_14) throw e_14.error; }
3804
4158
  return [7 /*endfinally*/];
3805
4159
  case 15:
3806
4160
  _l.trys.push([15, 20, 21, 22]);
@@ -3819,14 +4173,14 @@ describe('service worker host', function () {
3819
4173
  return [3 /*break*/, 16];
3820
4174
  case 19: return [3 /*break*/, 22];
3821
4175
  case 20:
3822
- e_14_1 = _l.sent();
3823
- e_14 = { error: e_14_1 };
4176
+ e_15_1 = _l.sent();
4177
+ e_15 = { error: e_15_1 };
3824
4178
  return [3 /*break*/, 22];
3825
4179
  case 21:
3826
4180
  try {
3827
4181
  if (_e && !_e.done && (_g = _d.return)) _g.call(_d);
3828
4182
  }
3829
- finally { if (e_14) throw e_14.error; }
4183
+ finally { if (e_15) throw e_15.error; }
3830
4184
  return [7 /*endfinally*/];
3831
4185
  case 22: return [2 /*return*/];
3832
4186
  }
@@ -3859,12 +4213,12 @@ describe('service worker host', function () {
3859
4213
  _loop_2(testCase);
3860
4214
  }
3861
4215
  }
3862
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
4216
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
3863
4217
  finally {
3864
4218
  try {
3865
4219
  if (_d && !_d.done && (_b = _c.return)) _b.call(_c);
3866
4220
  }
3867
- finally { if (e_7) throw e_7.error; }
4221
+ finally { if (e_8) throw e_8.error; }
3868
4222
  }
3869
4223
  });
3870
4224
  /**
@@ -3876,7 +4230,7 @@ describe('service worker host', function () {
3876
4230
  * are the package's.
3877
4231
  */
3878
4232
  describe('identity and record conformance', function () {
3879
- var e_15, _a;
4233
+ var e_16, _a;
3880
4234
  /**
3881
4235
  * Boots the worker from a fixture, the way the page does: the case's own
3882
4236
  * identity, and `subject: null` for no implicit caller, so a case that
@@ -3909,7 +4263,7 @@ describe('service worker host', function () {
3909
4263
  };
3910
4264
  /** The request a case describes, as HTTP: path parameters filled, token sent. */
3911
4265
  var httpForCase = function (testCase) {
3912
- var e_16, _a;
4266
+ var e_17, _a;
3913
4267
  var _b = __read(testCase.route.split(' '), 2), method = _b[0], routePath = _b[1];
3914
4268
  var filled = routePath.replace(/\{(\w+)\}/g, function (_match, name) {
3915
4269
  return encodeURIComponent(String((testCase.request.params || {})[name]));
@@ -3922,12 +4276,12 @@ describe('service worker host', function () {
3922
4276
  query.set(key, String(value));
3923
4277
  }
3924
4278
  }
3925
- catch (e_16_1) { e_16 = { error: e_16_1 }; }
4279
+ catch (e_17_1) { e_17 = { error: e_17_1 }; }
3926
4280
  finally {
3927
4281
  try {
3928
4282
  if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
3929
4283
  }
3930
- finally { if (e_16) throw e_16.error; }
4284
+ finally { if (e_17) throw e_17.error; }
3931
4285
  }
3932
4286
  var suffix = query.toString() ? "?".concat(query) : '';
3933
4287
  var headers = {};
@@ -4049,12 +4403,12 @@ describe('service worker host', function () {
4049
4403
  _loop_3(testCase);
4050
4404
  }
4051
4405
  }
4052
- catch (e_15_1) { e_15 = { error: e_15_1 }; }
4406
+ catch (e_16_1) { e_16 = { error: e_16_1 }; }
4053
4407
  finally {
4054
4408
  try {
4055
4409
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
4056
4410
  }
4057
- finally { if (e_15) throw e_15.error; }
4411
+ finally { if (e_16) throw e_16.error; }
4058
4412
  }
4059
4413
  it('answers as the fixture administrator when no subject was chosen', function () { return __awaiter(void 0, void 0, void 0, function () {
4060
4414
  var worker, response, rules;