@chelone/core 0.6.0 → 0.7.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/assistant/assistant-session.d.ts +6 -0
- package/dist/assistant/assistant-session.js +204 -181
- package/dist/assistant/echo-transport.js +26 -29
- package/dist/assistant/http-transport.d.ts +10 -0
- package/dist/assistant/http-transport.js +98 -0
- package/dist/assistant/types.d.ts +1 -7
- package/dist/auth/access-control.d.ts +13 -5
- package/dist/auth/access-control.js +25 -21
- package/dist/auth/auth-options.d.ts +21 -17
- package/dist/auth/auth-options.js +80 -48
- package/dist/auth/permission-matrix.d.ts +12 -0
- package/dist/auth/permission-matrix.js +67 -0
- package/dist/auth/types.d.ts +14 -4
- package/dist/block-editor/block-document.d.ts +60 -0
- package/dist/block-editor/block-document.js +143 -0
- package/dist/block-editor/data-blocks.d.ts +63 -0
- package/dist/block-editor/data-blocks.js +117 -0
- package/dist/chart/chart-data.js +46 -44
- package/dist/command-menu/hotkey.js +58 -47
- package/dist/command-menu/recents.js +8 -11
- package/dist/command-menu/registry.d.ts +9 -5
- package/dist/command-menu/registry.js +36 -46
- package/dist/controller/calendar-dates.js +82 -113
- package/dist/controller/calendar-events.js +93 -88
- package/dist/controller/client-list-controller.d.ts +2 -2
- package/dist/controller/client-list-controller.js +251 -254
- package/dist/controller/data-table-columns.d.ts +2 -0
- package/dist/controller/data-table-columns.js +50 -56
- package/dist/controller/deep-equal.d.ts +1 -0
- package/dist/controller/deep-equal.js +14 -0
- package/dist/controller/filter-toggle.js +36 -44
- package/dist/controller/in-place-editor.d.ts +42 -0
- package/dist/controller/in-place-editor.js +42 -0
- package/dist/controller/infinite-list-controller.js +219 -189
- package/dist/controller/inspector.d.ts +49 -0
- package/dist/controller/inspector.js +64 -0
- package/dist/controller/job-monitor.d.ts +3 -4
- package/dist/controller/job-monitor.js +37 -47
- package/dist/controller/kanban-board.d.ts +2 -3
- package/dist/controller/kanban-board.js +43 -24
- package/dist/controller/list-controller.d.ts +5 -5
- package/dist/controller/list-controller.js +444 -400
- package/dist/controller/list-params.js +96 -87
- package/dist/controller/pivot-grid.js +89 -95
- package/dist/controller/prev-next.d.ts +2 -2
- package/dist/controller/prev-next.js +74 -54
- package/dist/controller/record-controllers.d.ts +14 -6
- package/dist/controller/record-controllers.js +288 -257
- package/dist/controller/record-diff.d.ts +21 -0
- package/dist/controller/record-diff.js +50 -0
- package/dist/controller/record-timeline.js +42 -30
- package/dist/controller/record-tree.js +57 -47
- package/dist/controller/record-workflow.d.ts +38 -16
- package/dist/controller/record-workflow.js +85 -64
- package/dist/controller/reference-many-controller.js +198 -192
- package/dist/controller/reference-one-controller.d.ts +1 -1
- package/dist/controller/reference-one-controller.js +63 -56
- package/dist/controller/saved-queries.js +104 -91
- package/dist/controller/translate-fallback.js +10 -6
- package/dist/csv/export.d.ts +2 -1
- package/dist/csv/export.js +43 -40
- package/dist/csv/import-controller.js +185 -169
- package/dist/csv/match-headers.js +29 -22
- package/dist/csv/parse-with.js +60 -54
- package/dist/csv/run-import.js +69 -72
- package/dist/csv/validate-rows.d.ts +1 -1
- package/dist/csv/validate-rows.js +65 -52
- package/dist/data/cache-ops.d.ts +5 -4
- package/dist/data/cache-ops.js +208 -188
- package/dist/data/combine-data-providers.js +25 -23
- package/dist/data/data-provider-log.d.ts +45 -0
- package/dist/data/data-provider-log.js +93 -0
- package/dist/data/errors.js +14 -10
- package/dist/data/fake-provider.d.ts +6 -3
- package/dist/data/fake-provider.js +129 -114
- package/dist/data/fetch-utils.js +61 -44
- package/dist/data/filter-branch.d.ts +5 -0
- package/dist/data/filter-branch.js +10 -0
- package/dist/data/get-many-aggregate.js +84 -76
- package/dist/data/http-error.js +17 -12
- package/dist/data/keys.js +50 -36
- package/dist/data/mutation-options.d.ts +14 -9
- package/dist/data/mutation-options.js +93 -109
- package/dist/data/query-options.js +73 -68
- package/dist/data/remove-empty.d.ts +3 -0
- package/dist/data/remove-empty.js +11 -0
- package/dist/data/undoable.d.ts +2 -2
- package/dist/data/undoable.js +190 -174
- package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
- package/dist/data/with-lifecycle-callbacks.js +66 -52
- package/dist/environment/application-update-store.js +58 -55
- package/dist/environment/offline-store.js +21 -21
- package/dist/form/array-binding.d.ts +30 -11
- package/dist/form/array-binding.js +184 -166
- package/dist/form/choice-ids.d.ts +7 -0
- package/dist/form/choice-ids.js +11 -0
- package/dist/form/choices-controller.d.ts +3 -2
- package/dist/form/choices-controller.js +302 -275
- package/dist/form/date-format.d.ts +38 -0
- package/dist/form/date-format.js +89 -0
- package/dist/form/dirty-values.d.ts +2 -2
- package/dist/form/dirty-values.js +22 -11
- package/dist/form/duration.d.ts +22 -0
- package/dist/form/duration.js +49 -0
- package/dist/form/edit-create-form.d.ts +12 -5
- package/dist/form/edit-create-form.js +243 -188
- package/dist/form/event-value.d.ts +6 -0
- package/dist/form/event-value.js +17 -0
- package/dist/form/field-error.d.ts +14 -0
- package/dist/form/field-error.js +28 -0
- package/dist/form/field-name.d.ts +20 -0
- package/dist/form/field-name.js +6 -0
- package/dist/form/filter-form-values.js +63 -49
- package/dist/form/form-controller.d.ts +9 -1
- package/dist/form/form-controller.js +165 -160
- package/dist/form/form-groups.d.ts +4 -3
- package/dist/form/form-groups.js +79 -79
- package/dist/form/form-handle.d.ts +175 -0
- package/dist/form/form-handle.js +68 -0
- package/dist/form/input-binding.d.ts +56 -31
- package/dist/form/input-binding.js +169 -118
- package/dist/form/input-values.d.ts +130 -0
- package/dist/form/input-values.js +177 -0
- package/dist/form/json-text.js +51 -37
- package/dist/form/save-pipeline.d.ts +6 -8
- package/dist/form/save-pipeline.js +91 -82
- package/dist/form/set-form-values.d.ts +6 -0
- package/dist/form/set-form-values.js +10 -0
- package/dist/form/source.js +97 -65
- package/dist/form/text-of.d.ts +2 -0
- package/dist/form/text-of.js +7 -0
- package/dist/form/validators.d.ts +58 -19
- package/dist/form/validators.js +97 -71
- package/dist/geo/bbox.d.ts +5 -4
- package/dist/geo/bbox.js +69 -48
- package/dist/geo/bounds.d.ts +14 -3
- package/dist/geo/bounds.js +98 -68
- package/dist/geo/constraints.js +88 -93
- package/dist/geo/coordinates.js +31 -23
- package/dist/geo/editor.d.ts +13 -13
- package/dist/geo/editor.js +176 -153
- package/dist/geo/geocoding.d.ts +4 -4
- package/dist/geo/geocoding.js +38 -41
- package/dist/geo/geojson.d.ts +12 -74
- package/dist/geo/geojson.js +52 -40
- package/dist/geo/geometry-math.d.ts +4 -4
- package/dist/geo/geometry-math.js +100 -93
- package/dist/geo/index.d.ts +7 -6
- package/dist/geo/map-layers.d.ts +42 -7
- package/dist/geo/map-layers.js +88 -62
- package/dist/geo/map-style.js +31 -32
- package/dist/geo/messages.js +48 -45
- package/dist/geo/nominatim.js +88 -80
- package/dist/geo/osm-presets.d.ts +4 -3
- package/dist/geo/osm-presets.js +108 -86
- package/dist/geo/osm-tags.js +14 -12
- package/dist/geo/overpass.d.ts +14 -2
- package/dist/geo/overpass.js +83 -90
- package/dist/geo/shape-value.d.ts +9 -5
- package/dist/geo/shape-value.js +169 -146
- package/dist/geo/snap-to-roads.d.ts +3 -3
- package/dist/geo/snap-to-roads.js +55 -50
- package/dist/geo/throttle.js +36 -39
- package/dist/guards.d.ts +37 -0
- package/dist/guards.js +62 -0
- package/dist/i18n/default-messages.js +34 -29
- package/dist/i18n/english-messages.js +187 -189
- package/dist/i18n/i18n.d.ts +2 -2
- package/dist/i18n/i18n.js +47 -60
- package/dist/i18n/inflection.d.ts +34 -0
- package/dist/i18n/inflection.js +82 -0
- package/dist/i18n/messages.d.ts +10 -2
- package/dist/i18n/messages.js +17 -4
- package/dist/i18n/missing-keys.js +66 -62
- package/dist/i18n/translatable.d.ts +16 -1
- package/dist/i18n/translatable.js +40 -47
- package/dist/index.d.ts +71 -24
- package/dist/index.js +40 -288
- package/dist/json/deep-equal.d.ts +8 -0
- package/dist/json/deep-equal.js +24 -0
- package/dist/json/strip-json-comments.js +38 -42
- package/dist/noop.d.ts +2 -0
- package/dist/noop.js +4 -0
- package/dist/notification/error-message.d.ts +2 -0
- package/dist/notification/error-message.js +10 -0
- package/dist/notification/notification-store.d.ts +3 -2
- package/dist/notification/notification-store.js +41 -45
- package/dist/onboarding-tour/placement.js +63 -78
- package/dist/onboarding-tour/tour-controller.js +46 -54
- package/dist/realtime/add-events-for-mutations.js +75 -81
- package/dist/realtime/announce-lock.js +16 -13
- package/dist/realtime/broadcast-channel-transport.js +65 -59
- package/dist/realtime/copy-members.js +22 -14
- package/dist/realtime/in-memory-lock-provider.js +40 -48
- package/dist/realtime/live.js +95 -101
- package/dist/realtime/lock-options.js +16 -16
- package/dist/realtime/lock-session.js +91 -78
- package/dist/realtime/memory-transport.js +25 -25
- package/dist/realtime/presence.js +120 -122
- package/dist/realtime/realtime-data-provider.d.ts +10 -0
- package/dist/realtime/realtime-data-provider.js +45 -42
- package/dist/realtime/reconnect-backoff.js +17 -0
- package/dist/realtime/sse-transport.d.ts +31 -0
- package/dist/realtime/sse-transport.js +235 -0
- package/dist/realtime/status-store.js +23 -28
- package/dist/realtime/topic-hub.js +62 -71
- package/dist/realtime/topics.js +10 -12
- package/dist/realtime/types.js +10 -12
- package/dist/realtime/unread-count.js +21 -21
- package/dist/realtime/websocket-transport.d.ts +32 -0
- package/dist/realtime/websocket-transport.js +306 -0
- package/dist/resource/describe-resource.d.ts +2 -0
- package/dist/resource/describe-resource.js +232 -219
- package/dist/resource/get-at-path.js +29 -14
- package/dist/resource/identifier.d.ts +18 -0
- package/dist/resource/identifier.js +27 -0
- package/dist/resource/record.d.ts +4 -0
- package/dist/resource/record.js +10 -0
- package/dist/resource/register.d.ts +7 -0
- package/dist/resource/registry-store.d.ts +45 -15
- package/dist/resource/registry-store.js +67 -60
- package/dist/resource/registry.d.ts +10 -0
- package/dist/resource/registry.js +8 -4
- package/dist/resource/source-path.d.ts +16 -1
- package/dist/rich-text/rich-text-extras.d.ts +132 -0
- package/dist/rich-text/rich-text-extras.js +179 -0
- package/dist/rich-text/rich-text-image.d.ts +124 -0
- package/dist/rich-text/rich-text-image.js +163 -0
- package/dist/rich-text/rich-text.d.ts +6 -0
- package/dist/rich-text/rich-text.js +125 -88
- package/dist/router/location.d.ts +22 -4
- package/dist/router/location.js +41 -18
- package/dist/router/match.js +47 -39
- package/dist/router/paths.js +40 -34
- package/dist/router/record-from-location.d.ts +1 -1
- package/dist/router/record-from-location.js +25 -17
- package/dist/store/adapters.js +51 -55
- package/dist/store/micro-store.js +57 -59
- package/dist/store/shallow-equal.d.ts +12 -0
- package/dist/store/shallow-equal.js +20 -0
- package/dist/store/store.js +54 -57
- package/package.json +13 -11
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { isRecord } from "../guards.js";
|
|
2
|
+
import { reconnectDelay, resolveReconnectConfig } from "./reconnect-backoff.js";
|
|
3
|
+
import { createTopicHub } from "./topic-hub.js";
|
|
4
|
+
// src/realtime/sse-transport.ts
|
|
5
|
+
function isWireEvent(value) {
|
|
6
|
+
return isRecord(value) && typeof value.type === "string" && (value.meta === void 0 || isRecord(value.meta));
|
|
7
|
+
}
|
|
8
|
+
var DEFAULT_IDLE_DISCONNECT_MS = 3e4;
|
|
9
|
+
/**
|
|
10
|
+
* Receives realtime events over server-sent events, one named event per topic,
|
|
11
|
+
* and publishes with a POST.
|
|
12
|
+
*/
|
|
13
|
+
function sseTransport({ url, publishUrl, getAuthToken, withCredentials = false, reconnect, idleDisconnectMs = DEFAULT_IDLE_DISCONNECT_MS, topicFilterParam = "topics", onError, fetch: fetchOption }) {
|
|
14
|
+
const hub = createTopicHub({ onError });
|
|
15
|
+
hub.setStatus("idle");
|
|
16
|
+
const config = resolveReconnectConfig(reconnect);
|
|
17
|
+
let source = null;
|
|
18
|
+
let filter = /* @__PURE__ */ new Set();
|
|
19
|
+
let active = false;
|
|
20
|
+
let generation = 0;
|
|
21
|
+
let attempt = 0;
|
|
22
|
+
let dropped = false;
|
|
23
|
+
let openQueued = false;
|
|
24
|
+
let resolvingToken = false;
|
|
25
|
+
let retryTimer;
|
|
26
|
+
let idleTimer;
|
|
27
|
+
const readToken = async () => await getAuthToken?.() || void 0;
|
|
28
|
+
const stopStream = () => {
|
|
29
|
+
clearTimeout(retryTimer);
|
|
30
|
+
retryTimer = void 0;
|
|
31
|
+
source?.close();
|
|
32
|
+
source = null;
|
|
33
|
+
filter = /* @__PURE__ */ new Set();
|
|
34
|
+
};
|
|
35
|
+
const streamUrl = (topics, token) => {
|
|
36
|
+
const query = [`${encodeURIComponent(topicFilterParam)}=${encodeURIComponent(topics.join(","))}`, ...token === void 0 ? [] : [`token=${encodeURIComponent(token)}`]].join("&");
|
|
37
|
+
return `${url}${url.includes("?") ? "&" : "?"}${query}`;
|
|
38
|
+
};
|
|
39
|
+
const retryOrGiveUp = (failure) => {
|
|
40
|
+
const retrying = config.enabled && attempt < config.maxAttempts;
|
|
41
|
+
hub.report(failure(retrying));
|
|
42
|
+
if (!retrying) {
|
|
43
|
+
active = false;
|
|
44
|
+
hub.setStatus("disconnected");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
hub.setStatus("reconnecting");
|
|
48
|
+
retryTimer = setTimeout(() => {
|
|
49
|
+
retryTimer = void 0;
|
|
50
|
+
open(true);
|
|
51
|
+
}, reconnectDelay(attempt, config));
|
|
52
|
+
attempt += 1;
|
|
53
|
+
};
|
|
54
|
+
const handleError = (stream, opened) => {
|
|
55
|
+
if (source !== stream) return;
|
|
56
|
+
stopStream();
|
|
57
|
+
dropped ||= opened;
|
|
58
|
+
retryOrGiveUp((retrying) => ({
|
|
59
|
+
kind: "connect_failed",
|
|
60
|
+
retrying
|
|
61
|
+
}));
|
|
62
|
+
};
|
|
63
|
+
async function open(isRetry = false) {
|
|
64
|
+
stopStream();
|
|
65
|
+
const current = ++generation;
|
|
66
|
+
active = true;
|
|
67
|
+
resolvingToken = true;
|
|
68
|
+
let token;
|
|
69
|
+
try {
|
|
70
|
+
token = await readToken();
|
|
71
|
+
} catch (cause) {
|
|
72
|
+
if (current !== generation) return;
|
|
73
|
+
resolvingToken = false;
|
|
74
|
+
if (isRetry) {
|
|
75
|
+
retryOrGiveUp((retrying) => ({
|
|
76
|
+
kind: "auth_failed",
|
|
77
|
+
cause,
|
|
78
|
+
retrying
|
|
79
|
+
}));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
active = false;
|
|
83
|
+
hub.report({
|
|
84
|
+
kind: "auth_failed",
|
|
85
|
+
cause,
|
|
86
|
+
retrying: false
|
|
87
|
+
});
|
|
88
|
+
hub.setStatus("disconnected");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (current !== generation) return;
|
|
92
|
+
resolvingToken = false;
|
|
93
|
+
const topics = hub.topics();
|
|
94
|
+
if (topics.length === 0) {
|
|
95
|
+
goIdle();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let stream;
|
|
99
|
+
try {
|
|
100
|
+
stream = new EventSource(streamUrl(topics, token), { withCredentials });
|
|
101
|
+
} catch {
|
|
102
|
+
active = false;
|
|
103
|
+
hub.report({
|
|
104
|
+
kind: "connect_failed",
|
|
105
|
+
retrying: false
|
|
106
|
+
});
|
|
107
|
+
hub.setStatus("disconnected");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
source = stream;
|
|
111
|
+
filter = new Set(topics);
|
|
112
|
+
let opened = false;
|
|
113
|
+
stream.addEventListener("open", () => {
|
|
114
|
+
if (source !== stream) return;
|
|
115
|
+
opened = true;
|
|
116
|
+
attempt = 0;
|
|
117
|
+
hub.setStatus("connected");
|
|
118
|
+
if (dropped) {
|
|
119
|
+
dropped = false;
|
|
120
|
+
hub.reconnected();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
stream.addEventListener("error", () => handleError(stream, opened));
|
|
124
|
+
for (const topic of topics) stream.addEventListener(topic, (message) => {
|
|
125
|
+
if (source !== stream || !("data" in message)) return;
|
|
126
|
+
if (typeof message.data === "string") receive(topic, message.data);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const receive = (topic, data) => {
|
|
130
|
+
let parsed;
|
|
131
|
+
try {
|
|
132
|
+
parsed = JSON.parse(data);
|
|
133
|
+
} catch (cause) {
|
|
134
|
+
hub.report({
|
|
135
|
+
kind: "parse_failed",
|
|
136
|
+
topic,
|
|
137
|
+
cause,
|
|
138
|
+
retrying: false
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (isWireEvent(parsed)) hub.dispatch({
|
|
143
|
+
...parsed,
|
|
144
|
+
topic
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const queueOpen = () => {
|
|
148
|
+
if (openQueued) return;
|
|
149
|
+
openQueued = true;
|
|
150
|
+
Promise.resolve().then(() => {
|
|
151
|
+
if (!openQueued) return;
|
|
152
|
+
openQueued = false;
|
|
153
|
+
if (!active) attempt = 0;
|
|
154
|
+
return open();
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
function goIdle() {
|
|
158
|
+
generation += 1;
|
|
159
|
+
resolvingToken = false;
|
|
160
|
+
clearTimeout(idleTimer);
|
|
161
|
+
idleTimer = void 0;
|
|
162
|
+
stopStream();
|
|
163
|
+
active = false;
|
|
164
|
+
dropped = false;
|
|
165
|
+
if (hub.getStatus() !== "idle") hub.setStatus("idle");
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
subscribe(topic, callback) {
|
|
169
|
+
const stop = hub.subscribe(topic, callback);
|
|
170
|
+
clearTimeout(idleTimer);
|
|
171
|
+
idleTimer = void 0;
|
|
172
|
+
const covered = retryTimer !== void 0 || resolvingToken || filter.has(topic);
|
|
173
|
+
if (!active || !covered) queueOpen();
|
|
174
|
+
return () => {
|
|
175
|
+
stop();
|
|
176
|
+
if (hub.topics().length > 0 || !active) return;
|
|
177
|
+
clearTimeout(idleTimer);
|
|
178
|
+
idleTimer = setTimeout(goIdle, idleDisconnectMs);
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
async publish(topic, event) {
|
|
182
|
+
if (!publishUrl) throw new Error("sseTransport needs the publishUrl option to publish");
|
|
183
|
+
const headers = { "Content-Type": "application/json" };
|
|
184
|
+
try {
|
|
185
|
+
const token = await readToken();
|
|
186
|
+
if (token !== void 0) headers.Authorization = `Bearer ${token}`;
|
|
187
|
+
} catch (cause) {
|
|
188
|
+
hub.report({
|
|
189
|
+
kind: "auth_failed",
|
|
190
|
+
topic,
|
|
191
|
+
cause,
|
|
192
|
+
retrying: false
|
|
193
|
+
});
|
|
194
|
+
throw cause;
|
|
195
|
+
}
|
|
196
|
+
const doFetch = fetchOption ?? ((input, init) => globalThis.fetch(input, init));
|
|
197
|
+
try {
|
|
198
|
+
const response = await doFetch(publishUrl, {
|
|
199
|
+
method: "POST",
|
|
200
|
+
headers,
|
|
201
|
+
body: JSON.stringify({
|
|
202
|
+
topic,
|
|
203
|
+
event
|
|
204
|
+
}),
|
|
205
|
+
credentials: withCredentials ? "include" : void 0
|
|
206
|
+
});
|
|
207
|
+
if (!response.ok) throw new Error(`Publishing to ${topic} failed: ${response.status}`);
|
|
208
|
+
} catch (cause) {
|
|
209
|
+
hub.report({
|
|
210
|
+
kind: "send_failed",
|
|
211
|
+
topic,
|
|
212
|
+
cause,
|
|
213
|
+
retrying: false
|
|
214
|
+
});
|
|
215
|
+
throw cause;
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
onReconnect: hub.onReconnect,
|
|
219
|
+
onStatusChange: hub.onStatusChange,
|
|
220
|
+
onError: hub.onError,
|
|
221
|
+
getStatus: hub.getStatus,
|
|
222
|
+
close() {
|
|
223
|
+
openQueued = false;
|
|
224
|
+
generation += 1;
|
|
225
|
+
resolvingToken = false;
|
|
226
|
+
clearTimeout(idleTimer);
|
|
227
|
+
idleTimer = void 0;
|
|
228
|
+
stopStream();
|
|
229
|
+
active = false;
|
|
230
|
+
dropped = false;
|
|
231
|
+
hub.setStatus("disconnected");
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
export { sseTransport };
|
|
@@ -1,33 +1,28 @@
|
|
|
1
1
|
import { createMicroStore } from "../store/micro-store.js";
|
|
2
|
+
// src/realtime/status-store.ts
|
|
2
3
|
function readStatus(source) {
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
if (source.getStatus) return source.getStatus();
|
|
5
|
+
return source.onStatusChange ? "disconnected" : "idle";
|
|
5
6
|
}
|
|
7
|
+
/** Follows the connection while it has a subscriber, and lets go of it at zero. */
|
|
6
8
|
function createRealtimeStatusStore(source) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
);
|
|
29
|
-
return store;
|
|
9
|
+
const store = createMicroStore({
|
|
10
|
+
status: readStatus(source),
|
|
11
|
+
lastError: null
|
|
12
|
+
}, { onSubscribed() {
|
|
13
|
+
const status = readStatus(source);
|
|
14
|
+
store.setState(status === "connected" ? {
|
|
15
|
+
status,
|
|
16
|
+
lastError: null
|
|
17
|
+
} : { status });
|
|
18
|
+
const stops = [source.onStatusChange?.((status) => store.setState(status === "connected" ? {
|
|
19
|
+
status,
|
|
20
|
+
lastError: null
|
|
21
|
+
} : { status })), source.onError?.((lastError) => store.setState({ lastError }))];
|
|
22
|
+
return () => {
|
|
23
|
+
for (const stop of stops) stop?.();
|
|
24
|
+
};
|
|
25
|
+
} });
|
|
26
|
+
return store;
|
|
30
27
|
}
|
|
31
|
-
export {
|
|
32
|
-
createRealtimeStatusStore
|
|
33
|
-
};
|
|
28
|
+
export { createRealtimeStatusStore };
|
|
@@ -1,75 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// src/realtime/topic-hub.ts
|
|
2
|
+
var addListener = (listeners, listener) => {
|
|
3
|
+
listeners.add(listener);
|
|
4
|
+
return () => {
|
|
5
|
+
listeners.delete(listener);
|
|
6
|
+
};
|
|
6
7
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
8
|
+
var isolate = (call) => {
|
|
9
|
+
try {
|
|
10
|
+
call();
|
|
11
|
+
} catch {}
|
|
12
12
|
};
|
|
13
|
+
/** Starts `"connected"`; a transport that connects asynchronously sets its own status. */
|
|
13
14
|
function createTopicHub({ onError } = {}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
status = next;
|
|
65
|
-
for (const listener of [...statusListeners])
|
|
66
|
-
isolate(() => listener(next));
|
|
67
|
-
},
|
|
68
|
-
reconnected() {
|
|
69
|
-
for (const listener of [...reconnectListeners]) isolate(listener);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
15
|
+
const subscribers = /* @__PURE__ */ new Map();
|
|
16
|
+
const reconnectListeners = /* @__PURE__ */ new Set();
|
|
17
|
+
const statusListeners = /* @__PURE__ */ new Set();
|
|
18
|
+
const errorListeners = /* @__PURE__ */ new Set();
|
|
19
|
+
let status = "connected";
|
|
20
|
+
const report = (error) => {
|
|
21
|
+
for (const listener of [...onError ? [onError] : [], ...errorListeners]) isolate(() => listener(error));
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
subscribe(topic, callback) {
|
|
25
|
+
const subscription = { callback };
|
|
26
|
+
const set = subscribers.get(topic) ?? /* @__PURE__ */ new Set();
|
|
27
|
+
set.add(subscription);
|
|
28
|
+
subscribers.set(topic, set);
|
|
29
|
+
return () => {
|
|
30
|
+
set.delete(subscription);
|
|
31
|
+
if (set.size === 0 && subscribers.get(topic) === set) subscribers.delete(topic);
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
dispatch(event) {
|
|
35
|
+
const live = subscribers.get(event.topic);
|
|
36
|
+
for (const subscription of [...live ?? []]) {
|
|
37
|
+
if (!live?.has(subscription)) continue;
|
|
38
|
+
try {
|
|
39
|
+
subscription.callback(event);
|
|
40
|
+
} catch (cause) {
|
|
41
|
+
report({
|
|
42
|
+
kind: "handler_threw",
|
|
43
|
+
topic: event.topic,
|
|
44
|
+
cause,
|
|
45
|
+
retrying: false
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
has: (topic) => subscribers.has(topic),
|
|
51
|
+
topics: () => [...subscribers.keys()],
|
|
52
|
+
report,
|
|
53
|
+
onReconnect: (callback) => addListener(reconnectListeners, callback),
|
|
54
|
+
onStatusChange: (callback) => addListener(statusListeners, callback),
|
|
55
|
+
onError: (callback) => addListener(errorListeners, callback),
|
|
56
|
+
getStatus: () => status,
|
|
57
|
+
setStatus(next) {
|
|
58
|
+
status = next;
|
|
59
|
+
for (const listener of [...statusListeners]) isolate(() => listener(next));
|
|
60
|
+
},
|
|
61
|
+
reconnected() {
|
|
62
|
+
for (const listener of [...reconnectListeners]) isolate(listener);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
72
65
|
}
|
|
73
|
-
export {
|
|
74
|
-
createTopicHub
|
|
75
|
-
};
|
|
66
|
+
export { createTopicHub };
|
package/dist/realtime/topics.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
resourceTopic
|
|
12
|
-
};
|
|
1
|
+
// src/realtime/topics.ts
|
|
2
|
+
/** Events for the resource as a whole: creates, and the ids of any write. */
|
|
3
|
+
var resourceTopic = (resource) => `resource/${resource}`;
|
|
4
|
+
/** Events for one record. `1` and `"1"` give the same topic. */
|
|
5
|
+
var recordTopic = (resource, id) => `resource/${resource}/${id}`;
|
|
6
|
+
var lockResourceTopic = (resource) => `lock/${resource}`;
|
|
7
|
+
var lockTopic = (resource, id) => `lock/${resource}/${id}`;
|
|
8
|
+
/** Who is on one record. `1` and `"1"` give the same topic. */
|
|
9
|
+
var presenceTopic = (resource, id) => `presence/${resource}/${id}`;
|
|
10
|
+
export { lockResourceTopic, lockTopic, presenceTopic, recordTopic, resourceTopic };
|
package/dist/realtime/types.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
export {
|
|
12
|
-
LockConflictError
|
|
1
|
+
// src/realtime/types.ts
|
|
2
|
+
/** Rejected by `lock()` when another identity already holds the record. */
|
|
3
|
+
var LockConflictError = class extends Error {
|
|
4
|
+
kind = "LockConflictError";
|
|
5
|
+
existingLock;
|
|
6
|
+
constructor(existingLock) {
|
|
7
|
+
super(`Resource ${existingLock.resource}/${String(existingLock.recordId)} is locked by ${String(existingLock.identity)}`);
|
|
8
|
+
this.existingLock = existingLock;
|
|
9
|
+
}
|
|
13
10
|
};
|
|
11
|
+
export { LockConflictError };
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { resourceTopic } from "./topics.js";
|
|
2
|
-
|
|
2
|
+
// src/realtime/unread-count.ts
|
|
3
|
+
var DEFAULT_EVENTS = ["created"];
|
|
4
|
+
/**
|
|
5
|
+
* The store key of a resource's unread count for an event list. The default
|
|
6
|
+
* `["created"]` keeps `realtime.unread.<resource>`; any other list adds its
|
|
7
|
+
* sorted, de-duplicated types as a suffix, so the order of `events` never
|
|
8
|
+
* splits a count and links watching different events never share one.
|
|
9
|
+
*/
|
|
3
10
|
function unreadKey(resource, events = DEFAULT_EVENTS) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
11
|
+
const types = [...new Set(events)].sort();
|
|
12
|
+
const base = `realtime.unread.${resource}`;
|
|
13
|
+
return types.length === 1 && types[0] === "created" ? base : `${base}.${types.join("+")}`;
|
|
7
14
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return dataProvider.subscribe(resourceTopic(resource), (event) => {
|
|
16
|
-
if (!events.includes(event.type)) return;
|
|
17
|
-
store.setItem(key, (store.getItem(key) ?? 0) + 1);
|
|
18
|
-
});
|
|
15
|
+
/** Adds one to the count under `unreadKey(resource, events)` for each listed event. Returns the teardown. */
|
|
16
|
+
function subscribeUnreadCount({ dataProvider, store, resource, events = DEFAULT_EVENTS }) {
|
|
17
|
+
const key = unreadKey(resource, events);
|
|
18
|
+
return dataProvider.subscribe(resourceTopic(resource), (event) => {
|
|
19
|
+
if (!events.includes(event.type)) return;
|
|
20
|
+
store.setItem(key, (store.getItem(key) ?? 0) + 1);
|
|
21
|
+
});
|
|
19
22
|
}
|
|
23
|
+
/** Resets the count of one event list; pass the same `events` the link subscribed with. */
|
|
20
24
|
function clearUnread(store, resource, events) {
|
|
21
|
-
|
|
25
|
+
store.setItem(unreadKey(resource, events), 0);
|
|
22
26
|
}
|
|
23
|
-
export {
|
|
24
|
-
clearUnread,
|
|
25
|
-
subscribeUnreadCount,
|
|
26
|
-
unreadKey
|
|
27
|
-
};
|
|
27
|
+
export { clearUnread, subscribeUnreadCount, unreadKey };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type RealtimeReconnectConfig } from "./reconnect-backoff.js";
|
|
2
|
+
import type { RealtimeTransport, RealtimeTransportError } from "./types.js";
|
|
3
|
+
export interface WebSocketTransportOptions {
|
|
4
|
+
url: string;
|
|
5
|
+
/** Subprotocols offered on every connection. */
|
|
6
|
+
protocols?: string[];
|
|
7
|
+
/** Read before every connection, so a retry carries a fresh token. */
|
|
8
|
+
getAuthToken?: () => string | null | undefined | Promise<string | null | undefined>;
|
|
9
|
+
/**
|
|
10
|
+
* Where the token goes. `"query"` (default) adds a URL-encoded `token`
|
|
11
|
+
* parameter; `"subprotocol"` offers it after `protocols` and keeps it out of
|
|
12
|
+
* the URL.
|
|
13
|
+
*/
|
|
14
|
+
authMode?: "query" | "subprotocol";
|
|
15
|
+
reconnect?: RealtimeReconnectConfig;
|
|
16
|
+
/** Close the socket after this long with no subscriber. Default 30000. */
|
|
17
|
+
idleDisconnectMs?: number;
|
|
18
|
+
/** Ping this often; a ping with no reply by the next one is a drop. Default 30000. */
|
|
19
|
+
heartbeatMs?: number;
|
|
20
|
+
/** Called for every transport error, like a listener from `onError()`. */
|
|
21
|
+
onError?: (error: RealtimeTransportError) => void;
|
|
22
|
+
}
|
|
23
|
+
/** A `RealtimeTransport` with every optional member, plus `close()`. */
|
|
24
|
+
export interface WebSocketTransport extends Required<RealtimeTransport> {
|
|
25
|
+
/**
|
|
26
|
+
* Closes the socket, rejects queued publishes and sets `"disconnected"`.
|
|
27
|
+
* A later subscribe or publish opens again.
|
|
28
|
+
*/
|
|
29
|
+
close(): void;
|
|
30
|
+
}
|
|
31
|
+
/** Carries realtime events over one WebSocket and reconnects with backoff. */
|
|
32
|
+
export declare function webSocketTransport({ url, protocols, getAuthToken, authMode, reconnect, idleDisconnectMs, heartbeatMs, onError, }: WebSocketTransportOptions): WebSocketTransport;
|