@sasjs/cli 6.24.2 → 6.26.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/build/commands/build/build.js +6 -0
- package/build/commands/build/internal/appManifest.d.ts +42 -0
- package/build/commands/build/internal/appManifest.js +85 -0
- package/build/commands/build/internal/mockSite/assets/persist.js +165 -26
- package/build/commands/build/internal/mockSite/assets/sw.js +419 -63
- package/build/commands/build/internal/mockSite/mockSite.d.ts +5 -0
- package/build/commands/build/internal/mockSite/mockSite.js +49 -6
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +150 -0
- package/build/commands/build/internal/mockSite/spec/persist.spec.js +335 -15
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +522 -18
- package/build/commands/build/internal/spec/appManifest.spec.d.ts +1 -0
- package/build/commands/build/internal/spec/appManifest.spec.js +71 -0
- package/build/commands/build/spec/build.spec.js +52 -0
- package/build/commands/deploy/internal/appStreamUrl.d.ts +25 -0
- package/build/commands/deploy/internal/appStreamUrl.js +27 -0
- package/build/commands/deploy/internal/deployToSASJSWithServicePack.js +17 -16
- package/build/commands/deploy/internal/executeDeployScriptSasjs.js +14 -13
- package/build/commands/deploy/internal/spec/appStreamUrl.spec.d.ts +1 -0
- package/build/commands/deploy/internal/spec/appStreamUrl.spec.js +42 -0
- package/build/commands/deploy/internal/spec/executeDeployScriptSasjs.spec.js +1 -1
- package/package.json +5 -5
|
@@ -99,6 +99,7 @@ var file_1 = require("../../utils/file");
|
|
|
99
99
|
var internal_1 = require("../compile/internal");
|
|
100
100
|
var config_2 = require("./internal/config");
|
|
101
101
|
var getLaunchPageCode_1 = require("./internal/getLaunchPageCode");
|
|
102
|
+
var appManifest_1 = require("./internal/appManifest");
|
|
102
103
|
var mockSite_1 = require("./internal/mockSite/mockSite");
|
|
103
104
|
function build(target) {
|
|
104
105
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -241,6 +242,11 @@ function createFinalSasFiles(target) {
|
|
|
241
242
|
servicePackSASjs.streamLogo = streamConfig.streamLogo;
|
|
242
243
|
servicePackSASjs.streamServiceName = streamConfig.streamServiceName;
|
|
243
244
|
servicePackSASjs.streamWebFolder = streamConfig.streamWebFolder;
|
|
245
|
+
// The deployment and the statement that it is an app travel together: the
|
|
246
|
+
// server indexes the app from this file in the same request that writes the
|
|
247
|
+
// files, so a publish cannot fail on its own after a deploy succeeded. It
|
|
248
|
+
// also means the zip a CI job keeps is everything needed to publish.
|
|
249
|
+
servicePackSASjs.fileTree = (0, appManifest_1.withAppManifest)(folderContentJSON, (0, appManifest_1.appManifest)(streamConfig));
|
|
244
250
|
}
|
|
245
251
|
}
|
|
246
252
|
finalSasFileContent = (0, utils_1.removeHeader)(finalSasFileContent);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { FileTree, StreamConfig } from '@sasjs/utils';
|
|
2
|
+
/**
|
|
3
|
+
* The file, at the root of a deployment, that says the folder is an app.
|
|
4
|
+
*
|
|
5
|
+
* It carries what a deploy payload names for a stream today - the label, the
|
|
6
|
+
* folder the compiled assets are in, the logo - inside the deployment itself, so
|
|
7
|
+
* one upload can be both the deployment and the publish.
|
|
8
|
+
*
|
|
9
|
+
* **This ships ahead of the reader.** The manifest is read by `splitAppBundle` in
|
|
10
|
+
* sasjs/api (PR #65), which takes it out of the tree before the tree is written
|
|
11
|
+
* and indexes the app in the same request; no released `@sasjs/api`, `@sasjs/adapter`
|
|
12
|
+
* or `sasjs/server` knows the file yet, so until a host adopts that package the
|
|
13
|
+
* manifest is deployed as an ordinary file and the publish still comes from the
|
|
14
|
+
* payload's `streamWebFolder` / `streamServiceName`, exactly as now. The name has
|
|
15
|
+
* to match `APP_MANIFEST` in that package - `sasjs.app.json` is spelled here
|
|
16
|
+
* rather than imported because the deployment does not ship the package.
|
|
17
|
+
*/
|
|
18
|
+
export declare const APP_MANIFEST = "sasjs.app.json";
|
|
19
|
+
export interface AppManifest {
|
|
20
|
+
/** The label the app is listed under. */
|
|
21
|
+
name?: string;
|
|
22
|
+
/** The folder under `services/` holding the compiled web app. */
|
|
23
|
+
streamWebFolder: string;
|
|
24
|
+
/** An asset inside the app, used as its tile image. */
|
|
25
|
+
logo?: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* What a target's stream configuration says about the app it deploys.
|
|
29
|
+
*
|
|
30
|
+
* Only fields the deployer actually set are written: a manifest that states
|
|
31
|
+
* `"logo": undefined` is not JSON, and one that states a default the server also
|
|
32
|
+
* defaults would freeze that default into every deployment.
|
|
33
|
+
*/
|
|
34
|
+
export declare const appManifest: (streamConfig: StreamConfig) => AppManifest;
|
|
35
|
+
/**
|
|
36
|
+
* The deployment tree with the manifest at its root.
|
|
37
|
+
*
|
|
38
|
+
* A tree that already carries a manifest - a build output adjusted by hand, or a
|
|
39
|
+
* bundle built twice - has it replaced rather than duplicated, because two files
|
|
40
|
+
* with one name in one folder is not a tree any host can deploy.
|
|
41
|
+
*/
|
|
42
|
+
export declare const withAppManifest: (tree: FileTree, manifest: AppManifest) => FileTree;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __read = (this && this.__read) || function (o, n) {
|
|
14
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
15
|
+
if (!m) return o;
|
|
16
|
+
var i = m.call(o), r, ar = [], e;
|
|
17
|
+
try {
|
|
18
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
19
|
+
}
|
|
20
|
+
catch (error) { e = { error: error }; }
|
|
21
|
+
finally {
|
|
22
|
+
try {
|
|
23
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
24
|
+
}
|
|
25
|
+
finally { if (e) throw e.error; }
|
|
26
|
+
}
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
30
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
31
|
+
if (ar || !(i in from)) {
|
|
32
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
33
|
+
ar[i] = from[i];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.withAppManifest = exports.appManifest = exports.APP_MANIFEST = void 0;
|
|
40
|
+
var utils_1 = require("@sasjs/utils");
|
|
41
|
+
/**
|
|
42
|
+
* The file, at the root of a deployment, that says the folder is an app.
|
|
43
|
+
*
|
|
44
|
+
* It carries what a deploy payload names for a stream today - the label, the
|
|
45
|
+
* folder the compiled assets are in, the logo - inside the deployment itself, so
|
|
46
|
+
* one upload can be both the deployment and the publish.
|
|
47
|
+
*
|
|
48
|
+
* **This ships ahead of the reader.** The manifest is read by `splitAppBundle` in
|
|
49
|
+
* sasjs/api (PR #65), which takes it out of the tree before the tree is written
|
|
50
|
+
* and indexes the app in the same request; no released `@sasjs/api`, `@sasjs/adapter`
|
|
51
|
+
* or `sasjs/server` knows the file yet, so until a host adopts that package the
|
|
52
|
+
* manifest is deployed as an ordinary file and the publish still comes from the
|
|
53
|
+
* payload's `streamWebFolder` / `streamServiceName`, exactly as now. The name has
|
|
54
|
+
* to match `APP_MANIFEST` in that package - `sasjs.app.json` is spelled here
|
|
55
|
+
* rather than imported because the deployment does not ship the package.
|
|
56
|
+
*/
|
|
57
|
+
exports.APP_MANIFEST = 'sasjs.app.json';
|
|
58
|
+
/**
|
|
59
|
+
* What a target's stream configuration says about the app it deploys.
|
|
60
|
+
*
|
|
61
|
+
* Only fields the deployer actually set are written: a manifest that states
|
|
62
|
+
* `"logo": undefined` is not JSON, and one that states a default the server also
|
|
63
|
+
* defaults would freeze that default into every deployment.
|
|
64
|
+
*/
|
|
65
|
+
var appManifest = function (streamConfig) { return (__assign(__assign(__assign({}, (streamConfig.streamServiceName
|
|
66
|
+
? { name: streamConfig.streamServiceName }
|
|
67
|
+
: {})), { streamWebFolder: streamConfig.streamWebFolder }), (streamConfig.streamLogo ? { logo: streamConfig.streamLogo } : {}))); };
|
|
68
|
+
exports.appManifest = appManifest;
|
|
69
|
+
/**
|
|
70
|
+
* The deployment tree with the manifest at its root.
|
|
71
|
+
*
|
|
72
|
+
* A tree that already carries a manifest - a build output adjusted by hand, or a
|
|
73
|
+
* bundle built twice - has it replaced rather than duplicated, because two files
|
|
74
|
+
* with one name in one folder is not a tree any host can deploy.
|
|
75
|
+
*/
|
|
76
|
+
var withAppManifest = function (tree, manifest) { return (__assign(__assign({}, tree), { members: __spreadArray(__spreadArray([], __read(tree.members.filter(function (member) {
|
|
77
|
+
return !(member.type === utils_1.MemberType.file && member.name === exports.APP_MANIFEST);
|
|
78
|
+
})), false), [
|
|
79
|
+
{
|
|
80
|
+
name: exports.APP_MANIFEST,
|
|
81
|
+
type: utils_1.MemberType.file,
|
|
82
|
+
code: JSON.stringify(manifest, null, 2)
|
|
83
|
+
}
|
|
84
|
+
], false) })); };
|
|
85
|
+
exports.withAppManifest = withAppManifest;
|
|
@@ -3,29 +3,40 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The service worker hosts the runtime but may be terminated between requests,
|
|
5
5
|
* so it holds no durable state. On boot this script reads the persisted drive
|
|
6
|
-
* and hands
|
|
7
|
-
* files it changed and
|
|
6
|
+
* and identity and hands them to the worker; after every service run the worker
|
|
7
|
+
* sends back the files it changed and the identity store's snapshot when a route
|
|
8
|
+
* changed it, and both are written here.
|
|
8
9
|
*/
|
|
9
10
|
;(function () {
|
|
10
11
|
const DB_NAME = 'sasjs-mock-host'
|
|
11
|
-
const
|
|
12
|
+
const FILES = 'files'
|
|
13
|
+
/**
|
|
14
|
+
* The identity the visitor last had: the fixture the worker's store reported
|
|
15
|
+
* and the account the visitor chose to view the app as. One row, because the
|
|
16
|
+
* two are written together - a subject without the fixture it names would be a
|
|
17
|
+
* boot signed in as an account the store no longer holds.
|
|
18
|
+
*/
|
|
19
|
+
const IDENTITY = 'identity'
|
|
20
|
+
const IDENTITY_KEY = 'current'
|
|
12
21
|
|
|
13
22
|
const openDb = () =>
|
|
14
23
|
new Promise((resolve, reject) => {
|
|
15
|
-
const req = indexedDB.open(DB_NAME,
|
|
24
|
+
const req = indexedDB.open(DB_NAME, 2)
|
|
16
25
|
req.onupgradeneeded = () => {
|
|
17
26
|
const db = req.result
|
|
18
|
-
if (!db.objectStoreNames.contains(
|
|
27
|
+
if (!db.objectStoreNames.contains(FILES)) db.createObjectStore(FILES)
|
|
28
|
+
if (!db.objectStoreNames.contains(IDENTITY))
|
|
29
|
+
db.createObjectStore(IDENTITY)
|
|
19
30
|
}
|
|
20
31
|
req.onsuccess = () => resolve(req.result)
|
|
21
32
|
req.onerror = () => reject(req.error)
|
|
22
33
|
})
|
|
23
34
|
|
|
24
|
-
const withStore = async (mode, run) => {
|
|
35
|
+
const withStore = async (mode, run, name = FILES) => {
|
|
25
36
|
const db = await openDb()
|
|
26
37
|
return new Promise((resolve, reject) => {
|
|
27
|
-
const tx = db.transaction(
|
|
28
|
-
const store = tx.objectStore(
|
|
38
|
+
const tx = db.transaction(name, mode)
|
|
39
|
+
const store = tx.objectStore(name)
|
|
29
40
|
let result
|
|
30
41
|
try {
|
|
31
42
|
result = run(store)
|
|
@@ -44,15 +55,15 @@
|
|
|
44
55
|
try {
|
|
45
56
|
const db = await openDb()
|
|
46
57
|
const keysReq = db
|
|
47
|
-
.transaction(
|
|
48
|
-
.objectStore(
|
|
58
|
+
.transaction(FILES, 'readonly')
|
|
59
|
+
.objectStore(FILES)
|
|
49
60
|
.getAllKeys()
|
|
50
61
|
keysReq.onsuccess = () => {
|
|
51
62
|
const keys = keysReq.result
|
|
52
63
|
if (!keys.length) return resolve({})
|
|
53
64
|
const valsReq = db
|
|
54
|
-
.transaction(
|
|
55
|
-
.objectStore(
|
|
65
|
+
.transaction(FILES, 'readonly')
|
|
66
|
+
.objectStore(FILES)
|
|
56
67
|
.getAll()
|
|
57
68
|
valsReq.onsuccess = () => {
|
|
58
69
|
const files = {}
|
|
@@ -89,6 +100,36 @@
|
|
|
89
100
|
|
|
90
101
|
const clearAll = () => withStore('readwrite', (store) => store.clear())
|
|
91
102
|
|
|
103
|
+
/**
|
|
104
|
+
* The stored identity, `null` when there is none. A row whose shape is not the
|
|
105
|
+
* one this script writes is ignored rather than handed on: the worker refuses a
|
|
106
|
+
* fixture it cannot read by name, and re-sending a value that is not a row at
|
|
107
|
+
* all would blame the fixture for a corrupt database.
|
|
108
|
+
*/
|
|
109
|
+
const readIdentity = () =>
|
|
110
|
+
new Promise(async (resolve, reject) => {
|
|
111
|
+
try {
|
|
112
|
+
const db = await openDb()
|
|
113
|
+
const req = db
|
|
114
|
+
.transaction(IDENTITY, 'readonly')
|
|
115
|
+
.objectStore(IDENTITY)
|
|
116
|
+
.get(IDENTITY_KEY)
|
|
117
|
+
req.onsuccess = () => {
|
|
118
|
+
const row = req.result
|
|
119
|
+
resolve(row && typeof row === 'object' && row.fixture ? row : null)
|
|
120
|
+
}
|
|
121
|
+
req.onerror = () => reject(req.error)
|
|
122
|
+
} catch (e) {
|
|
123
|
+
reject(e)
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const writeIdentity = (row) =>
|
|
128
|
+
withStore('readwrite', (store) => store.put(row, IDENTITY_KEY), IDENTITY)
|
|
129
|
+
|
|
130
|
+
const clearIdentity = () =>
|
|
131
|
+
withStore('readwrite', (store) => store.delete(IDENTITY_KEY), IDENTITY)
|
|
132
|
+
|
|
92
133
|
/**
|
|
93
134
|
* The build this site is, as the page can see it: mock-config.json carries the
|
|
94
135
|
* stamp the build wrote over its host code and the seed it lays out. Read here
|
|
@@ -191,13 +232,29 @@
|
|
|
191
232
|
})
|
|
192
233
|
})
|
|
193
234
|
|
|
194
|
-
// Persist whatever the worker reports as changed
|
|
235
|
+
// Persist whatever the worker reports as changed: drive files, and the
|
|
236
|
+
// identity store's snapshot when a route created, changed or removed anything.
|
|
195
237
|
navigator.serviceWorker.addEventListener('message', (ev) => {
|
|
196
238
|
const data = ev.data || {}
|
|
197
|
-
if (data.type === 'sasjs-persist')
|
|
239
|
+
if (data.type === 'sasjs-persist') {
|
|
198
240
|
writeFiles(data.files).catch((e) =>
|
|
199
241
|
console.error('[sasjs-mock] persist failed', e)
|
|
200
242
|
)
|
|
243
|
+
if (data.identity)
|
|
244
|
+
readIdentity()
|
|
245
|
+
.then((row) =>
|
|
246
|
+
// `undefined`, not null: no stored subject means the fixture's own
|
|
247
|
+
// administrator, which is not the same as the anonymous caller a
|
|
248
|
+
// null would name
|
|
249
|
+
writeIdentity({
|
|
250
|
+
fixture: data.identity,
|
|
251
|
+
subject: row ? row.subject : undefined
|
|
252
|
+
})
|
|
253
|
+
)
|
|
254
|
+
.catch((e) =>
|
|
255
|
+
console.error('[sasjs-mock] identity persist failed', e)
|
|
256
|
+
)
|
|
257
|
+
}
|
|
201
258
|
if (data.type === 'sasjs-host-error')
|
|
202
259
|
console.error('[sasjs-mock] ' + data.path + ': ' + data.message)
|
|
203
260
|
})
|
|
@@ -206,7 +263,52 @@
|
|
|
206
263
|
readAll,
|
|
207
264
|
writeFiles,
|
|
208
265
|
clear: clearAll,
|
|
266
|
+
identity: readIdentity,
|
|
209
267
|
snapshot: () => askWorker({ type: 'sasjs-snapshot' }, 'sasjs-snapshot'),
|
|
268
|
+
/**
|
|
269
|
+
* Views the app as another account: `as('sasjsuser')` for a restricted user,
|
|
270
|
+
* `as()` or `as('admin')` to return to the fixture's administrator.
|
|
271
|
+
*
|
|
272
|
+
* The worker issues a token pair for the account and answers as it from then
|
|
273
|
+
* on; the subject and the fixture are stored here so a reload stays that
|
|
274
|
+
* account, and the app is restarted so every screen redraws its menus and
|
|
275
|
+
* refusals. The pair is returned for a caller who wants to send it as a
|
|
276
|
+
* bearer token - nothing signs it, and no password is checked.
|
|
277
|
+
*/
|
|
278
|
+
as: async (username) => {
|
|
279
|
+
const answer = await askWorker(
|
|
280
|
+
{ type: 'sasjs-as', username },
|
|
281
|
+
'sasjs-as-ack'
|
|
282
|
+
)
|
|
283
|
+
if (answer.error) throw new Error(answer.error)
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
await writeIdentity({
|
|
287
|
+
fixture: answer.identity,
|
|
288
|
+
subject: answer.username
|
|
289
|
+
})
|
|
290
|
+
} catch (e) {
|
|
291
|
+
// the subject still applies to this run; it just will not outlive it
|
|
292
|
+
console.error('[sasjs-mock] could not store the subject: ' + e)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const pair = {
|
|
296
|
+
username: answer.username,
|
|
297
|
+
accessToken: answer.accessToken,
|
|
298
|
+
refreshToken: answer.refreshToken
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// a task, so the caller's promise resolves with the pair first
|
|
302
|
+
setTimeout(() => {
|
|
303
|
+
try {
|
|
304
|
+
window.location.reload()
|
|
305
|
+
} catch (e) {
|
|
306
|
+
/* a page that cannot reload keeps the subject it already has */
|
|
307
|
+
}
|
|
308
|
+
}, 0)
|
|
309
|
+
|
|
310
|
+
return pair
|
|
311
|
+
},
|
|
210
312
|
/**
|
|
211
313
|
* The drive total in bytes, measured by the worker: it owns the stored
|
|
212
314
|
* representation, so the figure stays the one `statSync` reports rather than
|
|
@@ -249,20 +351,62 @@
|
|
|
249
351
|
return Object.keys(files).length
|
|
250
352
|
},
|
|
251
353
|
reset: async () => {
|
|
252
|
-
await askWorker({ type: 'sasjs-reset' }, 'sasjs-reset-ack')
|
|
354
|
+
const answer = await askWorker({ type: 'sasjs-reset' }, 'sasjs-reset-ack')
|
|
355
|
+
if (answer.error) throw new Error(answer.error)
|
|
253
356
|
await clearAll()
|
|
357
|
+
await clearIdentity()
|
|
254
358
|
return true
|
|
255
359
|
}
|
|
256
360
|
}
|
|
257
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Boots the worker with what is stored, and recovers when the stored identity
|
|
364
|
+
* is one this build cannot read.
|
|
365
|
+
*
|
|
366
|
+
* A fixture outlives the build that wrote it, exactly as the drive does, and
|
|
367
|
+
* the worker refuses one whose format stamp it does not speak - by name, and
|
|
368
|
+
* with `errorKind: 'identity'`. Only that answer drops the stored row: the
|
|
369
|
+
* row holds the accounts the visitor created as well as the fixture, so a
|
|
370
|
+
* host-side failure (a drive that could not be read) leaves it alone.
|
|
371
|
+
*/
|
|
372
|
+
const boot = async (files, stored) => {
|
|
373
|
+
const loading = Object.keys(files).length > 0
|
|
374
|
+
const type = loading ? 'sasjs-load' : 'sasjs-seed'
|
|
375
|
+
const ack = loading ? 'sasjs-load-ack' : 'sasjs-seed-ack'
|
|
376
|
+
|
|
377
|
+
const answer = await askWorker(
|
|
378
|
+
{
|
|
379
|
+
type,
|
|
380
|
+
files,
|
|
381
|
+
identity: stored ? stored.fixture : undefined,
|
|
382
|
+
subject: stored ? stored.subject : undefined
|
|
383
|
+
},
|
|
384
|
+
ack
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if (answer.error && stored && answer.errorKind === 'identity') {
|
|
388
|
+
console.info('[sasjs-mock] ' + answer.error)
|
|
389
|
+
try {
|
|
390
|
+
await clearIdentity()
|
|
391
|
+
} catch (e) {
|
|
392
|
+
console.error('[sasjs-mock] could not clear the stored identity: ' + e)
|
|
393
|
+
}
|
|
394
|
+
return await askWorker({ type, files }, ack)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return answer
|
|
398
|
+
}
|
|
399
|
+
|
|
258
400
|
window.__sasjsMockReady = (async () => {
|
|
259
401
|
await navigator.serviceWorker.ready
|
|
260
402
|
const controller = await waitForController()
|
|
261
403
|
if (!controller) return { error: 'no service worker controller' }
|
|
262
404
|
|
|
263
405
|
let files = {}
|
|
406
|
+
let stored = null
|
|
264
407
|
try {
|
|
265
408
|
files = await readAll()
|
|
409
|
+
stored = await readIdentity()
|
|
266
410
|
} catch (e) {
|
|
267
411
|
console.error('[sasjs-mock] could not read storage: ' + e)
|
|
268
412
|
}
|
|
@@ -287,23 +431,18 @@
|
|
|
287
431
|
)
|
|
288
432
|
try {
|
|
289
433
|
await clearAll()
|
|
434
|
+
// the identity is part of what this build ships, so a visitor who kept
|
|
435
|
+
// the previous build's fixture would never see a changed
|
|
436
|
+
// `mock-identity.json`: it goes with the drive, as it does on reset
|
|
437
|
+
await clearIdentity()
|
|
290
438
|
} catch (e) {
|
|
291
439
|
console.error('[sasjs-mock] could not clear storage: ' + e)
|
|
292
440
|
}
|
|
293
441
|
files = {}
|
|
442
|
+
stored = null
|
|
294
443
|
}
|
|
295
444
|
|
|
296
|
-
|
|
297
|
-
window.__sasjsMockBoot = await askWorker(
|
|
298
|
-
{ type: 'sasjs-load', files },
|
|
299
|
-
'sasjs-load-ack'
|
|
300
|
-
)
|
|
301
|
-
} else {
|
|
302
|
-
window.__sasjsMockBoot = await askWorker(
|
|
303
|
-
{ type: 'sasjs-seed' },
|
|
304
|
-
'sasjs-seed-ack'
|
|
305
|
-
)
|
|
306
|
-
}
|
|
445
|
+
window.__sasjsMockBoot = await boot(files, stored)
|
|
307
446
|
if (window.__sasjsMockBoot && !window.__sasjsMockBoot.error)
|
|
308
447
|
keepBuildStamp(stamp)
|
|
309
448
|
return window.__sasjsMockBoot
|