@sasjs/cli 6.29.0 → 6.30.1
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/internal/mockSite/assets/boot.js +23 -1
- package/build/commands/build/internal/mockSite/assets/persist.js +76 -5
- package/build/commands/build/internal/mockSite/assets/runtime-core.js +413 -50
- package/build/commands/build/internal/mockSite/assets/sw.js +182 -12
- package/build/commands/build/internal/mockSite/mockSite.d.ts +23 -0
- package/build/commands/build/internal/mockSite/mockSite.js +245 -29
- package/build/commands/build/internal/mockSite/spec/boot.spec.js +50 -3
- package/build/commands/build/internal/mockSite/spec/mockSite.spec.js +132 -3
- package/build/commands/build/internal/mockSite/spec/persist.spec.js +179 -1
- package/build/commands/build/internal/mockSite/spec/runtime-core.spec.js +546 -0
- package/build/commands/build/internal/mockSite/spec/sw.spec.js +587 -4
- package/build/commands/create/spec/create.spec.js +22 -18
- package/build/commands/deploy/spec/cbd.spec.js +149 -75
- package/build/utils/test.d.ts +12 -0
- package/build/utils/test.js +66 -1
- package/build/utils/utils.d.ts +8 -0
- package/build/utils/utils.js +16 -4
- package/package.json +2 -2
- package/build/commands/create/spec/angularAppFiles.d.ts +0 -2
- package/build/commands/create/spec/angularAppFiles.js +0 -117
- package/build/commands/create/spec/minimalAppFiles.d.ts +0 -2
- package/build/commands/create/spec/minimalAppFiles.js +0 -44
- package/build/commands/create/spec/reactAppFiles.d.ts +0 -2
- package/build/commands/create/spec/reactAppFiles.js +0 -93
|
@@ -12,6 +12,12 @@
|
|
|
12
12
|
* same tags, the same order, and the same globals, just later than the parser
|
|
13
13
|
* would have run them.
|
|
14
14
|
*
|
|
15
|
+
* Two of those start before the worker is ready, because neither needs it: the
|
|
16
|
+
* drive restore is the page's own read of IndexedDB and is handed over as soon
|
|
17
|
+
* as there is a worker, and the app's bundle is hinted by the page (a preload
|
|
18
|
+
* link the builder writes). Both then overlap the install instead of following
|
|
19
|
+
* it.
|
|
20
|
+
*
|
|
15
21
|
* The tag list arrives in the `data-scripts` attribute as JSON, written by
|
|
16
22
|
* buildMockSite. `defer` and `async` are not replayed: execution is sequenced
|
|
17
23
|
* on purpose here, because the host has to be ready before the first request
|
|
@@ -81,10 +87,26 @@
|
|
|
81
87
|
})
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
/*
|
|
91
|
+
* The drive restore is the page's own work: persist.js reads the stored drive
|
|
92
|
+
* and hands it to the worker as soon as there is one, so it neither needs nor
|
|
93
|
+
* waits for the registration below. Started here, its download and its
|
|
94
|
+
* IndexedDB read overlap the worker's install, which is the slowest part of a
|
|
95
|
+
* first visit - sequenced after it, they are two more round trips on top.
|
|
96
|
+
*/
|
|
97
|
+
var persist = new Promise(function (resolve) {
|
|
98
|
+
load('host/persist.js', resolve)
|
|
99
|
+
})
|
|
100
|
+
|
|
84
101
|
function start() {
|
|
85
|
-
|
|
102
|
+
persist.then(function () {
|
|
86
103
|
Promise.resolve(window.__sasjsMockReady).then(function (boot) {
|
|
87
104
|
if (boot && boot.error) console.error('[mock] boot: ' + boot.error)
|
|
105
|
+
// the drive was restored from less than the whole capture: the page still runs,
|
|
106
|
+
// but every file it did not already hold will read as missing, and nothing else
|
|
107
|
+
// says so
|
|
108
|
+
if (boot && boot.driveError)
|
|
109
|
+
console.error('[mock] drive: ' + boot.driveError)
|
|
88
110
|
load('host/intercept.js', function () {
|
|
89
111
|
replayAll(specs, function () {})
|
|
90
112
|
})
|
|
@@ -18,6 +18,13 @@
|
|
|
18
18
|
*/
|
|
19
19
|
const IDENTITY = 'identity'
|
|
20
20
|
const IDENTITY_KEY = 'current'
|
|
21
|
+
/**
|
|
22
|
+
* The drive paths the visitor deleted, kept beside the identity rather than in
|
|
23
|
+
* the files store because a deleted file has no row to keep it in. A sharded
|
|
24
|
+
* site's index lists every file the build ships, so this list is what stops a
|
|
25
|
+
* deletion from being undone by the next page load.
|
|
26
|
+
*/
|
|
27
|
+
const DELETED_KEY = 'deleted'
|
|
21
28
|
|
|
22
29
|
const openDb = () =>
|
|
23
30
|
new Promise((resolve, reject) => {
|
|
@@ -81,24 +88,70 @@
|
|
|
81
88
|
}
|
|
82
89
|
})
|
|
83
90
|
|
|
91
|
+
/** The drive paths this visitor deleted, `[]` when the store is new or unreadable. */
|
|
92
|
+
const readDeleted = async () => {
|
|
93
|
+
try {
|
|
94
|
+
const db = await openDb()
|
|
95
|
+
return await new Promise((resolve) => {
|
|
96
|
+
const req = db
|
|
97
|
+
.transaction(IDENTITY, 'readonly')
|
|
98
|
+
.objectStore(IDENTITY)
|
|
99
|
+
.get(DELETED_KEY)
|
|
100
|
+
req.onsuccess = () =>
|
|
101
|
+
resolve(Array.isArray(req.result) ? req.result : [])
|
|
102
|
+
req.onerror = () => resolve([])
|
|
103
|
+
})
|
|
104
|
+
} catch (e) {
|
|
105
|
+
return []
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// no tombstones is no row: an empty list left behind would read as a stored
|
|
110
|
+
// drive that had something in it
|
|
111
|
+
const writeDeleted = (paths) =>
|
|
112
|
+
paths.length
|
|
113
|
+
? withStore(
|
|
114
|
+
'readwrite',
|
|
115
|
+
(store) => store.put(paths, DELETED_KEY),
|
|
116
|
+
IDENTITY
|
|
117
|
+
)
|
|
118
|
+
: withStore('readwrite', (store) => store.delete(DELETED_KEY), IDENTITY)
|
|
119
|
+
|
|
84
120
|
const writeFiles = async (files) => {
|
|
85
121
|
const entries = Object.entries(files || {})
|
|
86
122
|
if (!entries.length) return { written: 0, deleted: 0 }
|
|
87
123
|
let deleted = 0
|
|
124
|
+
const removed = []
|
|
125
|
+
const written = []
|
|
88
126
|
await withStore('readwrite', (store) => {
|
|
89
127
|
for (const [path, contents] of entries) {
|
|
90
128
|
if (contents === null || contents === undefined) {
|
|
91
129
|
store.delete(path)
|
|
130
|
+
removed.push(path)
|
|
92
131
|
deleted++
|
|
93
132
|
} else {
|
|
94
133
|
store.put(contents, path)
|
|
134
|
+
written.push(path)
|
|
95
135
|
}
|
|
96
136
|
}
|
|
97
137
|
})
|
|
138
|
+
// the worker forgets deletions when it is terminated, so they are recorded
|
|
139
|
+
// here: a path written again is not deleted any more, and a path deleted
|
|
140
|
+
// again stays recorded
|
|
141
|
+
if (removed.length || written.length) {
|
|
142
|
+
const known = (await readDeleted()).filter(
|
|
143
|
+
(path) => !written.includes(path)
|
|
144
|
+
)
|
|
145
|
+
for (const path of removed) if (!known.includes(path)) known.push(path)
|
|
146
|
+
await writeDeleted(known)
|
|
147
|
+
}
|
|
98
148
|
return { written: entries.length - deleted, deleted }
|
|
99
149
|
}
|
|
100
150
|
|
|
101
|
-
const clearAll =
|
|
151
|
+
const clearAll = async () => {
|
|
152
|
+
await withStore('readwrite', (store) => store.clear())
|
|
153
|
+
await withStore('readwrite', (store) => store.delete(DELETED_KEY), IDENTITY)
|
|
154
|
+
}
|
|
102
155
|
|
|
103
156
|
/**
|
|
104
157
|
* The stored identity, `null` when there is none. A row whose shape is not the
|
|
@@ -334,6 +387,12 @@
|
|
|
334
387
|
{ type: 'sasjs-snapshot' },
|
|
335
388
|
'sasjs-snapshot'
|
|
336
389
|
)
|
|
390
|
+
// the worker answers `sasjs-snapshot` with whatever is in memory plus an
|
|
391
|
+
// `error` when it could not read the drive whole. Downloading that would
|
|
392
|
+
// hand the next build a capture with every app bundle missing and no sign
|
|
393
|
+
// of it, which is the failure "whole means whole" exists to prevent.
|
|
394
|
+
if (snapshot && snapshot.error)
|
|
395
|
+
throw new Error('The drive could not be read whole: ' + snapshot.error)
|
|
337
396
|
const files = (snapshot && snapshot.files) || {}
|
|
338
397
|
// the format stamp the runtime checks on load: a snapshot that outlived a
|
|
339
398
|
// marker-format change is refused by name, instead of decoding as text
|
|
@@ -369,15 +428,23 @@
|
|
|
369
428
|
* row holds the accounts the visitor created as well as the fixture, so a
|
|
370
429
|
* host-side failure (a drive that could not be read) leaves it alone.
|
|
371
430
|
*/
|
|
372
|
-
const boot = async (files, stored) => {
|
|
373
|
-
|
|
431
|
+
const boot = async (files, stored, deleted) => {
|
|
432
|
+
// a drive of nothing but deletions is still a drive the visitor had: seeding
|
|
433
|
+
// over it would bring back every file they removed
|
|
434
|
+
const loading =
|
|
435
|
+
Object.keys(files).length > 0 || (deleted && deleted.length > 0)
|
|
374
436
|
const type = loading ? 'sasjs-load' : 'sasjs-seed'
|
|
375
437
|
const ack = loading ? 'sasjs-load-ack' : 'sasjs-seed-ack'
|
|
376
438
|
|
|
439
|
+
// only when there are some: an empty list is the common case, and the worker
|
|
440
|
+
// reads an absent one as "nothing was deleted"
|
|
441
|
+
const deletions = loading && deleted.length ? deleted : undefined
|
|
442
|
+
|
|
377
443
|
const answer = await askWorker(
|
|
378
444
|
{
|
|
379
445
|
type,
|
|
380
446
|
files,
|
|
447
|
+
deleted: deletions,
|
|
381
448
|
identity: stored ? stored.fixture : undefined,
|
|
382
449
|
subject: stored ? stored.subject : undefined
|
|
383
450
|
},
|
|
@@ -391,7 +458,7 @@
|
|
|
391
458
|
} catch (e) {
|
|
392
459
|
console.error('[sasjs-mock] could not clear the stored identity: ' + e)
|
|
393
460
|
}
|
|
394
|
-
return await askWorker({ type, files }, ack)
|
|
461
|
+
return await askWorker({ type, files, deleted: deletions }, ack)
|
|
395
462
|
}
|
|
396
463
|
|
|
397
464
|
return answer
|
|
@@ -442,7 +509,11 @@
|
|
|
442
509
|
stored = null
|
|
443
510
|
}
|
|
444
511
|
|
|
445
|
-
|
|
512
|
+
// the deletions go with the drive: a sharded site's index lists every file
|
|
513
|
+
// the build ships, so the paths this visitor removed have to be named or the
|
|
514
|
+
// page would restore them
|
|
515
|
+
const deleted = await readDeleted()
|
|
516
|
+
window.__sasjsMockBoot = await boot(files, stored, deleted)
|
|
446
517
|
if (window.__sasjsMockBoot && !window.__sasjsMockBoot.error)
|
|
447
518
|
keepBuildStamp(stamp)
|
|
448
519
|
return window.__sasjsMockBoot
|