create-eigen-game 0.16.2 → 0.17.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/package.json +1 -1
- package/templates/app-overlay/web/firebase-messaging-sw.js.template +2 -2
- package/templates/app-overlay/web/flutter_bootstrap.js.template +38 -25
- package/templates/ci/.github/workflows/checks.yml.template +9 -7
- package/templates/project/README.md +3 -2
- package/templates/project/package.json +3 -2
- package/templates/project/workbox-config.cjs +45 -0
- package/templates/app-overlay/web/README.md +0 -11
- package/templates/app-overlay/web/drift_worker.js +0 -13453
- package/templates/app-overlay/web/eigen_offline_sw.js +0 -82
- package/templates/app-overlay/web/sqlite3.wasm +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Keep this version aligned with the Firebase JS SDK supported by the
|
|
2
2
|
// firebase_core_web version resolved by the Flutter app.
|
|
3
|
-
importScripts("https://www.gstatic.com/firebasejs/12.
|
|
4
|
-
importScripts("https://www.gstatic.com/firebasejs/12.
|
|
3
|
+
importScripts("https://www.gstatic.com/firebasejs/12.19.0/firebase-app-compat.js");
|
|
4
|
+
importScripts("https://www.gstatic.com/firebasejs/12.19.0/firebase-messaging-compat.js");
|
|
5
5
|
importScripts("firebase-config.js");
|
|
6
6
|
|
|
7
7
|
// `firebase:configure` generates this public configuration from the Web app
|
|
@@ -1,30 +1,43 @@
|
|
|
1
1
|
{{flutter_js}}
|
|
2
2
|
{{flutter_build_config}}
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
.register("firebase-messaging-sw.js", {
|
|
8
|
-
scope: "/firebase-cloud-messaging-push-scope",
|
|
9
|
-
})
|
|
10
|
-
.catch((error) => {
|
|
11
|
-
console.warn(
|
|
12
|
-
"Firebase Messaging service worker registration failed",
|
|
13
|
-
error,
|
|
14
|
-
);
|
|
15
|
-
});
|
|
16
|
-
}
|
|
4
|
+
// Flutter starts at once. Neither worker below is anything this page waits on,
|
|
5
|
+
// so they register beside it rather than ahead of it.
|
|
6
|
+
_flutter.loader.load();
|
|
17
7
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
8
|
+
if ("serviceWorker" in navigator) {
|
|
9
|
+
// The app's own worker, generated by Workbox (workbox-config.cjs): the
|
|
10
|
+
// precached build, which is what lets the app open with no network.
|
|
11
|
+
navigator.serviceWorker.register("sw.js").catch((error) => {
|
|
12
|
+
console.warn("App service worker registration failed", error);
|
|
13
|
+
});
|
|
14
|
+
// Firebase Messaging's worker, which owns only its push scope.
|
|
15
|
+
navigator.serviceWorker.register("firebase-messaging-sw.js", { scope: "/firebase-cloud-messaging-push-scope" }).catch((error) => {
|
|
16
|
+
console.warn("Firebase Messaging service worker registration failed", error);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
28
19
|
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
// CanvasKit and the Firebase JS SDK load from Google's CDN, which the app's
|
|
21
|
+
// worker caches as they pass through it. A first visit fetches them before that
|
|
22
|
+
// worker controls the page, so the page copies what it loaded into the same
|
|
23
|
+
// cache ("eigen-cdn" in workbox-config.cjs), and drops versions of each it no
|
|
24
|
+
// longer loads. Without this, the first visit would not open offline.
|
|
25
|
+
if ("caches" in self && "PerformanceObserver" in self) {
|
|
26
|
+
const cdn = /^https:\/\/www\.gstatic\.com\/(flutter-canvaskit|firebasejs)\/[^/]+\//;
|
|
27
|
+
new PerformanceObserver(async (entries) => {
|
|
28
|
+
for (const { name } of entries.getEntries()) {
|
|
29
|
+
const loaded = cdn.exec(name);
|
|
30
|
+
if (!loaded) continue;
|
|
31
|
+
try {
|
|
32
|
+
const cache = await caches.open("eigen-cdn");
|
|
33
|
+
if (!(await cache.match(name))) await cache.add(name);
|
|
34
|
+
for (const request of await cache.keys()) {
|
|
35
|
+
const cached = cdn.exec(request.url);
|
|
36
|
+
if (cached && cached[1] === loaded[1] && cached[0] !== loaded[0]) await cache.delete(request);
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Only a later offline start is at stake, and the next load tries again.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}).observe({ type: "resource", buffered: true });
|
|
43
|
+
}
|
|
@@ -109,17 +109,19 @@ jobs:
|
|
|
109
109
|
run: flutter test
|
|
110
110
|
|
|
111
111
|
# A VM widget test can pass while a dependency still imports dart:io or
|
|
112
|
-
# calls an unsupported plugin.
|
|
113
|
-
#
|
|
114
|
-
#
|
|
112
|
+
# calls an unsupported plugin. Building the real web app is what actually
|
|
113
|
+
# proves it ships, mirroring eigen-flutter's own `example` job, which does
|
|
114
|
+
# the same for the RPS reference app. `build:web` is the deploy's own
|
|
115
|
+
# build: Flutter, then the service worker Workbox generates from it, so
|
|
116
|
+
# the root's Node tools are installed first.
|
|
115
117
|
#
|
|
116
118
|
# No Android SDK step in this job, for the same reason
|
|
117
119
|
# `create-eigen-game`'s own `scaffold-e2e.mjs` check skips one:
|
|
118
120
|
# `flutter analyze`/`test`/`build web` don't need it, and it would
|
|
119
121
|
# roughly double this job for no extra coverage. The Android build is
|
|
120
122
|
# verified for real, with a real signing config, in `release.yml`.
|
|
123
|
+
- name: Install root dependencies
|
|
124
|
+
run: {{PACKAGE_MANAGER}} install
|
|
125
|
+
|
|
121
126
|
- name: Build web
|
|
122
|
-
|
|
123
|
-
run: >
|
|
124
|
-
flutter build web --release
|
|
125
|
-
--dart-define-from-file=app-config.json
|
|
127
|
+
run: {{PACKAGE_MANAGER}} run build:web
|
|
@@ -88,8 +88,9 @@ values in `app/app-config.json`, then deploy both halves together:
|
|
|
88
88
|
{{PACKAGE_MANAGER}} run deploy
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
That builds Flutter into `server/public
|
|
92
|
-
|
|
91
|
+
That builds Flutter into `server/public/`, generates the service worker that lets
|
|
92
|
+
the web app open offline (`workbox-config.cjs`), and deploys one Worker
|
|
93
|
+
containing the SPA and API. The app is `/`; invite and game URLs open the installed native app
|
|
93
94
|
or the browser SPA; the native install landing page is `/download`.
|
|
94
95
|
|
|
95
96
|
Configure Android, Flutter Web, and the messaging service worker together:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build:android": "cd app && flutter build appbundle --release --dart-define-from-file=app-config.json",
|
|
6
|
-
"build:web": "cd app && flutter build web --release --output ../server/public --dart-define-from-file=app-config.json",
|
|
6
|
+
"build:web": "cd app && flutter build web --release --output ../server/public --dart-define-from-file=app-config.json && cd .. && workbox generateSW workbox-config.cjs",
|
|
7
7
|
"contract": "cd server && {{PACKAGE_MANAGER}} run contract && cd ../app && dart run eigen_codegen:generate_payloads --contract ../server/game-contract.json --output lib/game/generated/payloads.dart --fixtures-output test/fixtures",
|
|
8
8
|
"contract:check": "cd server && {{PACKAGE_MANAGER}} run contract:check && cd ../app && dart run eigen_codegen:generate_payloads --contract ../server/game-contract.json --output lib/game/generated/payloads.dart --fixtures-output test/fixtures --check",
|
|
9
9
|
"deploy": "{{PACKAGE_MANAGER}} run build:web && cd server && {{PACKAGE_MANAGER}} run deploy",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"lint": "biome check ."
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@biomejs/biome": "2.5.12"
|
|
15
|
+
"@biomejs/biome": "2.5.12",
|
|
16
|
+
"workbox-cli": "7.4.1"
|
|
16
17
|
}
|
|
17
18
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// The web app's service worker, generated by Workbox from the Flutter build
|
|
2
|
+
// (decision 0014). `build:web` runs it after building
|
|
3
|
+
// Flutter into `server/public/`.
|
|
4
|
+
//
|
|
5
|
+
// Every file the app is built from is precached when the worker installs, so
|
|
6
|
+
// the app opens with no network from the first visit on, and always from one
|
|
7
|
+
// consistent build. A deploy reaches a player as a new worker that precaches
|
|
8
|
+
// the new build beside the old one and takes over once no tab still runs the
|
|
9
|
+
// old build, or at once when the app asks it to because the engine requires the
|
|
10
|
+
// update.
|
|
11
|
+
|
|
12
|
+
// Paths the game Worker answers itself rather than the app shell: its
|
|
13
|
+
// `run_worker_first` routes in wrangler.jsonc, less `/join/*` and `/game/*`,
|
|
14
|
+
// which render the app. A navigation to any other path opens the app.
|
|
15
|
+
const workerRoutes = [/^\/api\//, /^\/avatars\//, /^\/\.well-known\//, /^\/(health|download|terms|privacy|delete-account|robots\.txt|sitemap\.xml|site\.webmanifest)$/];
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
globDirectory: "server/public",
|
|
19
|
+
swDest: "server/public/sw.js",
|
|
20
|
+
globPatterns: ["index.html", "flutter_bootstrap.js", "main.dart.*", "manifest.json", "favicon.png", "icons/**", "assets/**"],
|
|
21
|
+
globIgnores: ["**/*.map", "**/*.symbols"],
|
|
22
|
+
// `main.dart.js` alone is several megabytes, and a game's assets may be too.
|
|
23
|
+
// A file over the limit would be left out with only a warning, and the app
|
|
24
|
+
// would then fail offline, so the limit is set well above anything expected.
|
|
25
|
+
maximumFileSizeToCacheInBytes: 32 * 1024 * 1024,
|
|
26
|
+
navigateFallback: "index.html",
|
|
27
|
+
navigateFallbackDenylist: workerRoutes,
|
|
28
|
+
runtimeCaching: [
|
|
29
|
+
{
|
|
30
|
+
// CanvasKit and the Firebase JS SDK load from Google's CDN at URLs that
|
|
31
|
+
// name their version, so a cached copy is never stale. The page adds what
|
|
32
|
+
// a first visit loaded before this worker controlled it (see
|
|
33
|
+
// web/flutter_bootstrap.js), under the same cache name.
|
|
34
|
+
urlPattern: /^https:\/\/www\.gstatic\.com\/(flutter-canvaskit|firebasejs)\//,
|
|
35
|
+
handler: "CacheFirst",
|
|
36
|
+
options: { cacheName: "eigen-cdn", cacheableResponse: { statuses: [200] } },
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
skipWaiting: false,
|
|
40
|
+
clientsClaim: true,
|
|
41
|
+
cleanupOutdatedCaches: true,
|
|
42
|
+
inlineWorkboxRuntime: true,
|
|
43
|
+
sourcemap: false,
|
|
44
|
+
mode: "production",
|
|
45
|
+
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# Web assets
|
|
2
|
-
|
|
3
|
-
`sqlite3.wasm` and `drift_worker.js` are drift's web runtime, taken verbatim
|
|
4
|
-
from the [drift 2.35.0
|
|
5
|
-
release](https://github.com/simolus3/drift/releases/tag/drift-2.35.0). They are
|
|
6
|
-
served from the app's own origin because that is where `LocalDatabase` looks for
|
|
7
|
-
them, and they are what gives a browser the storage offline play needs.
|
|
8
|
-
|
|
9
|
-
Replace both together whenever the `drift` constraint in `eigen_flutter`'s
|
|
10
|
-
pubspec moves, and take them from the matching release: the worker and the
|
|
11
|
-
module are one pair.
|