@dxos/plugin-pwa 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.13.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/dist/lib/PwaPlugin.mjs +2 -13
- package/dist/lib/PwaPlugin.mjs.map +1 -1
- package/dist/lib/capabilities.browser.mjs +142 -0
- package/dist/lib/capabilities.browser.mjs.map +1 -0
- package/dist/lib/{capabilities.mjs → capabilities.node.mjs} +50 -13
- package/dist/lib/capabilities.node.mjs.map +1 -0
- package/dist/lib/capabilities.tauri.mjs +142 -0
- package/dist/lib/capabilities.tauri.mjs.map +1 -0
- package/dist/lib/capabilities.workerd.mjs +142 -0
- package/dist/lib/capabilities.workerd.mjs.map +1 -0
- package/dist/lib/chunk-PwaSettings.mjs +44 -0
- package/dist/lib/chunk-PwaSettings.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +17 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
- package/dist/lib/chunk-settings.mjs +23 -0
- package/dist/lib/chunk-settings.mjs.map +1 -0
- package/dist/lib/chunk-update-manager.mjs +64 -0
- package/dist/lib/chunk-update-manager.mjs.map +1 -0
- package/dist/lib/containers.mjs +7 -0
- package/dist/lib/containers.mjs.map +1 -0
- package/dist/lib/index.mjs +0 -1
- package/dist/lib/meta.mjs.map +1 -1
- package/dist/lib/plugin.mjs +3 -3
- package/dist/lib/plugin.mjs.map +1 -1
- package/dist/lib/translations.mjs +16 -1
- package/dist/lib/translations.mjs.map +1 -1
- package/dist/lib/types.mjs +15 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/capabilities/gen/browser.d.ts +7 -0
- package/dist/types/src/capabilities/gen/browser.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/node.d.ts +7 -0
- package/dist/types/src/capabilities/gen/node.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/tauri.d.ts +7 -0
- package/dist/types/src/capabilities/gen/tauri.d.ts.map +1 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts +7 -0
- package/dist/types/src/capabilities/gen/workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +2 -0
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +5 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/capabilities/update-manager.d.ts +35 -0
- package/dist/types/src/capabilities/update-manager.d.ts.map +1 -0
- package/dist/types/src/capabilities/update-manager.test.d.ts +2 -0
- package/dist/types/src/capabilities/update-manager.test.d.ts.map +1 -0
- package/dist/types/src/containers/PwaSettings/PwaSettings.d.ts +10 -0
- package/dist/types/src/containers/PwaSettings/PwaSettings.d.ts.map +1 -0
- package/dist/types/src/containers/PwaSettings/index.d.ts +2 -0
- package/dist/types/src/containers/PwaSettings/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +3 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/plugin.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +15 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Settings.d.ts +10 -0
- package/dist/types/src/types/Settings.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +2 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +36 -15
- package/src/capabilities/gen/browser.ts +218 -0
- package/src/capabilities/gen/node.ts +216 -0
- package/src/capabilities/gen/tauri.ts +218 -0
- package/src/capabilities/gen/workerd.ts +216 -0
- package/src/capabilities/index.ts +70 -4
- package/src/capabilities/react-surface.ts +25 -0
- package/src/capabilities/settings.ts +30 -0
- package/src/capabilities/update-manager.test.ts +102 -0
- package/src/capabilities/update-manager.ts +107 -0
- package/src/containers/PwaSettings/PwaSettings.tsx +46 -0
- package/src/containers/PwaSettings/index.ts +5 -0
- package/src/containers/index.ts +7 -0
- package/src/index.ts +1 -2
- package/src/meta.ts +1 -1
- package/src/plugin.tsx +3 -1
- package/src/pwa.d.ts +1 -0
- package/src/translations.ts +18 -2
- package/src/types/Settings.ts +17 -0
- package/src/types/index.ts +5 -0
- package/dist/lib/capabilities.mjs.map +0 -1
package/dist/lib/PwaPlugin.mjs
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
2
|
import { meta as meta$1 } from "#meta";
|
|
3
|
-
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __exportAll = (all, no_symbols) => {
|
|
6
|
-
let target = {};
|
|
7
|
-
for (var name in all) __defProp(target, name, {
|
|
8
|
-
get: all[name],
|
|
9
|
-
enumerable: true
|
|
10
|
-
});
|
|
11
|
-
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
12
|
-
return target;
|
|
13
|
-
};
|
|
14
|
-
//#endregion
|
|
3
|
+
import * as Plugin from "@dxos/app-framework/Plugin";
|
|
15
4
|
//#region src/PwaPlugin.ts
|
|
16
5
|
var PwaPlugin_exports = /* @__PURE__ */ __exportAll({
|
|
17
6
|
make: () => make,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PwaPlugin.mjs","names":[],"sources":["../../src/PwaPlugin.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Plugin from '@dxos/app-framework/Plugin';\n\nimport { meta as pluginMeta } from '#meta';\n\n/** Plugin metadata, available without loading the plugin body. */\nexport const meta = pluginMeta;\n\n/** Constructs the plugin; the body loads on first enable. */\nexport const make = Plugin.lazy(meta, () => import('#plugin'));\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"PwaPlugin.mjs","names":[],"sources":["../../src/PwaPlugin.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Plugin from '@dxos/app-framework/Plugin';\n\nimport { meta as pluginMeta } from '#meta';\n\n/** Plugin metadata, available without loading the plugin body. */\nexport const meta = pluginMeta;\n\n/** Constructs the plugin; the body loads on first enable. */\nexport const make = Plugin.lazy(meta, () => import('#plugin'));\n"],"mappings":";;;;;;;;;AAWA,IAAa,OAAO;;AAGpB,IAAa,OAAO,OAAO,KAAK,YAAY,OAAO,UAAU"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { n as progressStatus, t as makeUpdateManager } from "./chunk-update-manager.mjs";
|
|
2
|
+
import * as Duration from "effect/Duration";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Fiber from "effect/Fiber";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
import * as Predicate from "effect/Predicate";
|
|
7
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
8
|
+
import * as Schedule from "effect/Schedule";
|
|
9
|
+
import { registerSW } from "virtual:pwa-register";
|
|
10
|
+
import * as ActivationEvents from "@dxos/app-framework/ActivationEvents";
|
|
11
|
+
import * as Capabilities from "@dxos/app-framework/Capabilities";
|
|
12
|
+
import * as Capability from "@dxos/app-framework/Capability";
|
|
13
|
+
import * as AppCapabilities from "@dxos/app-toolkit/AppCapabilities";
|
|
14
|
+
import * as AppCapability from "@dxos/app-toolkit/AppCapability";
|
|
15
|
+
import * as LayoutOperation from "@dxos/app-toolkit/LayoutOperation";
|
|
16
|
+
import { log } from "@dxos/log";
|
|
17
|
+
import { meta } from "#meta";
|
|
18
|
+
import { translations } from "#translations";
|
|
19
|
+
//#region src/capabilities/gen/browser.ts
|
|
20
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-pwa/src/capabilities/gen/browser.ts";
|
|
21
|
+
var UPDATE_CHECK_INTERVAL = Duration.minutes(15);
|
|
22
|
+
var isPrecacheProgress = (data) => Predicate.isObject(data) && data.type === PRECACHE_PROGRESS;
|
|
23
|
+
/**
|
|
24
|
+
* Progress envelope posted by the host's service worker while it precaches a build. The contract is
|
|
25
|
+
* duplicated in `composer-app/src/sw.ts` so the worker bundle stays free of host code — keep in sync.
|
|
26
|
+
*/
|
|
27
|
+
var PRECACHE_PROGRESS = "dxos:precache-progress";
|
|
28
|
+
var RegisterPwa = Capability.inlineModule("RegisterPwa", {
|
|
29
|
+
requires: [Capabilities.OperationInvoker, Capabilities.AtomRegistry],
|
|
30
|
+
provides: [AppCapabilities.UpdateManager]
|
|
31
|
+
}, Effect.fnUntraced(function* () {
|
|
32
|
+
const { invokePromise } = yield* Capabilities.OperationInvoker;
|
|
33
|
+
const atomRegistry = yield* Capabilities.AtomRegistry;
|
|
34
|
+
const supported = typeof navigator !== "undefined" && "serviceWorker" in navigator;
|
|
35
|
+
const dev = false;
|
|
36
|
+
const statusAtom = Atom.make(!supported ? { kind: "unsupported" } : { kind: "idle" }).pipe(Atom.keepAlive);
|
|
37
|
+
const setStatus = (status) => atomRegistry.set(statusAtom, status);
|
|
38
|
+
let registration;
|
|
39
|
+
let resolveRegistration;
|
|
40
|
+
const whenRegistered = new Promise((resolve) => {
|
|
41
|
+
resolveRegistration = resolve;
|
|
42
|
+
});
|
|
43
|
+
const updateSW = registerSW({
|
|
44
|
+
onRegisteredSW: (_swUrl, swRegistration) => {
|
|
45
|
+
registration = swRegistration;
|
|
46
|
+
resolveRegistration(swRegistration);
|
|
47
|
+
},
|
|
48
|
+
onNeedRefresh: () => {
|
|
49
|
+
setStatus({ kind: "ready" });
|
|
50
|
+
invokePromise(LayoutOperation.AddToast, {
|
|
51
|
+
id: `${meta.profile.key}.need-refresh`,
|
|
52
|
+
title: ["need-refresh.label", { ns: meta.profile.key }],
|
|
53
|
+
description: ["need-refresh.description", { ns: meta.profile.key }],
|
|
54
|
+
duration: Infinity,
|
|
55
|
+
actionLabel: ["refresh.label", { ns: meta.profile.key }],
|
|
56
|
+
actionAlt: ["refresh.alt", { ns: meta.profile.key }],
|
|
57
|
+
onAction: () => updateSW(true)
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
onOfflineReady: () => {
|
|
61
|
+
invokePromise(LayoutOperation.AddToast, {
|
|
62
|
+
id: `${meta.profile.key}.offline-ready`,
|
|
63
|
+
title: ["offline-ready.label", { ns: meta.profile.key }],
|
|
64
|
+
closeLabel: ["confirm.label", { ns: meta.profile.key }]
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
onRegisterError: (err) => {
|
|
68
|
+
log.error(err, void 0, {
|
|
69
|
+
"~LogMeta": "~LogMeta",
|
|
70
|
+
F: __dxlog_file,
|
|
71
|
+
L: 104,
|
|
72
|
+
S: this
|
|
73
|
+
});
|
|
74
|
+
setStatus({
|
|
75
|
+
kind: "failed",
|
|
76
|
+
error: err instanceof Error ? err.message : String(err)
|
|
77
|
+
});
|
|
78
|
+
resolveRegistration(void 0);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const handleProgress = (event) => {
|
|
82
|
+
if (!isPrecacheProgress(event.data) || !event.data.isUpdate) return;
|
|
83
|
+
setStatus(progressStatus(event.data));
|
|
84
|
+
};
|
|
85
|
+
navigator.serviceWorker?.addEventListener("message", handleProgress);
|
|
86
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => navigator.serviceWorker?.removeEventListener("message", handleProgress)));
|
|
87
|
+
const manager = makeUpdateManager({
|
|
88
|
+
status: statusAtom,
|
|
89
|
+
setStatus,
|
|
90
|
+
supported,
|
|
91
|
+
dev,
|
|
92
|
+
registration: whenRegistered,
|
|
93
|
+
container: navigator.serviceWorker,
|
|
94
|
+
skipWaiting: () => updateSW(true),
|
|
95
|
+
reload: () => window.location.reload()
|
|
96
|
+
});
|
|
97
|
+
const fiber = yield* Effect.tryPromise(async () => {
|
|
98
|
+
await registration?.update();
|
|
99
|
+
}).pipe(Effect.catch((error) => Effect.sync(() => log.warn("service worker update check failed", { error }, {
|
|
100
|
+
"~LogMeta": "~LogMeta",
|
|
101
|
+
F: __dxlog_file,
|
|
102
|
+
L: 147,
|
|
103
|
+
S: this
|
|
104
|
+
})))).pipe(Effect.repeat(Schedule.fixed(UPDATE_CHECK_INTERVAL)), Effect.forkDetach);
|
|
105
|
+
yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));
|
|
106
|
+
return Capability.contribute(AppCapabilities.UpdateManager, manager);
|
|
107
|
+
}));
|
|
108
|
+
var UpdateProgress = Capability.inlineModule("UpdateProgress", { provides: [] }, Effect.fnUntraced(function* () {
|
|
109
|
+
const registryOption = yield* Capability.getOption(AppCapabilities.ProgressRegistry);
|
|
110
|
+
if (Option.isNone(registryOption)) return [];
|
|
111
|
+
const registry = registryOption.value;
|
|
112
|
+
let monitor;
|
|
113
|
+
const handleMessage = (event) => {
|
|
114
|
+
if (!isPrecacheProgress(event.data)) return;
|
|
115
|
+
const { current, total, isUpdate, done } = event.data;
|
|
116
|
+
if (done && !monitor) return;
|
|
117
|
+
monitor ??= registry.register(`${meta.profile.key}.precache`, {
|
|
118
|
+
label: isUpdate ? "Downloading app update" : "Preparing offline use",
|
|
119
|
+
total
|
|
120
|
+
});
|
|
121
|
+
monitor.total(total);
|
|
122
|
+
monitor.set(current);
|
|
123
|
+
if (done) {
|
|
124
|
+
monitor.done();
|
|
125
|
+
monitor.remove();
|
|
126
|
+
monitor = void 0;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
navigator.serviceWorker?.addEventListener("message", handleMessage);
|
|
130
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
131
|
+
navigator.serviceWorker?.removeEventListener("message", handleMessage);
|
|
132
|
+
monitor?.remove();
|
|
133
|
+
}));
|
|
134
|
+
return [];
|
|
135
|
+
}));
|
|
136
|
+
var PwaSettings = AppCapability.settings(() => import("./chunk-settings.mjs"), { activatesOn: ActivationEvents.Idle });
|
|
137
|
+
var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article"] });
|
|
138
|
+
var Translations = AppCapability.translations(translations);
|
|
139
|
+
//#endregion
|
|
140
|
+
export { PwaSettings, ReactSurface, RegisterPwa, Translations, UpdateProgress };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=capabilities.browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.browser.mjs","names":[],"sources":["../../src/capabilities/gen/browser.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'browser' condition.\n\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport * as Fiber from 'effect/Fiber';\nimport * as Option from 'effect/Option';\nimport * as Predicate from 'effect/Predicate';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Schedule from 'effect/Schedule';\nimport { registerSW } from 'virtual:pwa-register';\n\nimport * as ActivationEvents from '@dxos/app-framework/ActivationEvents';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as AppUpdate from '@dxos/app-toolkit/AppUpdate';\nimport * as LayoutOperation from '@dxos/app-toolkit/LayoutOperation';\nimport { log } from '@dxos/log';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport { makeUpdateManager, progressStatus } from '../update-manager.ts';\n\n// Every 15 minutes rather than hourly. A check is one conditional request for the worker script, and\n// finding an update sooner costs nothing extra: the install happens once per deployed version either\n// way. An hour was long enough to leave a tab a full release behind.\nconst UPDATE_CHECK_INTERVAL = Duration.minutes(15);\n\nconst isPrecacheProgress = (data: unknown): data is PrecacheProgress =>\n Predicate.isObject(data) && data.type === PRECACHE_PROGRESS;\n\n/**\n * Progress envelope posted by the host's service worker while it precaches a build. The contract is\n * duplicated in `composer-app/src/sw.ts` so the worker bundle stays free of host code — keep in sync.\n */\nconst PRECACHE_PROGRESS = 'dxos:precache-progress';\n\ntype PrecacheProgress = {\n type: typeof PRECACHE_PROGRESS;\n current: number;\n total: number;\n isUpdate: boolean;\n done: boolean;\n};\n\nexport const RegisterPwa = Capability.inlineModule(\n 'RegisterPwa',\n { requires: [Capabilities.OperationInvoker, Capabilities.AtomRegistry], provides: [AppCapabilities.UpdateManager] },\n Effect.fnUntraced(function* () {\n const { invokePromise } = yield* Capabilities.OperationInvoker;\n const atomRegistry = yield* Capabilities.AtomRegistry;\n\n // No service workers at all on an insecure origin and in some embedded webviews. Under the vite dev\n // server the plugin still loads, but `virtual:pwa-register` is a stub that never registers.\n const supported = typeof navigator !== 'undefined' && 'serviceWorker' in navigator;\n const dev = import.meta.env.DEV;\n const statusAtom = Atom.make<AppUpdate.Status>(\n !supported ? { kind: 'unsupported' } : dev ? { kind: 'dev' } : { kind: 'idle' },\n ).pipe(Atom.keepAlive);\n const setStatus = (status: AppUpdate.Status) => atomRegistry.set(statusAtom, status);\n\n let registration: ServiceWorkerRegistration | undefined;\n let resolveRegistration!: (registration: ServiceWorkerRegistration | undefined) => void;\n const whenRegistered = new Promise<ServiceWorkerRegistration | undefined>((resolve) => {\n resolveRegistration = resolve;\n });\n\n const updateSW = registerSW({\n onRegisteredSW: (_swUrl, swRegistration) => {\n registration = swRegistration;\n resolveRegistration(swRegistration);\n },\n onNeedRefresh: () => {\n // The worker is installed and waiting, so the download is already done — this is `ready`, not\n // `available`. See AppUpdate.Manager on why the web has no `install` step.\n setStatus({ kind: 'ready' });\n void invokePromise(LayoutOperation.AddToast, {\n id: `${meta.profile.key}.need-refresh`,\n title: ['need-refresh.label', { ns: meta.profile.key }],\n description: ['need-refresh.description', { ns: meta.profile.key }],\n // Persists until acted on. It used to expire after 4 minutes, which left a user who missed\n // it with no way back to the update; the settings row is that way back now, and a toast\n // that outlives the glance costs nothing next to a client running a stale build for days.\n duration: Infinity,\n actionLabel: ['refresh.label', { ns: meta.profile.key }],\n actionAlt: ['refresh.alt', { ns: meta.profile.key }],\n onAction: () => updateSW(true),\n });\n },\n onOfflineReady: () => {\n void invokePromise(LayoutOperation.AddToast, {\n id: `${meta.profile.key}.offline-ready`,\n title: ['offline-ready.label', { ns: meta.profile.key }],\n closeLabel: ['confirm.label', { ns: meta.profile.key }],\n });\n },\n onRegisterError: (err) => {\n log.error(err);\n setStatus({ kind: 'failed', error: err instanceof Error ? err.message : String(err) });\n resolveRegistration(undefined);\n },\n });\n\n // Precache progress doubles as the download meter for an update in flight. Read here as well as in\n // `UpdateProgress` because the two surfaces are independent: the progress registry is optional, and\n // the settings row must still show a percentage on a host that omits it.\n const handleProgress = (event: MessageEvent) => {\n if (!isPrecacheProgress(event.data) || !event.data.isUpdate) {\n return;\n }\n // Not left to `onNeedRefresh`: workbox stops observing after the first update found by polling.\n setStatus(progressStatus(event.data));\n };\n navigator.serviceWorker?.addEventListener('message', handleProgress);\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => navigator.serviceWorker?.removeEventListener('message', handleProgress)),\n );\n\n const manager = makeUpdateManager({\n status: statusAtom,\n setStatus,\n supported,\n dev,\n registration: whenRegistered,\n container: navigator.serviceWorker,\n skipWaiting: () => updateSW(true),\n reload: () => window.location.reload(),\n });\n\n // The browser only re-fetches the worker script on navigation, but Composer sessions stay open\n // for days, so without polling a deployed update is never noticed and the refresh toast only\n // ever appears on reload. `Effect.repeat` runs the first check straight away, before\n // `onRegisteredSW` has fired — the optional call makes that opening tick a no-op.\n //\n // `update()` rejects in its own right — `InvalidStateError` while a worker is already\n // installing, a `TypeError` when the script fetch fails offline — so the rejection is recovered\n // rather than left to `Effect.promise`, whose defect would kill the daemon fiber and silently\n // end polling for the rest of the session.\n const checkForUpdate = Effect.tryPromise(async () => {\n await registration?.update();\n }).pipe(Effect.catch((error) => Effect.sync(() => log.warn('service worker update check failed', { error }))));\n\n const fiber = yield* checkForUpdate.pipe(Effect.repeat(Schedule.fixed(UPDATE_CHECK_INTERVAL)), Effect.forkDetach);\n\n yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));\n\n return Capability.contribute(AppCapabilities.UpdateManager, manager);\n }),\n);\n\n// Separate from `RegisterPwa` so that an app without the progress registry still registers the\n// service worker — only the meter is lost, not the update flow itself.\nexport const UpdateProgress = Capability.inlineModule(\n 'UpdateProgress',\n { provides: [] },\n Effect.fnUntraced(function* () {\n // Optional, so that the comment above holds: requiring it would fail the whole plugin — and with\n // it the service-worker registration — on a host that omits plugin-progress.\n const registryOption = yield* Capability.getOption(AppCapabilities.ProgressRegistry);\n if (Option.isNone(registryOption)) {\n return [];\n }\n const registry = registryOption.value;\n\n let monitor: AppCapabilities.ProgressMonitor | undefined;\n const handleMessage = (event: MessageEvent) => {\n if (!isPrecacheProgress(event.data)) {\n return;\n }\n\n const { current, total, isUpdate, done } = event.data;\n if (done && !monitor) {\n // Completion for a download this window never saw start — nothing to report.\n return;\n }\n\n monitor ??= registry.register(`${meta.profile.key}.precache`, {\n label: isUpdate ? 'Downloading app update' : 'Preparing offline use',\n total,\n });\n monitor.total(total);\n monitor.set(current);\n if (done) {\n monitor.done();\n // Transient monitor: the download is a one-shot, so drop it rather than leave a completed\n // row in the registry until the next reload.\n monitor.remove();\n monitor = undefined;\n }\n };\n\n navigator.serviceWorker?.addEventListener('message', handleMessage);\n\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n navigator.serviceWorker?.removeEventListener('message', handleMessage);\n monitor?.remove();\n }),\n );\n return [];\n }),\n);\n\nexport const PwaSettings = AppCapability.settings(() => import('../settings.ts'), {\n activatesOn: ActivationEvents.Idle,\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article'],\n});\n\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":"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"}
|
|
@@ -1,43 +1,57 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as progressStatus, t as makeUpdateManager } from "./chunk-update-manager.mjs";
|
|
2
2
|
import * as Duration from "effect/Duration";
|
|
3
3
|
import * as Effect from "effect/Effect";
|
|
4
4
|
import * as Fiber from "effect/Fiber";
|
|
5
5
|
import * as Option from "effect/Option";
|
|
6
6
|
import * as Predicate from "effect/Predicate";
|
|
7
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
7
8
|
import * as Schedule from "effect/Schedule";
|
|
8
9
|
import { registerSW } from "virtual:pwa-register";
|
|
10
|
+
import * as ActivationEvents from "@dxos/app-framework/ActivationEvents";
|
|
9
11
|
import * as Capabilities from "@dxos/app-framework/Capabilities";
|
|
10
12
|
import * as Capability from "@dxos/app-framework/Capability";
|
|
11
13
|
import * as AppCapabilities from "@dxos/app-toolkit/AppCapabilities";
|
|
12
14
|
import * as AppCapability from "@dxos/app-toolkit/AppCapability";
|
|
13
15
|
import * as LayoutOperation from "@dxos/app-toolkit/LayoutOperation";
|
|
14
16
|
import { log } from "@dxos/log";
|
|
17
|
+
import { meta } from "#meta";
|
|
15
18
|
import { translations } from "#translations";
|
|
16
|
-
//#region src/capabilities/
|
|
17
|
-
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-pwa/src/capabilities/
|
|
18
|
-
var UPDATE_CHECK_INTERVAL = Duration.
|
|
19
|
+
//#region src/capabilities/gen/node.ts
|
|
20
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-pwa/src/capabilities/gen/node.ts";
|
|
21
|
+
var UPDATE_CHECK_INTERVAL = Duration.minutes(15);
|
|
22
|
+
var isPrecacheProgress = (data) => Predicate.isObject(data) && data.type === PRECACHE_PROGRESS;
|
|
19
23
|
/**
|
|
20
24
|
* Progress envelope posted by the host's service worker while it precaches a build. The contract is
|
|
21
25
|
* duplicated in `composer-app/src/sw.ts` so the worker bundle stays free of host code — keep in sync.
|
|
22
26
|
*/
|
|
23
27
|
var PRECACHE_PROGRESS = "dxos:precache-progress";
|
|
24
|
-
var isPrecacheProgress = (data) => Predicate.isObject(data) && data.type === PRECACHE_PROGRESS;
|
|
25
28
|
var RegisterPwa = Capability.inlineModule("RegisterPwa", {
|
|
26
|
-
requires: [Capabilities.OperationInvoker],
|
|
27
|
-
provides: []
|
|
29
|
+
requires: [Capabilities.OperationInvoker, Capabilities.AtomRegistry],
|
|
30
|
+
provides: [AppCapabilities.UpdateManager]
|
|
28
31
|
}, Effect.fnUntraced(function* () {
|
|
29
32
|
const { invokePromise } = yield* Capabilities.OperationInvoker;
|
|
33
|
+
const atomRegistry = yield* Capabilities.AtomRegistry;
|
|
34
|
+
const supported = typeof navigator !== "undefined" && "serviceWorker" in navigator;
|
|
35
|
+
const dev = false;
|
|
36
|
+
const statusAtom = Atom.make(!supported ? { kind: "unsupported" } : { kind: "idle" }).pipe(Atom.keepAlive);
|
|
37
|
+
const setStatus = (status) => atomRegistry.set(statusAtom, status);
|
|
30
38
|
let registration;
|
|
39
|
+
let resolveRegistration;
|
|
40
|
+
const whenRegistered = new Promise((resolve) => {
|
|
41
|
+
resolveRegistration = resolve;
|
|
42
|
+
});
|
|
31
43
|
const updateSW = registerSW({
|
|
32
44
|
onRegisteredSW: (_swUrl, swRegistration) => {
|
|
33
45
|
registration = swRegistration;
|
|
46
|
+
resolveRegistration(swRegistration);
|
|
34
47
|
},
|
|
35
48
|
onNeedRefresh: () => {
|
|
49
|
+
setStatus({ kind: "ready" });
|
|
36
50
|
invokePromise(LayoutOperation.AddToast, {
|
|
37
51
|
id: `${meta.profile.key}.need-refresh`,
|
|
38
52
|
title: ["need-refresh.label", { ns: meta.profile.key }],
|
|
39
53
|
description: ["need-refresh.description", { ns: meta.profile.key }],
|
|
40
|
-
duration:
|
|
54
|
+
duration: Infinity,
|
|
41
55
|
actionLabel: ["refresh.label", { ns: meta.profile.key }],
|
|
42
56
|
actionAlt: ["refresh.alt", { ns: meta.profile.key }],
|
|
43
57
|
onAction: () => updateSW(true)
|
|
@@ -54,21 +68,42 @@ var RegisterPwa = Capability.inlineModule("RegisterPwa", {
|
|
|
54
68
|
log.error(err, void 0, {
|
|
55
69
|
"~LogMeta": "~LogMeta",
|
|
56
70
|
F: __dxlog_file,
|
|
57
|
-
L:
|
|
71
|
+
L: 104,
|
|
58
72
|
S: this
|
|
59
73
|
});
|
|
74
|
+
setStatus({
|
|
75
|
+
kind: "failed",
|
|
76
|
+
error: err instanceof Error ? err.message : String(err)
|
|
77
|
+
});
|
|
78
|
+
resolveRegistration(void 0);
|
|
60
79
|
}
|
|
61
80
|
});
|
|
81
|
+
const handleProgress = (event) => {
|
|
82
|
+
if (!isPrecacheProgress(event.data) || !event.data.isUpdate) return;
|
|
83
|
+
setStatus(progressStatus(event.data));
|
|
84
|
+
};
|
|
85
|
+
navigator.serviceWorker?.addEventListener("message", handleProgress);
|
|
86
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => navigator.serviceWorker?.removeEventListener("message", handleProgress)));
|
|
87
|
+
const manager = makeUpdateManager({
|
|
88
|
+
status: statusAtom,
|
|
89
|
+
setStatus,
|
|
90
|
+
supported,
|
|
91
|
+
dev,
|
|
92
|
+
registration: whenRegistered,
|
|
93
|
+
container: navigator.serviceWorker,
|
|
94
|
+
skipWaiting: () => updateSW(true),
|
|
95
|
+
reload: () => window.location.reload()
|
|
96
|
+
});
|
|
62
97
|
const fiber = yield* Effect.tryPromise(async () => {
|
|
63
98
|
await registration?.update();
|
|
64
99
|
}).pipe(Effect.catch((error) => Effect.sync(() => log.warn("service worker update check failed", { error }, {
|
|
65
100
|
"~LogMeta": "~LogMeta",
|
|
66
101
|
F: __dxlog_file,
|
|
67
|
-
L:
|
|
102
|
+
L: 147,
|
|
68
103
|
S: this
|
|
69
104
|
})))).pipe(Effect.repeat(Schedule.fixed(UPDATE_CHECK_INTERVAL)), Effect.forkDetach);
|
|
70
105
|
yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));
|
|
71
|
-
return
|
|
106
|
+
return Capability.contribute(AppCapabilities.UpdateManager, manager);
|
|
72
107
|
}));
|
|
73
108
|
var UpdateProgress = Capability.inlineModule("UpdateProgress", { provides: [] }, Effect.fnUntraced(function* () {
|
|
74
109
|
const registryOption = yield* Capability.getOption(AppCapabilities.ProgressRegistry);
|
|
@@ -98,8 +133,10 @@ var UpdateProgress = Capability.inlineModule("UpdateProgress", { provides: [] },
|
|
|
98
133
|
}));
|
|
99
134
|
return [];
|
|
100
135
|
}));
|
|
136
|
+
var PwaSettings = AppCapability.settings(() => import("./chunk-settings.mjs"), { activatesOn: ActivationEvents.Idle });
|
|
101
137
|
var Translations = AppCapability.translations(translations);
|
|
138
|
+
var ReactSurface = void 0;
|
|
102
139
|
//#endregion
|
|
103
|
-
export { RegisterPwa, Translations, UpdateProgress };
|
|
140
|
+
export { PwaSettings, ReactSurface, RegisterPwa, Translations, UpdateProgress };
|
|
104
141
|
|
|
105
|
-
//# sourceMappingURL=capabilities.mjs.map
|
|
142
|
+
//# sourceMappingURL=capabilities.node.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.node.mjs","names":[],"sources":["../../src/capabilities/gen/node.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'node' condition.\n\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport * as Fiber from 'effect/Fiber';\nimport * as Option from 'effect/Option';\nimport * as Predicate from 'effect/Predicate';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Schedule from 'effect/Schedule';\nimport { registerSW } from 'virtual:pwa-register';\n\nimport * as ActivationEvents from '@dxos/app-framework/ActivationEvents';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as AppUpdate from '@dxos/app-toolkit/AppUpdate';\nimport * as LayoutOperation from '@dxos/app-toolkit/LayoutOperation';\nimport { log } from '@dxos/log';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport { makeUpdateManager, progressStatus } from '../update-manager.ts';\n\n// Every 15 minutes rather than hourly. A check is one conditional request for the worker script, and\n// finding an update sooner costs nothing extra: the install happens once per deployed version either\n// way. An hour was long enough to leave a tab a full release behind.\nconst UPDATE_CHECK_INTERVAL = Duration.minutes(15);\n\nconst isPrecacheProgress = (data: unknown): data is PrecacheProgress =>\n Predicate.isObject(data) && data.type === PRECACHE_PROGRESS;\n\n/**\n * Progress envelope posted by the host's service worker while it precaches a build. The contract is\n * duplicated in `composer-app/src/sw.ts` so the worker bundle stays free of host code — keep in sync.\n */\nconst PRECACHE_PROGRESS = 'dxos:precache-progress';\n\ntype PrecacheProgress = {\n type: typeof PRECACHE_PROGRESS;\n current: number;\n total: number;\n isUpdate: boolean;\n done: boolean;\n};\n\nexport const RegisterPwa = Capability.inlineModule(\n 'RegisterPwa',\n { requires: [Capabilities.OperationInvoker, Capabilities.AtomRegistry], provides: [AppCapabilities.UpdateManager] },\n Effect.fnUntraced(function* () {\n const { invokePromise } = yield* Capabilities.OperationInvoker;\n const atomRegistry = yield* Capabilities.AtomRegistry;\n\n // No service workers at all on an insecure origin and in some embedded webviews. Under the vite dev\n // server the plugin still loads, but `virtual:pwa-register` is a stub that never registers.\n const supported = typeof navigator !== 'undefined' && 'serviceWorker' in navigator;\n const dev = import.meta.env.DEV;\n const statusAtom = Atom.make<AppUpdate.Status>(\n !supported ? { kind: 'unsupported' } : dev ? { kind: 'dev' } : { kind: 'idle' },\n ).pipe(Atom.keepAlive);\n const setStatus = (status: AppUpdate.Status) => atomRegistry.set(statusAtom, status);\n\n let registration: ServiceWorkerRegistration | undefined;\n let resolveRegistration!: (registration: ServiceWorkerRegistration | undefined) => void;\n const whenRegistered = new Promise<ServiceWorkerRegistration | undefined>((resolve) => {\n resolveRegistration = resolve;\n });\n\n const updateSW = registerSW({\n onRegisteredSW: (_swUrl, swRegistration) => {\n registration = swRegistration;\n resolveRegistration(swRegistration);\n },\n onNeedRefresh: () => {\n // The worker is installed and waiting, so the download is already done — this is `ready`, not\n // `available`. See AppUpdate.Manager on why the web has no `install` step.\n setStatus({ kind: 'ready' });\n void invokePromise(LayoutOperation.AddToast, {\n id: `${meta.profile.key}.need-refresh`,\n title: ['need-refresh.label', { ns: meta.profile.key }],\n description: ['need-refresh.description', { ns: meta.profile.key }],\n // Persists until acted on. It used to expire after 4 minutes, which left a user who missed\n // it with no way back to the update; the settings row is that way back now, and a toast\n // that outlives the glance costs nothing next to a client running a stale build for days.\n duration: Infinity,\n actionLabel: ['refresh.label', { ns: meta.profile.key }],\n actionAlt: ['refresh.alt', { ns: meta.profile.key }],\n onAction: () => updateSW(true),\n });\n },\n onOfflineReady: () => {\n void invokePromise(LayoutOperation.AddToast, {\n id: `${meta.profile.key}.offline-ready`,\n title: ['offline-ready.label', { ns: meta.profile.key }],\n closeLabel: ['confirm.label', { ns: meta.profile.key }],\n });\n },\n onRegisterError: (err) => {\n log.error(err);\n setStatus({ kind: 'failed', error: err instanceof Error ? err.message : String(err) });\n resolveRegistration(undefined);\n },\n });\n\n // Precache progress doubles as the download meter for an update in flight. Read here as well as in\n // `UpdateProgress` because the two surfaces are independent: the progress registry is optional, and\n // the settings row must still show a percentage on a host that omits it.\n const handleProgress = (event: MessageEvent) => {\n if (!isPrecacheProgress(event.data) || !event.data.isUpdate) {\n return;\n }\n // Not left to `onNeedRefresh`: workbox stops observing after the first update found by polling.\n setStatus(progressStatus(event.data));\n };\n navigator.serviceWorker?.addEventListener('message', handleProgress);\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => navigator.serviceWorker?.removeEventListener('message', handleProgress)),\n );\n\n const manager = makeUpdateManager({\n status: statusAtom,\n setStatus,\n supported,\n dev,\n registration: whenRegistered,\n container: navigator.serviceWorker,\n skipWaiting: () => updateSW(true),\n reload: () => window.location.reload(),\n });\n\n // The browser only re-fetches the worker script on navigation, but Composer sessions stay open\n // for days, so without polling a deployed update is never noticed and the refresh toast only\n // ever appears on reload. `Effect.repeat` runs the first check straight away, before\n // `onRegisteredSW` has fired — the optional call makes that opening tick a no-op.\n //\n // `update()` rejects in its own right — `InvalidStateError` while a worker is already\n // installing, a `TypeError` when the script fetch fails offline — so the rejection is recovered\n // rather than left to `Effect.promise`, whose defect would kill the daemon fiber and silently\n // end polling for the rest of the session.\n const checkForUpdate = Effect.tryPromise(async () => {\n await registration?.update();\n }).pipe(Effect.catch((error) => Effect.sync(() => log.warn('service worker update check failed', { error }))));\n\n const fiber = yield* checkForUpdate.pipe(Effect.repeat(Schedule.fixed(UPDATE_CHECK_INTERVAL)), Effect.forkDetach);\n\n yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));\n\n return Capability.contribute(AppCapabilities.UpdateManager, manager);\n }),\n);\n\n// Separate from `RegisterPwa` so that an app without the progress registry still registers the\n// service worker — only the meter is lost, not the update flow itself.\nexport const UpdateProgress = Capability.inlineModule(\n 'UpdateProgress',\n { provides: [] },\n Effect.fnUntraced(function* () {\n // Optional, so that the comment above holds: requiring it would fail the whole plugin — and with\n // it the service-worker registration — on a host that omits plugin-progress.\n const registryOption = yield* Capability.getOption(AppCapabilities.ProgressRegistry);\n if (Option.isNone(registryOption)) {\n return [];\n }\n const registry = registryOption.value;\n\n let monitor: AppCapabilities.ProgressMonitor | undefined;\n const handleMessage = (event: MessageEvent) => {\n if (!isPrecacheProgress(event.data)) {\n return;\n }\n\n const { current, total, isUpdate, done } = event.data;\n if (done && !monitor) {\n // Completion for a download this window never saw start — nothing to report.\n return;\n }\n\n monitor ??= registry.register(`${meta.profile.key}.precache`, {\n label: isUpdate ? 'Downloading app update' : 'Preparing offline use',\n total,\n });\n monitor.total(total);\n monitor.set(current);\n if (done) {\n monitor.done();\n // Transient monitor: the download is a one-shot, so drop it rather than leave a completed\n // row in the registry until the next reload.\n monitor.remove();\n monitor = undefined;\n }\n };\n\n navigator.serviceWorker?.addEventListener('message', handleMessage);\n\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n navigator.serviceWorker?.removeEventListener('message', handleMessage);\n monitor?.remove();\n }),\n );\n return [];\n }),\n);\n\nexport const PwaSettings = AppCapability.settings(() => import('../settings.ts'), {\n activatesOn: ActivationEvents.Idle,\n});\n\nexport const Translations = AppCapability.translations(translations);\n\nexport const ReactSurface = undefined;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,wBAAwB,SAAS,QAAQ,EAAE;AAEjD,IAAM,sBAAsB,SAC1B,UAAU,SAAS,IAAI,KAAK,KAAK,SAAS;;;;;AAM5C,IAAM,oBAAoB;AAU1B,IAAa,cAAc,WAAW,aACpC,eACA;CAAE,UAAU,CAAC,aAAa,kBAAkB,aAAa,YAAY;CAAG,UAAU,CAAC,gBAAgB,aAAa;AAAE,GAClH,OAAO,WAAW,aAAa;CAC7B,MAAM,EAAE,kBAAkB,OAAO,aAAa;CAC9C,MAAM,eAAe,OAAO,aAAa;CAIzC,MAAM,YAAY,OAAO,cAAc,eAAe,mBAAmB;CACzE,MAAM,MAAA;CACN,MAAM,aAAa,KAAK,KACtB,CAAC,YAAY,EAAE,MAAM,cAAc,IAA4B,EAAE,MAAM,OAAO,CAChF,CAAC,CAAC,KAAK,KAAK,SAAS;CACrB,MAAM,aAAa,WAA6B,aAAa,IAAI,YAAY,MAAM;CAEnF,IAAI;CACJ,IAAI;CACJ,MAAM,iBAAiB,IAAI,SAAgD,YAAY;EACrF,sBAAsB;CACxB,CAAC;CAED,MAAM,WAAW,WAAW;EAC1B,iBAAiB,QAAQ,mBAAmB;GAC1C,eAAe;GACf,oBAAoB,cAAc;EACpC;EACA,qBAAqB;GAGnB,UAAU,EAAE,MAAM,QAAQ,CAAC;GAC3B,cAAmB,gBAAgB,UAAU;IAC3C,IAAI,GAAG,KAAK,QAAQ,IAAI;IACxB,OAAO,CAAC,sBAAsB,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;IACtD,aAAa,CAAC,4BAA4B,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;IAIlE,UAAU;IACV,aAAa,CAAC,iBAAiB,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;IACvD,WAAW,CAAC,eAAe,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;IACnD,gBAAgB,SAAS,IAAI;GAC/B,CAAC;EACH;EACA,sBAAsB;GACpB,cAAmB,gBAAgB,UAAU;IAC3C,IAAI,GAAG,KAAK,QAAQ,IAAI;IACxB,OAAO,CAAC,uBAAuB,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;IACvD,YAAY,CAAC,iBAAiB,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;GACxD,CAAC;EACH;EACA,kBAAkB,QAAQ;GACxB,IAAI,MAAM,KAAE,KAAA,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA;GAAA,CAAC;GACb,UAAU;IAAE,MAAM;IAAU,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GAAE,CAAC;GACrF,oBAAoB,KAAA,CAAS;EAC/B;CACF,CAAC;CAKD,MAAM,kBAAkB,UAAwB;EAC9C,IAAI,CAAC,mBAAmB,MAAM,IAAI,KAAK,CAAC,MAAM,KAAK,UACjD;EAGF,UAAU,eAAe,MAAM,IAAI,CAAC;CACtC;CACA,UAAU,eAAe,iBAAiB,WAAW,cAAc;CACnE,OAAO,OAAO,mBACZ,OAAO,WAAW,UAAU,eAAe,oBAAoB,WAAW,cAAc,CAAC,CAC3F;CAEA,MAAM,UAAU,kBAAkB;EAChC,QAAQ;EACR;EACA;EACA;EACA,cAAc;EACd,WAAW,UAAU;EACrB,mBAAmB,SAAS,IAAI;EAChC,cAAc,OAAO,SAAS,OAAO;CACvC,CAAC;CAeD,MAAM,QAAQ,OAJS,OAAO,WAAW,YAAY;EACnD,MAAM,cAAc,OAAO;CAC7B,CAAC,CAAC,CAAC,KAAK,OAAO,OAAO,UAAU,OAAO,WAAW,IAAI,KAAK,sCAAsC,EAAE,MAAM,GAAA;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA;CAAA,CAAC,CAAC,CAAC,CAEvF,CAAA,CAAe,KAAK,OAAO,OAAO,SAAS,MAAM,qBAAqB,CAAC,GAAG,OAAO,UAAU;CAEhH,OAAO,OAAO,mBAAmB,MAAM,UAAU,KAAK,CAAC;CAEvD,OAAO,WAAW,WAAW,gBAAgB,eAAe,OAAO;AACrE,CAAC,CACH;AAIA,IAAa,iBAAiB,WAAW,aACvC,kBACA,EAAE,UAAU,CAAC,EAAE,GACf,OAAO,WAAW,aAAa;CAG7B,MAAM,iBAAiB,OAAO,WAAW,UAAU,gBAAgB,gBAAgB;CACnF,IAAI,OAAO,OAAO,cAAc,GAC9B,OAAO,CAAC;CAEV,MAAM,WAAW,eAAe;CAEhC,IAAI;CACJ,MAAM,iBAAiB,UAAwB;EAC7C,IAAI,CAAC,mBAAmB,MAAM,IAAI,GAChC;EAGF,MAAM,EAAE,SAAS,OAAO,UAAU,SAAS,MAAM;EACjD,IAAI,QAAQ,CAAC,SAEX;EAGF,YAAY,SAAS,SAAS,GAAG,KAAK,QAAQ,IAAI,YAAY;GAC5D,OAAO,WAAW,2BAA2B;GAC7C;EACF,CAAC;EACD,QAAQ,MAAM,KAAK;EACnB,QAAQ,IAAI,OAAO;EACnB,IAAI,MAAM;GACR,QAAQ,KAAK;GAGb,QAAQ,OAAO;GACf,UAAU,KAAA;EACZ;CACF;CAEA,UAAU,eAAe,iBAAiB,WAAW,aAAa;CAElE,OAAO,OAAO,mBACZ,OAAO,WAAW;EAChB,UAAU,eAAe,oBAAoB,WAAW,aAAa;EACrE,SAAS,OAAO;CAClB,CAAC,CACH;CACA,OAAO,CAAC;AACV,CAAC,CACH;AAEA,IAAa,cAAc,cAAc,eAAe,OAAO,yBAAmB,EAChF,aAAa,iBAAiB,KAChC,CAAC;AAED,IAAa,eAAe,cAAc,aAAa,YAAY;AAEnE,IAAa,eAAe,KAAA"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { n as progressStatus, t as makeUpdateManager } from "./chunk-update-manager.mjs";
|
|
2
|
+
import * as Duration from "effect/Duration";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Fiber from "effect/Fiber";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
import * as Predicate from "effect/Predicate";
|
|
7
|
+
import * as Atom from "effect/reactivity/Atom";
|
|
8
|
+
import * as Schedule from "effect/Schedule";
|
|
9
|
+
import { registerSW } from "virtual:pwa-register";
|
|
10
|
+
import * as ActivationEvents from "@dxos/app-framework/ActivationEvents";
|
|
11
|
+
import * as Capabilities from "@dxos/app-framework/Capabilities";
|
|
12
|
+
import * as Capability from "@dxos/app-framework/Capability";
|
|
13
|
+
import * as AppCapabilities from "@dxos/app-toolkit/AppCapabilities";
|
|
14
|
+
import * as AppCapability from "@dxos/app-toolkit/AppCapability";
|
|
15
|
+
import * as LayoutOperation from "@dxos/app-toolkit/LayoutOperation";
|
|
16
|
+
import { log } from "@dxos/log";
|
|
17
|
+
import { meta } from "#meta";
|
|
18
|
+
import { translations } from "#translations";
|
|
19
|
+
//#region src/capabilities/gen/tauri.ts
|
|
20
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-pwa/src/capabilities/gen/tauri.ts";
|
|
21
|
+
var UPDATE_CHECK_INTERVAL = Duration.minutes(15);
|
|
22
|
+
var isPrecacheProgress = (data) => Predicate.isObject(data) && data.type === PRECACHE_PROGRESS;
|
|
23
|
+
/**
|
|
24
|
+
* Progress envelope posted by the host's service worker while it precaches a build. The contract is
|
|
25
|
+
* duplicated in `composer-app/src/sw.ts` so the worker bundle stays free of host code — keep in sync.
|
|
26
|
+
*/
|
|
27
|
+
var PRECACHE_PROGRESS = "dxos:precache-progress";
|
|
28
|
+
var RegisterPwa = Capability.inlineModule("RegisterPwa", {
|
|
29
|
+
requires: [Capabilities.OperationInvoker, Capabilities.AtomRegistry],
|
|
30
|
+
provides: [AppCapabilities.UpdateManager]
|
|
31
|
+
}, Effect.fnUntraced(function* () {
|
|
32
|
+
const { invokePromise } = yield* Capabilities.OperationInvoker;
|
|
33
|
+
const atomRegistry = yield* Capabilities.AtomRegistry;
|
|
34
|
+
const supported = typeof navigator !== "undefined" && "serviceWorker" in navigator;
|
|
35
|
+
const dev = false;
|
|
36
|
+
const statusAtom = Atom.make(!supported ? { kind: "unsupported" } : { kind: "idle" }).pipe(Atom.keepAlive);
|
|
37
|
+
const setStatus = (status) => atomRegistry.set(statusAtom, status);
|
|
38
|
+
let registration;
|
|
39
|
+
let resolveRegistration;
|
|
40
|
+
const whenRegistered = new Promise((resolve) => {
|
|
41
|
+
resolveRegistration = resolve;
|
|
42
|
+
});
|
|
43
|
+
const updateSW = registerSW({
|
|
44
|
+
onRegisteredSW: (_swUrl, swRegistration) => {
|
|
45
|
+
registration = swRegistration;
|
|
46
|
+
resolveRegistration(swRegistration);
|
|
47
|
+
},
|
|
48
|
+
onNeedRefresh: () => {
|
|
49
|
+
setStatus({ kind: "ready" });
|
|
50
|
+
invokePromise(LayoutOperation.AddToast, {
|
|
51
|
+
id: `${meta.profile.key}.need-refresh`,
|
|
52
|
+
title: ["need-refresh.label", { ns: meta.profile.key }],
|
|
53
|
+
description: ["need-refresh.description", { ns: meta.profile.key }],
|
|
54
|
+
duration: Infinity,
|
|
55
|
+
actionLabel: ["refresh.label", { ns: meta.profile.key }],
|
|
56
|
+
actionAlt: ["refresh.alt", { ns: meta.profile.key }],
|
|
57
|
+
onAction: () => updateSW(true)
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
onOfflineReady: () => {
|
|
61
|
+
invokePromise(LayoutOperation.AddToast, {
|
|
62
|
+
id: `${meta.profile.key}.offline-ready`,
|
|
63
|
+
title: ["offline-ready.label", { ns: meta.profile.key }],
|
|
64
|
+
closeLabel: ["confirm.label", { ns: meta.profile.key }]
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
onRegisterError: (err) => {
|
|
68
|
+
log.error(err, void 0, {
|
|
69
|
+
"~LogMeta": "~LogMeta",
|
|
70
|
+
F: __dxlog_file,
|
|
71
|
+
L: 104,
|
|
72
|
+
S: this
|
|
73
|
+
});
|
|
74
|
+
setStatus({
|
|
75
|
+
kind: "failed",
|
|
76
|
+
error: err instanceof Error ? err.message : String(err)
|
|
77
|
+
});
|
|
78
|
+
resolveRegistration(void 0);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
const handleProgress = (event) => {
|
|
82
|
+
if (!isPrecacheProgress(event.data) || !event.data.isUpdate) return;
|
|
83
|
+
setStatus(progressStatus(event.data));
|
|
84
|
+
};
|
|
85
|
+
navigator.serviceWorker?.addEventListener("message", handleProgress);
|
|
86
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => navigator.serviceWorker?.removeEventListener("message", handleProgress)));
|
|
87
|
+
const manager = makeUpdateManager({
|
|
88
|
+
status: statusAtom,
|
|
89
|
+
setStatus,
|
|
90
|
+
supported,
|
|
91
|
+
dev,
|
|
92
|
+
registration: whenRegistered,
|
|
93
|
+
container: navigator.serviceWorker,
|
|
94
|
+
skipWaiting: () => updateSW(true),
|
|
95
|
+
reload: () => window.location.reload()
|
|
96
|
+
});
|
|
97
|
+
const fiber = yield* Effect.tryPromise(async () => {
|
|
98
|
+
await registration?.update();
|
|
99
|
+
}).pipe(Effect.catch((error) => Effect.sync(() => log.warn("service worker update check failed", { error }, {
|
|
100
|
+
"~LogMeta": "~LogMeta",
|
|
101
|
+
F: __dxlog_file,
|
|
102
|
+
L: 147,
|
|
103
|
+
S: this
|
|
104
|
+
})))).pipe(Effect.repeat(Schedule.fixed(UPDATE_CHECK_INTERVAL)), Effect.forkDetach);
|
|
105
|
+
yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));
|
|
106
|
+
return Capability.contribute(AppCapabilities.UpdateManager, manager);
|
|
107
|
+
}));
|
|
108
|
+
var UpdateProgress = Capability.inlineModule("UpdateProgress", { provides: [] }, Effect.fnUntraced(function* () {
|
|
109
|
+
const registryOption = yield* Capability.getOption(AppCapabilities.ProgressRegistry);
|
|
110
|
+
if (Option.isNone(registryOption)) return [];
|
|
111
|
+
const registry = registryOption.value;
|
|
112
|
+
let monitor;
|
|
113
|
+
const handleMessage = (event) => {
|
|
114
|
+
if (!isPrecacheProgress(event.data)) return;
|
|
115
|
+
const { current, total, isUpdate, done } = event.data;
|
|
116
|
+
if (done && !monitor) return;
|
|
117
|
+
monitor ??= registry.register(`${meta.profile.key}.precache`, {
|
|
118
|
+
label: isUpdate ? "Downloading app update" : "Preparing offline use",
|
|
119
|
+
total
|
|
120
|
+
});
|
|
121
|
+
monitor.total(total);
|
|
122
|
+
monitor.set(current);
|
|
123
|
+
if (done) {
|
|
124
|
+
monitor.done();
|
|
125
|
+
monitor.remove();
|
|
126
|
+
monitor = void 0;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
navigator.serviceWorker?.addEventListener("message", handleMessage);
|
|
130
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => {
|
|
131
|
+
navigator.serviceWorker?.removeEventListener("message", handleMessage);
|
|
132
|
+
monitor?.remove();
|
|
133
|
+
}));
|
|
134
|
+
return [];
|
|
135
|
+
}));
|
|
136
|
+
var PwaSettings = AppCapability.settings(() => import("./chunk-settings.mjs"), { activatesOn: ActivationEvents.Idle });
|
|
137
|
+
var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article"] });
|
|
138
|
+
var Translations = AppCapability.translations(translations);
|
|
139
|
+
//#endregion
|
|
140
|
+
export { PwaSettings, ReactSurface, RegisterPwa, Translations, UpdateProgress };
|
|
141
|
+
|
|
142
|
+
//# sourceMappingURL=capabilities.tauri.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.tauri.mjs","names":[],"sources":["../../src/capabilities/gen/tauri.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// GENERATED by dx-plugin — do not edit. Sliced from ../index.ts for the 'tauri' condition.\n\nimport * as Duration from 'effect/Duration';\nimport * as Effect from 'effect/Effect';\nimport * as Fiber from 'effect/Fiber';\nimport * as Option from 'effect/Option';\nimport * as Predicate from 'effect/Predicate';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as Schedule from 'effect/Schedule';\nimport { registerSW } from 'virtual:pwa-register';\n\nimport * as ActivationEvents from '@dxos/app-framework/ActivationEvents';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as AppUpdate from '@dxos/app-toolkit/AppUpdate';\nimport * as LayoutOperation from '@dxos/app-toolkit/LayoutOperation';\nimport { log } from '@dxos/log';\n\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\nimport { makeUpdateManager, progressStatus } from '../update-manager.ts';\n\n// Every 15 minutes rather than hourly. A check is one conditional request for the worker script, and\n// finding an update sooner costs nothing extra: the install happens once per deployed version either\n// way. An hour was long enough to leave a tab a full release behind.\nconst UPDATE_CHECK_INTERVAL = Duration.minutes(15);\n\nconst isPrecacheProgress = (data: unknown): data is PrecacheProgress =>\n Predicate.isObject(data) && data.type === PRECACHE_PROGRESS;\n\n/**\n * Progress envelope posted by the host's service worker while it precaches a build. The contract is\n * duplicated in `composer-app/src/sw.ts` so the worker bundle stays free of host code — keep in sync.\n */\nconst PRECACHE_PROGRESS = 'dxos:precache-progress';\n\ntype PrecacheProgress = {\n type: typeof PRECACHE_PROGRESS;\n current: number;\n total: number;\n isUpdate: boolean;\n done: boolean;\n};\n\nexport const RegisterPwa = Capability.inlineModule(\n 'RegisterPwa',\n { requires: [Capabilities.OperationInvoker, Capabilities.AtomRegistry], provides: [AppCapabilities.UpdateManager] },\n Effect.fnUntraced(function* () {\n const { invokePromise } = yield* Capabilities.OperationInvoker;\n const atomRegistry = yield* Capabilities.AtomRegistry;\n\n // No service workers at all on an insecure origin and in some embedded webviews. Under the vite dev\n // server the plugin still loads, but `virtual:pwa-register` is a stub that never registers.\n const supported = typeof navigator !== 'undefined' && 'serviceWorker' in navigator;\n const dev = import.meta.env.DEV;\n const statusAtom = Atom.make<AppUpdate.Status>(\n !supported ? { kind: 'unsupported' } : dev ? { kind: 'dev' } : { kind: 'idle' },\n ).pipe(Atom.keepAlive);\n const setStatus = (status: AppUpdate.Status) => atomRegistry.set(statusAtom, status);\n\n let registration: ServiceWorkerRegistration | undefined;\n let resolveRegistration!: (registration: ServiceWorkerRegistration | undefined) => void;\n const whenRegistered = new Promise<ServiceWorkerRegistration | undefined>((resolve) => {\n resolveRegistration = resolve;\n });\n\n const updateSW = registerSW({\n onRegisteredSW: (_swUrl, swRegistration) => {\n registration = swRegistration;\n resolveRegistration(swRegistration);\n },\n onNeedRefresh: () => {\n // The worker is installed and waiting, so the download is already done — this is `ready`, not\n // `available`. See AppUpdate.Manager on why the web has no `install` step.\n setStatus({ kind: 'ready' });\n void invokePromise(LayoutOperation.AddToast, {\n id: `${meta.profile.key}.need-refresh`,\n title: ['need-refresh.label', { ns: meta.profile.key }],\n description: ['need-refresh.description', { ns: meta.profile.key }],\n // Persists until acted on. It used to expire after 4 minutes, which left a user who missed\n // it with no way back to the update; the settings row is that way back now, and a toast\n // that outlives the glance costs nothing next to a client running a stale build for days.\n duration: Infinity,\n actionLabel: ['refresh.label', { ns: meta.profile.key }],\n actionAlt: ['refresh.alt', { ns: meta.profile.key }],\n onAction: () => updateSW(true),\n });\n },\n onOfflineReady: () => {\n void invokePromise(LayoutOperation.AddToast, {\n id: `${meta.profile.key}.offline-ready`,\n title: ['offline-ready.label', { ns: meta.profile.key }],\n closeLabel: ['confirm.label', { ns: meta.profile.key }],\n });\n },\n onRegisterError: (err) => {\n log.error(err);\n setStatus({ kind: 'failed', error: err instanceof Error ? err.message : String(err) });\n resolveRegistration(undefined);\n },\n });\n\n // Precache progress doubles as the download meter for an update in flight. Read here as well as in\n // `UpdateProgress` because the two surfaces are independent: the progress registry is optional, and\n // the settings row must still show a percentage on a host that omits it.\n const handleProgress = (event: MessageEvent) => {\n if (!isPrecacheProgress(event.data) || !event.data.isUpdate) {\n return;\n }\n // Not left to `onNeedRefresh`: workbox stops observing after the first update found by polling.\n setStatus(progressStatus(event.data));\n };\n navigator.serviceWorker?.addEventListener('message', handleProgress);\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => navigator.serviceWorker?.removeEventListener('message', handleProgress)),\n );\n\n const manager = makeUpdateManager({\n status: statusAtom,\n setStatus,\n supported,\n dev,\n registration: whenRegistered,\n container: navigator.serviceWorker,\n skipWaiting: () => updateSW(true),\n reload: () => window.location.reload(),\n });\n\n // The browser only re-fetches the worker script on navigation, but Composer sessions stay open\n // for days, so without polling a deployed update is never noticed and the refresh toast only\n // ever appears on reload. `Effect.repeat` runs the first check straight away, before\n // `onRegisteredSW` has fired — the optional call makes that opening tick a no-op.\n //\n // `update()` rejects in its own right — `InvalidStateError` while a worker is already\n // installing, a `TypeError` when the script fetch fails offline — so the rejection is recovered\n // rather than left to `Effect.promise`, whose defect would kill the daemon fiber and silently\n // end polling for the rest of the session.\n const checkForUpdate = Effect.tryPromise(async () => {\n await registration?.update();\n }).pipe(Effect.catch((error) => Effect.sync(() => log.warn('service worker update check failed', { error }))));\n\n const fiber = yield* checkForUpdate.pipe(Effect.repeat(Schedule.fixed(UPDATE_CHECK_INTERVAL)), Effect.forkDetach);\n\n yield* Effect.addFinalizer(() => Fiber.interrupt(fiber));\n\n return Capability.contribute(AppCapabilities.UpdateManager, manager);\n }),\n);\n\n// Separate from `RegisterPwa` so that an app without the progress registry still registers the\n// service worker — only the meter is lost, not the update flow itself.\nexport const UpdateProgress = Capability.inlineModule(\n 'UpdateProgress',\n { provides: [] },\n Effect.fnUntraced(function* () {\n // Optional, so that the comment above holds: requiring it would fail the whole plugin — and with\n // it the service-worker registration — on a host that omits plugin-progress.\n const registryOption = yield* Capability.getOption(AppCapabilities.ProgressRegistry);\n if (Option.isNone(registryOption)) {\n return [];\n }\n const registry = registryOption.value;\n\n let monitor: AppCapabilities.ProgressMonitor | undefined;\n const handleMessage = (event: MessageEvent) => {\n if (!isPrecacheProgress(event.data)) {\n return;\n }\n\n const { current, total, isUpdate, done } = event.data;\n if (done && !monitor) {\n // Completion for a download this window never saw start — nothing to report.\n return;\n }\n\n monitor ??= registry.register(`${meta.profile.key}.precache`, {\n label: isUpdate ? 'Downloading app update' : 'Preparing offline use',\n total,\n });\n monitor.total(total);\n monitor.set(current);\n if (done) {\n monitor.done();\n // Transient monitor: the download is a one-shot, so drop it rather than leave a completed\n // row in the registry until the next reload.\n monitor.remove();\n monitor = undefined;\n }\n };\n\n navigator.serviceWorker?.addEventListener('message', handleMessage);\n\n yield* Effect.addFinalizer(() =>\n Effect.sync(() => {\n navigator.serviceWorker?.removeEventListener('message', handleMessage);\n monitor?.remove();\n }),\n );\n return [];\n }),\n);\n\nexport const PwaSettings = AppCapability.settings(() => import('../settings.ts'), {\n activatesOn: ActivationEvents.Idle,\n});\n\nexport const ReactSurface = AppCapability.surface(() => import('../react-surface.ts'), {\n roles: ['org.dxos.role.article'],\n});\n\nexport const Translations = AppCapability.translations(translations);\n"],"mappings":"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"}
|