* Attempt to quickly reach a state where it's safe to serve responses.
()
| 646 | * Attempt to quickly reach a state where it's safe to serve responses. |
| 647 | */ |
| 648 | private async initialize(): Promise<void> { |
| 649 | // On initialization, all of the serialized state is read out of the 'control' |
| 650 | // table. This includes: |
| 651 | // - map of hashes to manifests of currently loaded application versions |
| 652 | // - map of client IDs to their pinned versions |
| 653 | // - record of the most recently fetched manifest hash |
| 654 | // |
| 655 | // If these values don't exist in the DB, then this is the either the first time |
| 656 | // the SW has run or the DB state has been wiped or is inconsistent. In that case, |
| 657 | // load a fresh copy of the manifest and reset the state from scratch. |
| 658 | |
| 659 | const table = await this.controlTable; |
| 660 | |
| 661 | // Attempt to load the needed state from the DB. If this fails, the catch {} block |
| 662 | // will populate these variables with freshly constructed values. |
| 663 | let manifests: ManifestMap, assignments: ClientAssignments, latest: LatestEntry; |
| 664 | try { |
| 665 | // Read them from the DB simultaneously. |
| 666 | [manifests, assignments, latest] = await Promise.all([ |
| 667 | table.read<ManifestMap>('manifests'), |
| 668 | table.read<ClientAssignments>('assignments'), |
| 669 | table.read<LatestEntry>('latest'), |
| 670 | ]); |
| 671 | |
| 672 | // Make sure latest manifest is correctly installed. If not (e.g. corrupted data), |
| 673 | // it could stay locked in EXISTING_CLIENTS_ONLY or SAFE_MODE state. |
| 674 | if (!this.versions.has(latest.latest) && !manifests.hasOwnProperty(latest.latest)) { |
| 675 | this.debugger.log( |
| 676 | `Missing manifest for latest version hash ${latest.latest}`, |
| 677 | 'initialize: read from DB', |
| 678 | ); |
| 679 | throw new Error(`Missing manifest for latest hash ${latest.latest}`); |
| 680 | } |
| 681 | |
| 682 | // Successfully loaded from saved state. This implies a manifest exists, so |
| 683 | // the update check needs to happen in the background. |
| 684 | this.idle.schedule('init post-load (update)', async () => { |
| 685 | await this.checkForUpdate(); |
| 686 | }); |
| 687 | } catch (_) { |
| 688 | // Something went wrong. Try to start over by fetching a new manifest from the |
| 689 | // server and building up an empty initial state. |
| 690 | const manifest = await this.fetchLatestManifest(); |
| 691 | const hash = hashManifest(manifest); |
| 692 | manifests = {[hash]: manifest}; |
| 693 | assignments = {}; |
| 694 | latest = {latest: hash}; |
| 695 | |
| 696 | // Save the initial state to the DB. |
| 697 | await Promise.all([ |
| 698 | table.write('manifests', manifests), |
| 699 | table.write('assignments', assignments), |
| 700 | table.write('latest', latest), |
| 701 | ]); |
| 702 | } |
| 703 | |
| 704 | // At this point, either the state has been loaded successfully, or fresh state |
| 705 | // with a new copy of the manifest has been produced. At this point, the `Driver` |
no test coverage detected