@excom/service-worker 0.1.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/.rush/temp/chunked-rush-logs/service-worker.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/service-worker.build_docs.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/service-worker.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_docs/all.log +1 -0
- package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_docs/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +3 -0
- package/config/rig.json +5 -0
- package/index.ts +17 -0
- package/package.json +43 -0
- package/rush-logs/service-worker.apply-exports.cache.log +1 -0
- package/rush-logs/service-worker.apply-exports.log +1 -0
- package/rush-logs/service-worker.build_docs.cache.log +1 -0
- package/rush-logs/service-worker.build_docs.log +1 -0
- package/rush-logs/service-worker.build_package-metas.cache.log +1 -0
- package/rush-logs/service-worker.build_package-metas.log +1 -0
- package/service-worker.ts +217 -0
- package/support/custom-elements.json +162 -0
- package/support/demos/simple.html +3 -0
- package/support/dist-docs/service-worker.md +111 -0
- package/support/docs/INTERNAL.md +13 -0
- package/support/docs/README.md +55 -0
- package/support/package-meta.json +104 -0
- package/support/tests/service-worker.test.ts +525 -0
- package/support/tests/simple.view.test.ts +19 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: cd \"$RUSH_PROJECT_FOLDER\" && node ../heft-rig/scripts/apply-exports.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs \n"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"kind":"O","text":"Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs \n"}
|
package/config/rig.json
ADDED
package/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ServiceWorker } from "./service-worker";
|
|
2
|
+
|
|
3
|
+
ServiceWorker.define();
|
|
4
|
+
|
|
5
|
+
export { ServiceWorker };
|
|
6
|
+
|
|
7
|
+
type T_HTMLServiceWorkerElement = typeof ServiceWorker.CustomElement;
|
|
8
|
+
declare global {
|
|
9
|
+
interface HTMLServiceWorkerElement extends T_HTMLServiceWorkerElement {}
|
|
10
|
+
interface Window {
|
|
11
|
+
HTMLServiceWorkerElement: HTMLServiceWorkerElement;
|
|
12
|
+
}
|
|
13
|
+
interface HTMLElementTagNameMap {
|
|
14
|
+
"service-worker": HTMLServiceWorkerElement;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export type { HTMLServiceWorkerElement };
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@excom/service-worker",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "<service-worker> custom element",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=24.13.0"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@excom/neutron": "^0.1.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@excom/heft-rig": "^0.1.0",
|
|
16
|
+
"@excom/kit-utils": "^0.1.0"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"url": "excom-dev/nucleus",
|
|
20
|
+
"directory": "packages/service-worker"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/excom-dev/nucleus/tree/main/packages/service-worker/support/docs/README.md",
|
|
23
|
+
"bugs": "https://github.com/excom-dev/nucleus/issues",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"service-worker",
|
|
26
|
+
"neutron",
|
|
27
|
+
"custom-elements"
|
|
28
|
+
],
|
|
29
|
+
"excom": {
|
|
30
|
+
"packageType": "kit-element"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "node node_modules/@excom/heft-rig/scripts/vite-build.mjs",
|
|
34
|
+
"build:watch": "node node_modules/@excom/heft-rig/scripts/vite-build-watch.mjs",
|
|
35
|
+
"format": "node node_modules/@excom/heft-rig/scripts/format.mjs",
|
|
36
|
+
"test": "node node_modules/@excom/heft-rig/scripts/vitest.mjs",
|
|
37
|
+
"coverage": "node node_modules/@excom/heft-rig/scripts/coverage.mjs",
|
|
38
|
+
"dev": "node node_modules/@excom/heft-rig/scripts/vite-dev.mjs",
|
|
39
|
+
"preview": "node node_modules/@excom/heft-rig/scripts/vite-preview.mjs",
|
|
40
|
+
"build:package-metas": "node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs",
|
|
41
|
+
"build:docs": "node node_modules/@excom/heft-rig/scripts/build-docs.mjs"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Caching has been disabled for this project's "apply-exports" command.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: cd "$RUSH_PROJECT_FOLDER" && node ../heft-rig/scripts/apply-exports.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This project does not define the caching behavior of the "build:docs" command, so caching has been disabled.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-docs.mjs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This project does not define the caching behavior of the "build:package-metas" command, so caching has been disabled.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Invoking: node node_modules/@excom/heft-rig/scripts/build-package-metas.mjs
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConstructorType,
|
|
3
|
+
Neutron,
|
|
4
|
+
TEvent,
|
|
5
|
+
TokenList,
|
|
6
|
+
} from "@excom/neutron";
|
|
7
|
+
|
|
8
|
+
const SW_EVENTS = ["message", "messageerror", "controllerchange"];
|
|
9
|
+
|
|
10
|
+
export type ServiceWorkerMessageEvent = TEvent & {
|
|
11
|
+
type: "message";
|
|
12
|
+
detail: unknown;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type ServiceWorkerMessageErrorEvent = TEvent & {
|
|
16
|
+
type: "messageerror";
|
|
17
|
+
detail: unknown;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ServiceWorkerControllerChangeEvent = TEvent & {
|
|
21
|
+
type: "controllerchange";
|
|
22
|
+
detail: void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** What this element knows about `navigator.serviceWorker`. */
|
|
26
|
+
export interface ServiceWorkerProvision {
|
|
27
|
+
/** The Service Worker API is available. */
|
|
28
|
+
isSupported: boolean;
|
|
29
|
+
/** `navigator.serviceWorker.ready` has resolved. */
|
|
30
|
+
isReady: boolean;
|
|
31
|
+
/** A worker currently controls this page (`navigator.serviceWorker.controller`). */
|
|
32
|
+
hasController: boolean;
|
|
33
|
+
/** The ready registration's scope; `null` until `ready` resolves. */
|
|
34
|
+
scope: string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const hasController = () => !!navigator.serviceWorker?.controller;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Observes an already-registered Service Worker. This element does **not**
|
|
41
|
+
* register a Service Worker itself — your app must call
|
|
42
|
+
* `navigator.serviceWorker.register(...)` separately; this element only
|
|
43
|
+
* reports on `navigator.serviceWorker` and, optionally, relays its events
|
|
44
|
+
* onto itself as plain DOM events for Quark / `<event-handler>` to react
|
|
45
|
+
* to.
|
|
46
|
+
*
|
|
47
|
+
* `.provision` holds `{ isSupported, isReady, hasController, scope }`, kept
|
|
48
|
+
* current on connect, when `ready` resolves, and on every
|
|
49
|
+
* `controllerchange` — whether or not that event is relayed.
|
|
50
|
+
*
|
|
51
|
+
* @summary Observes `navigator.serviceWorker` — does not register a SW.
|
|
52
|
+
*
|
|
53
|
+
* @fires message - Relayed verbatim from `navigator.serviceWorker`'s
|
|
54
|
+
* `message` event when `message` is included in `relay-events`. Not
|
|
55
|
+
* tag-prefixed. `event.detail` is `event.data` from the original
|
|
56
|
+
* message.
|
|
57
|
+
* @type ServiceWorkerMessageEvent
|
|
58
|
+
* @fires messageerror - Relayed verbatim from `navigator.serviceWorker`'s
|
|
59
|
+
* `messageerror` event when `messageerror` is included in
|
|
60
|
+
* `relay-events`. Not tag-prefixed.
|
|
61
|
+
* @type ServiceWorkerMessageErrorEvent
|
|
62
|
+
* @fires controllerchange - Relayed verbatim from
|
|
63
|
+
* `navigator.serviceWorker`'s `controllerchange` event when
|
|
64
|
+
* `controllerchange` is included in `relay-events`. Not tag-prefixed.
|
|
65
|
+
* @type ServiceWorkerControllerChangeEvent
|
|
66
|
+
*/
|
|
67
|
+
export const ServiceWorker = Neutron({
|
|
68
|
+
tag: "service-worker",
|
|
69
|
+
reflectDefaultProps: ["isMounted"],
|
|
70
|
+
props: {
|
|
71
|
+
// options
|
|
72
|
+
/**
|
|
73
|
+
* @option
|
|
74
|
+
* Space-separated `navigator.serviceWorker` events to relay onto this
|
|
75
|
+
* element. Bare attribute (no value) relays all three. Event names
|
|
76
|
+
* are relayed as-is — not prefixed with the tag.
|
|
77
|
+
* @values message | messageerror | controllerchange
|
|
78
|
+
*/
|
|
79
|
+
relayEvents: TokenList,
|
|
80
|
+
// state
|
|
81
|
+
/**
|
|
82
|
+
* @state
|
|
83
|
+
* `navigator.serviceWorker.ready` has resolved — an active worker is
|
|
84
|
+
* controlling the page.
|
|
85
|
+
*/
|
|
86
|
+
isReady: Boolean,
|
|
87
|
+
/**
|
|
88
|
+
* @state
|
|
89
|
+
* The Service Worker API is available (`'serviceWorker' in
|
|
90
|
+
* navigator`).
|
|
91
|
+
*/
|
|
92
|
+
isSupported: Boolean,
|
|
93
|
+
/**
|
|
94
|
+
* @provision
|
|
95
|
+
* `{ isSupported, isReady, hasController, scope }` — set on connect,
|
|
96
|
+
* when `ready` resolves (`scope` comes from the registration), and on
|
|
97
|
+
* every `controllerchange`. Not reflected as an attribute.
|
|
98
|
+
* @type ServiceWorkerProvision
|
|
99
|
+
*/
|
|
100
|
+
provision: Object as unknown as ConstructorType<ServiceWorkerProvision>,
|
|
101
|
+
// private
|
|
102
|
+
swContainer: Object as unknown as typeof ServiceWorkerContainer,
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
.defineMethods({
|
|
106
|
+
messageCallback: (_, event: MessageEvent) => ({
|
|
107
|
+
emit: [event.type, { detail: event.data }],
|
|
108
|
+
}),
|
|
109
|
+
// Method so it reads the element when `ready` settles, and no-ops if
|
|
110
|
+
// the element has since left the document.
|
|
111
|
+
readyCallback: (
|
|
112
|
+
{ isConnected, isSupported, provision },
|
|
113
|
+
registration?: ServiceWorkerRegistration
|
|
114
|
+
) =>
|
|
115
|
+
isConnected && {
|
|
116
|
+
isReady: true,
|
|
117
|
+
provision: {
|
|
118
|
+
isSupported: !!isSupported,
|
|
119
|
+
isReady: true,
|
|
120
|
+
hasController: hasController(),
|
|
121
|
+
scope: registration?.scope ?? provision?.scope ?? null,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
// Internal `controllerchange` listener, independent of `relay-events`
|
|
125
|
+
_handleControllerChange: ({ isSupported, isReady, provision }) => ({
|
|
126
|
+
provision: {
|
|
127
|
+
isSupported: !!isSupported,
|
|
128
|
+
isReady: !!isReady,
|
|
129
|
+
hasController: hasController(),
|
|
130
|
+
scope: provision?.scope ?? null,
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
/**
|
|
134
|
+
* Apply `relay-events` to `navigator.serviceWorker`: drop the
|
|
135
|
+
* `previous` names (or all three when relaying stops) and add the
|
|
136
|
+
* current set. Also on a real reconnect, since `onDisconnected`
|
|
137
|
+
* removes them.
|
|
138
|
+
*/
|
|
139
|
+
// @ts-expect-error - TODO: method typing
|
|
140
|
+
_applyRelay: ({ relayEvents, messageCallback }, previous?: string[]) => {
|
|
141
|
+
if (!navigator.serviceWorker) return;
|
|
142
|
+
previous?.forEach((eventName) =>
|
|
143
|
+
navigator.serviceWorker.removeEventListener(eventName, messageCallback)
|
|
144
|
+
);
|
|
145
|
+
if (!relayEvents) {
|
|
146
|
+
SW_EVENTS.forEach((eventName) =>
|
|
147
|
+
navigator.serviceWorker.removeEventListener(
|
|
148
|
+
eventName,
|
|
149
|
+
messageCallback
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
} else {
|
|
153
|
+
// Add the current set. Bare `relay-events` (no value) means all.
|
|
154
|
+
const events = relayEvents?.length > 0 ? relayEvents : SW_EVENTS;
|
|
155
|
+
events.forEach((eventName) =>
|
|
156
|
+
navigator.serviceWorker.addEventListener(eventName, messageCallback)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
})
|
|
161
|
+
.onConnected(
|
|
162
|
+
({
|
|
163
|
+
readyCallback,
|
|
164
|
+
_handleControllerChange,
|
|
165
|
+
isMoving,
|
|
166
|
+
wasMounted,
|
|
167
|
+
isReady,
|
|
168
|
+
provision,
|
|
169
|
+
}) => {
|
|
170
|
+
if (isMoving) return;
|
|
171
|
+
if ("serviceWorker" in navigator) {
|
|
172
|
+
navigator.serviceWorker.ready.then(readyCallback);
|
|
173
|
+
return [
|
|
174
|
+
{
|
|
175
|
+
isSupported: true,
|
|
176
|
+
swContainer: navigator.serviceWorker,
|
|
177
|
+
// Tracked by Neutron: dropped on disconnect, restored on reconnect
|
|
178
|
+
addListener: [
|
|
179
|
+
"controllerchange",
|
|
180
|
+
_handleControllerChange,
|
|
181
|
+
{ target: navigator.serviceWorker },
|
|
182
|
+
],
|
|
183
|
+
provision: {
|
|
184
|
+
isSupported: true,
|
|
185
|
+
isReady: !!isReady,
|
|
186
|
+
hasController: hasController(),
|
|
187
|
+
scope: provision?.scope ?? null,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
/* Relay listeners are plain `navigator.serviceWorker` listeners,
|
|
191
|
+
removed in `onDisconnected`. Re-apply after a real reconnect
|
|
192
|
+
(first mount applies them via `relayEvents`). */
|
|
193
|
+
...(wasMounted ? [{ _applyRelay: [] }] : []),
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
provision: {
|
|
198
|
+
isSupported: false,
|
|
199
|
+
isReady: false,
|
|
200
|
+
hasController: false,
|
|
201
|
+
scope: null,
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
)
|
|
206
|
+
.onPropChanged("relayEvents", (_, previous) => ({
|
|
207
|
+
_applyRelay: [previous?.relayEvents],
|
|
208
|
+
}))
|
|
209
|
+
.onDisconnected(({ messageCallback, isMoving }) => {
|
|
210
|
+
if (isMoving) return;
|
|
211
|
+
// Drop relay listeners. Neutron removes the tracked `controllerchange`.
|
|
212
|
+
if (navigator.serviceWorker) {
|
|
213
|
+
SW_EVENTS.forEach((eventName) =>
|
|
214
|
+
navigator.serviceWorker.removeEventListener(eventName, messageCallback)
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"modules": [
|
|
4
|
+
{
|
|
5
|
+
"kind": "javascript-module",
|
|
6
|
+
"path": "service-worker.ts",
|
|
7
|
+
"declarations": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "class",
|
|
10
|
+
"name": "ServiceWorker",
|
|
11
|
+
"customElement": true,
|
|
12
|
+
"tagName": "service-worker",
|
|
13
|
+
"summary": "Observes `navigator.serviceWorker` — does not register a SW.",
|
|
14
|
+
"description": "What this element knows about `navigator.serviceWorker`.",
|
|
15
|
+
"attributes": [
|
|
16
|
+
{
|
|
17
|
+
"name": "relay-events",
|
|
18
|
+
"type": {
|
|
19
|
+
"text": "tokenlist"
|
|
20
|
+
},
|
|
21
|
+
"description": "Space-separated `navigator.serviceWorker` events to relay onto this element. Bare attribute (no value) relays all three. Event names are relayed as-is — not prefixed with the tag.",
|
|
22
|
+
"fieldName": "relayEvents",
|
|
23
|
+
"values": [
|
|
24
|
+
"message",
|
|
25
|
+
"messageerror",
|
|
26
|
+
"controllerchange"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "is-ready",
|
|
31
|
+
"type": {
|
|
32
|
+
"text": "boolean"
|
|
33
|
+
},
|
|
34
|
+
"description": "`navigator.serviceWorker.ready` has resolved — an active worker is controlling the page.",
|
|
35
|
+
"fieldName": "isReady"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "is-supported",
|
|
39
|
+
"type": {
|
|
40
|
+
"text": "boolean"
|
|
41
|
+
},
|
|
42
|
+
"description": "The Service Worker API is available (`'serviceWorker' in navigator`).",
|
|
43
|
+
"fieldName": "isSupported"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"members": [
|
|
47
|
+
{
|
|
48
|
+
"kind": "field",
|
|
49
|
+
"name": "relayEvents",
|
|
50
|
+
"type": {
|
|
51
|
+
"text": "tokenlist"
|
|
52
|
+
},
|
|
53
|
+
"privacy": "public",
|
|
54
|
+
"readonly": false,
|
|
55
|
+
"description": "Space-separated `navigator.serviceWorker` events to relay onto this element. Bare attribute (no value) relays all three. Event names are relayed as-is — not prefixed with the tag.",
|
|
56
|
+
"_neutron": {
|
|
57
|
+
"surface": "option"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"kind": "field",
|
|
62
|
+
"name": "isReady",
|
|
63
|
+
"type": {
|
|
64
|
+
"text": "boolean"
|
|
65
|
+
},
|
|
66
|
+
"privacy": "public",
|
|
67
|
+
"readonly": true,
|
|
68
|
+
"description": "`navigator.serviceWorker.ready` has resolved — an active worker is controlling the page.",
|
|
69
|
+
"_neutron": {
|
|
70
|
+
"surface": "state"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"kind": "field",
|
|
75
|
+
"name": "isSupported",
|
|
76
|
+
"type": {
|
|
77
|
+
"text": "boolean"
|
|
78
|
+
},
|
|
79
|
+
"privacy": "public",
|
|
80
|
+
"readonly": true,
|
|
81
|
+
"description": "The Service Worker API is available (`'serviceWorker' in navigator`).",
|
|
82
|
+
"_neutron": {
|
|
83
|
+
"surface": "state"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"kind": "field",
|
|
88
|
+
"name": "provision",
|
|
89
|
+
"type": {
|
|
90
|
+
"text": "ServiceWorkerProvision",
|
|
91
|
+
"expanded": "{ isSupported: boolean; isReady: boolean; hasController: boolean; scope: string | null; }"
|
|
92
|
+
},
|
|
93
|
+
"privacy": "public",
|
|
94
|
+
"readonly": false,
|
|
95
|
+
"description": "`{ isSupported, isReady, hasController, scope }` — set on connect, when `ready` resolves (`scope` comes from the registration), and on every `controllerchange`. Not reflected as an attribute.",
|
|
96
|
+
"_neutron": {
|
|
97
|
+
"surface": "option"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"events": [
|
|
102
|
+
{
|
|
103
|
+
"name": "message",
|
|
104
|
+
"description": "Relayed verbatim from `navigator.serviceWorker`'s `message` event when `message` is included in `relay-events`. Not tag-prefixed. `event.detail` is `event.data` from the original message.",
|
|
105
|
+
"type": {
|
|
106
|
+
"text": "ServiceWorkerMessageEvent",
|
|
107
|
+
"expanded": "CustomEvent & { type: \"message\"; detail: unknown; bubbles: true; cancelable: true; composed: true }"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"name": "messageerror",
|
|
112
|
+
"description": "Relayed verbatim from `navigator.serviceWorker`'s `messageerror` event when `messageerror` is included in `relay-events`. Not tag-prefixed.",
|
|
113
|
+
"type": {
|
|
114
|
+
"text": "ServiceWorkerMessageErrorEvent",
|
|
115
|
+
"expanded": "CustomEvent & { type: \"messageerror\"; detail: unknown; bubbles: true; cancelable: true; composed: true }"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"name": "controllerchange",
|
|
120
|
+
"description": "Relayed verbatim from `navigator.serviceWorker`'s `controllerchange` event when `controllerchange` is included in `relay-events`. Not tag-prefixed.",
|
|
121
|
+
"type": {
|
|
122
|
+
"text": "ServiceWorkerControllerChangeEvent",
|
|
123
|
+
"expanded": "CustomEvent & { type: \"controllerchange\"; detail: void; bubbles: true; cancelable: true; composed: true }"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"_neutron": {
|
|
128
|
+
"provisions": [
|
|
129
|
+
{
|
|
130
|
+
"name": "provision",
|
|
131
|
+
"type": {
|
|
132
|
+
"text": "ServiceWorkerProvision",
|
|
133
|
+
"expanded": "{ isSupported: boolean; isReady: boolean; hasController: boolean; scope: string | null; }"
|
|
134
|
+
},
|
|
135
|
+
"description": "`{ isSupported, isReady, hasController, scope }` — set on connect, when `ready` resolves (`scope` comes from the registration), and on every `controllerchange`. Not reflected as an attribute.",
|
|
136
|
+
"fieldName": "provision"
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
"exports": [
|
|
143
|
+
{
|
|
144
|
+
"kind": "js",
|
|
145
|
+
"name": "ServiceWorker",
|
|
146
|
+
"declaration": {
|
|
147
|
+
"name": "ServiceWorker",
|
|
148
|
+
"module": "service-worker.ts"
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"kind": "custom-element-definition",
|
|
153
|
+
"name": "service-worker",
|
|
154
|
+
"declaration": {
|
|
155
|
+
"name": "ServiceWorker",
|
|
156
|
+
"module": "service-worker.ts"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# service-worker
|
|
2
|
+
|
|
3
|
+
Observes `navigator.serviceWorker` and optionally relays its events — zero app JS required.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
```html
|
|
7
|
+
<div>
|
|
8
|
+
<service-worker></service-worker>
|
|
9
|
+
</div>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Observation only** Reports on an existing Service Worker; never registers one
|
|
16
|
+
- **Support detection** `is-supported` reflects API availability
|
|
17
|
+
- **Ready state** `is-ready` reflects once an active worker controls the page
|
|
18
|
+
- **Event relay** `relay-events` forwards `message` / `messageerror` / `controllerchange` as plain DOM events
|
|
19
|
+
- **Bindable state** `.provision` is `{ isSupported, isReady, hasController, scope }` — kept current on connect, `ready`, and every `controllerchange`; read it from Quark with `prop("provision")`
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
`@excom/service-worker` v0.1.0
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm add @excom/service-worker
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @excom/service-worker
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
yarn add @excom/service-worker
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Import
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import "@excom/service-worker";
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
This element only *observes* an already-registered Service Worker — it does not call `navigator.serviceWorker.register(...)` itself. Register your Service Worker separately (in app code, or your build tool), then drop this element anywhere to expose its state as attributes and, optionally, relay its events.
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<service-worker relay-events></service-worker>
|
|
52
|
+
<event-handler listen-for="message" fire-event="sw-message-received">
|
|
53
|
+
...
|
|
54
|
+
</event-handler>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Relayed events (`message`, `messageerror`, `controllerchange`) are dispatched with their original names — they are **not** prefixed with `service-worker-`.
|
|
58
|
+
|
|
59
|
+
### API Reference
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
#### Attributes
|
|
63
|
+
|
|
64
|
+
| Name | Surface | Type | Default | Values | Description |
|
|
65
|
+
| --- | --- | --- | --- | --- | --- |
|
|
66
|
+
| `relay-events` | option | `tokenlist` | | `"message"` \| `"messageerror"` \| `"controllerchange"` | Space-separated `navigator.serviceWorker` events to relay onto this element. Bare attribute (no value) relays all three. Event names are relayed as-is — not prefixed with the tag. |
|
|
67
|
+
| `is-ready` | state | `boolean` | | | `navigator.serviceWorker.ready` has resolved — an active worker is controlling the page. |
|
|
68
|
+
| `is-supported` | state | `boolean` | | | The Service Worker API is available (`'serviceWorker' in navigator`). |
|
|
69
|
+
|
|
70
|
+
#### Provision
|
|
71
|
+
|
|
72
|
+
| Name | Type | Description |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `provision` | `ServiceWorkerProvision` (`{ isSupported: boolean; isReady: boolean; hasController: boolean; scope: string \| null; }`) | `{ isSupported, isReady, hasController, scope }` — set on connect, when `ready` resolves (`scope` comes from the registration), and on every `controllerchange`. Not reflected as an attribute. |
|
|
75
|
+
|
|
76
|
+
#### Fires
|
|
77
|
+
|
|
78
|
+
| Name | Type | Description |
|
|
79
|
+
| --- | --- | --- |
|
|
80
|
+
| `message` | `ServiceWorkerMessageEvent` (`CustomEvent & { type: "message"; detail: unknown; bubbles: true; cancelable: true; composed: true }`) | Relayed verbatim from `navigator.serviceWorker`'s `message` event when `message` is included in `relay-events`. Not tag-prefixed. `event.detail` is `event.data` from the original message. |
|
|
81
|
+
| `messageerror` | `ServiceWorkerMessageErrorEvent` (`CustomEvent & { type: "messageerror"; detail: unknown; bubbles: true; cancelable: true; composed: true }`) | Relayed verbatim from `navigator.serviceWorker`'s `messageerror` event when `messageerror` is included in `relay-events`. Not tag-prefixed. |
|
|
82
|
+
| `controllerchange` | `ServiceWorkerControllerChangeEvent` (`CustomEvent & { type: "controllerchange"; detail: void; bubbles: true; cancelable: true; composed: true }`) | Relayed verbatim from `navigator.serviceWorker`'s `controllerchange` event when `controllerchange` is included in `relay-events`. Not tag-prefixed. |
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
### Examples
|
|
87
|
+
|
|
88
|
+
#### Support / mount / ready state
|
|
89
|
+
|
|
90
|
+
`is-supported`, `is-mounted` (reflected by default), and `is-ready` are all plain attributes — style or branch on them with CSS. `is-ready` needs an app-registered Service Worker to ever resolve, so it will likely stay unset in this docs site.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<div>
|
|
95
|
+
<service-worker></service-worker>
|
|
96
|
+
</div>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
#### Relay messages from your Service Worker
|
|
101
|
+
|
|
102
|
+
Relaying `message` / `messageerror` / `controllerchange` requires a Service Worker that your app has already registered and that is actively posting messages — this is not runnable in this docs site, but works like so once wired up:
|
|
103
|
+
|
|
104
|
+
```html
|
|
105
|
+
<service-worker relay-events="message"></service-worker>
|
|
106
|
+
<script>
|
|
107
|
+
document
|
|
108
|
+
.querySelector("service-worker")
|
|
109
|
+
.addEventListener("message", (e) => console.log(e.detail));
|
|
110
|
+
</script>
|
|
111
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Internal notes
|
|
2
|
+
|
|
3
|
+
## TODO: re-listen on reconnect
|
|
4
|
+
|
|
5
|
+
`onDisconnected` removes the `navigator.serviceWorker` listeners added for
|
|
6
|
+
`relay-events`, but if the element is later reconnected (moved in the DOM
|
|
7
|
+
counts as a disconnect + connect unless `isMoving` is set), `onConnected`
|
|
8
|
+
never re-attaches them — only `onPropChanged("relayEvents", ...)` does, and
|
|
9
|
+
that only fires on an actual attribute change, not on reconnect.
|
|
10
|
+
|
|
11
|
+
Fix: either re-run the `relayEvents` listener setup from `onConnected` (skip
|
|
12
|
+
when `wasMounted` is true, mirroring the `swContainer` re-detection guard),
|
|
13
|
+
or track listener state independently of the prop-change handler.
|