@rawdash/sdk-runtime 0.26.0 → 0.28.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/index.d.ts +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -23,6 +23,14 @@ function subscribe(source, dashboardId, callbacks, options = {}) {
|
|
|
23
23
|
let stopped = false;
|
|
24
24
|
let visibilityCleanup = null;
|
|
25
25
|
let bootstrapRetryHandle = null;
|
|
26
|
+
let bootstrapSettled = false;
|
|
27
|
+
function settleBootstrap() {
|
|
28
|
+
if (bootstrapSettled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
bootstrapSettled = true;
|
|
32
|
+
callbacks.onBootstrapped?.();
|
|
33
|
+
}
|
|
26
34
|
function schedule(t, delayMs) {
|
|
27
35
|
if (stopped) {
|
|
28
36
|
return;
|
|
@@ -92,10 +100,12 @@ function subscribe(source, dashboardId, callbacks, options = {}) {
|
|
|
92
100
|
for (const widget of widgets) {
|
|
93
101
|
applyWidget(widget);
|
|
94
102
|
}
|
|
103
|
+
settleBootstrap();
|
|
95
104
|
} catch (err) {
|
|
96
105
|
if (stopped) {
|
|
97
106
|
return;
|
|
98
107
|
}
|
|
108
|
+
settleBootstrap();
|
|
99
109
|
callbacks.onError?.(err);
|
|
100
110
|
if (bootstrapRetryHandle !== null) {
|
|
101
111
|
clearTimer(bootstrapRetryHandle);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine.ts"],"sourcesContent":["import type { CachedWidget, DataSource, WidgetSyncState } from '@rawdash/core';\n\nexport interface SubscribeCallbacks {\n onWidgetUpdated: (widget: CachedWidget) => void;\n onWidgetUnchanged?: (widget: CachedWidget) => void;\n onWidgetFailing?: (widget: CachedWidget) => void;\n onError?: (error: unknown) => void;\n}\n\nexport interface SubscribeOptions {\n syncingPollMs?: number;\n syncingPollMaxMs?: number;\n unsyncedPollMs?: number;\n failingBackoffMs?: number;\n lateRetryStartMs?: number;\n lateRetryMaxMs?: number;\n defaultIntervalSeconds?: number;\n jitterMs?: number;\n now?: () => number;\n setTimeout?: (cb: () => void, ms: number) => unknown;\n clearTimeout?: (handle: unknown) => void;\n random?: () => number;\n visibility?: VisibilitySource | null;\n}\n\nexport interface VisibilitySource {\n isHidden(): boolean;\n onChange(listener: () => void): () => void;\n}\n\ninterface ResolvedOptions {\n syncingPollMs: number;\n syncingPollMaxMs: number;\n unsyncedPollMs: number;\n failingBackoffMs: number;\n lateRetryStartMs: number;\n lateRetryMaxMs: number;\n defaultIntervalSeconds: number;\n jitterMs: number;\n}\n\nconst DEFAULT_OPTIONS: ResolvedOptions = {\n syncingPollMs: 3_000,\n syncingPollMaxMs: 60_000,\n unsyncedPollMs: 10_000,\n failingBackoffMs: 60_000,\n lateRetryStartMs: 3_000,\n lateRetryMaxMs: 30_000,\n defaultIntervalSeconds: 300,\n jitterMs: 2_000,\n};\n\ntype Timer = unknown;\n\ninterface WidgetTracker {\n widgetId: string;\n lastSyncAtMs: number | null;\n lastSyncState: WidgetSyncState | undefined;\n failingNotified: boolean;\n lateRetryDelayMs: number | null;\n syncingSinceMs: number | null;\n timer: Timer | null;\n}\n\nexport type Unsubscribe = () => void;\n\nexport function subscribe(\n source: DataSource,\n dashboardId: string,\n callbacks: SubscribeCallbacks,\n options: SubscribeOptions = {},\n): Unsubscribe {\n const opts = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n const now = opts.now ?? (() => Date.now());\n const setTimer = opts.setTimeout ?? ((cb, ms) => setTimeout(cb, ms));\n const clearTimer = opts.clearTimeout ?? ((h) => clearTimeout(h as never));\n const random = opts.random ?? Math.random;\n const visibility = opts.visibility ?? defaultVisibility();\n\n const trackers = new Map<string, WidgetTracker>();\n let stopped = false;\n let visibilityCleanup: (() => void) | null = null;\n let bootstrapRetryHandle: Timer | null = null;\n\n function schedule(t: WidgetTracker, delayMs: number): void {\n if (stopped) {\n return;\n }\n if (t.timer !== null) {\n clearTimer(t.timer);\n t.timer = null;\n }\n if (visibility && visibility.isHidden()) {\n return;\n }\n const jitter = Math.floor(random() * opts.jitterMs);\n t.timer = setTimer(\n () => {\n t.timer = null;\n void poll(t);\n },\n Math.max(0, delayMs + jitter),\n );\n }\n\n function tracker(widgetId: string): WidgetTracker {\n let t = trackers.get(widgetId);\n if (!t) {\n t = {\n widgetId,\n lastSyncAtMs: null,\n lastSyncState: undefined,\n failingNotified: false,\n lateRetryDelayMs: null,\n syncingSinceMs: null,\n timer: null,\n };\n trackers.set(widgetId, t);\n }\n return t;\n }\n\n function applyWidget(widget: CachedWidget): void {\n const t = tracker(widget.widgetId);\n const nextDelay = handleWidget(t, widget, now(), opts, callbacks);\n schedule(t, nextDelay);\n }\n\n async function poll(t: WidgetTracker): Promise<void> {\n if (stopped) {\n return;\n }\n try {\n const widget = await source.getWidget(dashboardId, t.widgetId);\n if (stopped) {\n return;\n }\n const nextDelay = handleWidget(t, widget, now(), opts, callbacks);\n schedule(t, nextDelay);\n } catch (err) {\n if (stopped) {\n return;\n }\n callbacks.onError?.(err);\n schedule(t, opts.failingBackoffMs);\n }\n }\n\n async function bootstrap(): Promise<void> {\n try {\n const widgets = await source.getWidgets(dashboardId);\n if (stopped) {\n return;\n }\n for (const widget of widgets) {\n applyWidget(widget);\n }\n } catch (err) {\n if (stopped) {\n return;\n }\n callbacks.onError?.(err);\n if (bootstrapRetryHandle !== null) {\n clearTimer(bootstrapRetryHandle);\n }\n bootstrapRetryHandle = setTimer(() => {\n bootstrapRetryHandle = null;\n if (!stopped) {\n void bootstrap();\n }\n }, opts.failingBackoffMs);\n }\n }\n\n if (visibility) {\n visibilityCleanup = visibility.onChange(() => {\n if (stopped) {\n return;\n }\n if (visibility.isHidden()) {\n for (const t of trackers.values()) {\n if (t.timer !== null) {\n clearTimer(t.timer);\n t.timer = null;\n }\n }\n } else {\n for (const t of trackers.values()) {\n schedule(t, 0);\n }\n }\n });\n }\n\n void bootstrap();\n\n return () => {\n stopped = true;\n for (const t of trackers.values()) {\n if (t.timer !== null) {\n clearTimer(t.timer);\n t.timer = null;\n }\n }\n if (bootstrapRetryHandle !== null) {\n clearTimer(bootstrapRetryHandle);\n bootstrapRetryHandle = null;\n }\n if (visibilityCleanup) {\n visibilityCleanup();\n }\n };\n}\n\nexport function handleWidget(\n t: WidgetTracker,\n widget: CachedWidget,\n nowMs: number,\n opts: ResolvedOptions,\n callbacks: SubscribeCallbacks,\n): number {\n const incomingSyncAtMs = widget.cachedAt\n ? new Date(widget.cachedAt).getTime()\n : null;\n const rawIntervalSeconds =\n widget.syncIntervalSeconds ?? opts.defaultIntervalSeconds;\n const safeIntervalSeconds =\n Number.isFinite(rawIntervalSeconds) && rawIntervalSeconds > 0\n ? rawIntervalSeconds\n : opts.defaultIntervalSeconds;\n const intervalMs = safeIntervalSeconds * 1000;\n const state = widget.syncState;\n\n const previousSyncAtMs = t.lastSyncAtMs;\n const advanced =\n incomingSyncAtMs !== null && incomingSyncAtMs !== previousSyncAtMs;\n\n switch (state) {\n case 'fresh': {\n if (advanced || previousSyncAtMs === null) {\n t.lastSyncAtMs = incomingSyncAtMs;\n t.lastSyncState = state;\n t.failingNotified = false;\n t.lateRetryDelayMs = null;\n t.syncingSinceMs = null;\n callbacks.onWidgetUpdated(widget);\n if (incomingSyncAtMs === null) {\n return intervalMs;\n }\n const expected = incomingSyncAtMs + intervalMs;\n return Math.max(0, expected - nowMs);\n }\n callbacks.onWidgetUnchanged?.(widget);\n const expected = (incomingSyncAtMs ?? nowMs) + intervalMs;\n const baseDelay = Math.max(0, expected - nowMs);\n const giveUpAtMs = (incomingSyncAtMs ?? nowMs) + 2 * intervalMs;\n if (nowMs >= giveUpAtMs) {\n t.lateRetryDelayMs = null;\n return Math.max(baseDelay, intervalMs);\n }\n const prev = t.lateRetryDelayMs ?? 0;\n const next =\n prev === 0\n ? opts.lateRetryStartMs\n : Math.min(prev * 2, opts.lateRetryMaxMs);\n t.lateRetryDelayMs = next;\n t.lastSyncState = state;\n return next;\n }\n case 'syncing': {\n t.lastSyncState = state;\n if (t.syncingSinceMs === null) {\n t.syncingSinceMs = nowMs;\n }\n callbacks.onWidgetUnchanged?.(widget);\n const elapsed = nowMs - t.syncingSinceMs;\n if (elapsed >= opts.syncingPollMaxMs) {\n return intervalMs;\n }\n return opts.syncingPollMs;\n }\n case 'failing':\n case 'stale': {\n t.lastSyncState = state;\n if (!t.failingNotified) {\n t.failingNotified = true;\n callbacks.onWidgetFailing?.(widget);\n } else {\n callbacks.onWidgetUnchanged?.(widget);\n }\n return opts.failingBackoffMs;\n }\n case 'unsynced': {\n t.lastSyncState = state;\n callbacks.onWidgetUnchanged?.(widget);\n return opts.unsyncedPollMs;\n }\n default: {\n if (advanced || previousSyncAtMs === null) {\n t.lastSyncAtMs = incomingSyncAtMs;\n callbacks.onWidgetUpdated(widget);\n } else {\n callbacks.onWidgetUnchanged?.(widget);\n }\n t.lastSyncState = state;\n return intervalMs;\n }\n }\n}\n\nfunction defaultVisibility(): VisibilitySource | null {\n if (typeof document === 'undefined') {\n return null;\n }\n const doc = document;\n return {\n isHidden: () => doc.hidden === true,\n onChange: (listener) => {\n const handler = () => listener();\n doc.addEventListener('visibilitychange', handler);\n if (typeof window !== 'undefined') {\n window.addEventListener('focus', handler);\n }\n return () => {\n doc.removeEventListener('visibilitychange', handler);\n if (typeof window !== 'undefined') {\n window.removeEventListener('focus', handler);\n }\n };\n },\n };\n}\n"],"mappings":"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","names":["expected"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine.ts"],"sourcesContent":["import type { CachedWidget, DataSource, WidgetSyncState } from '@rawdash/core';\n\nexport interface SubscribeCallbacks {\n onWidgetUpdated: (widget: CachedWidget) => void;\n onWidgetUnchanged?: (widget: CachedWidget) => void;\n onWidgetFailing?: (widget: CachedWidget) => void;\n onError?: (error: unknown) => void;\n onBootstrapped?: () => void;\n}\n\nexport interface SubscribeOptions {\n syncingPollMs?: number;\n syncingPollMaxMs?: number;\n unsyncedPollMs?: number;\n failingBackoffMs?: number;\n lateRetryStartMs?: number;\n lateRetryMaxMs?: number;\n defaultIntervalSeconds?: number;\n jitterMs?: number;\n now?: () => number;\n setTimeout?: (cb: () => void, ms: number) => unknown;\n clearTimeout?: (handle: unknown) => void;\n random?: () => number;\n visibility?: VisibilitySource | null;\n}\n\nexport interface VisibilitySource {\n isHidden(): boolean;\n onChange(listener: () => void): () => void;\n}\n\ninterface ResolvedOptions {\n syncingPollMs: number;\n syncingPollMaxMs: number;\n unsyncedPollMs: number;\n failingBackoffMs: number;\n lateRetryStartMs: number;\n lateRetryMaxMs: number;\n defaultIntervalSeconds: number;\n jitterMs: number;\n}\n\nconst DEFAULT_OPTIONS: ResolvedOptions = {\n syncingPollMs: 3_000,\n syncingPollMaxMs: 60_000,\n unsyncedPollMs: 10_000,\n failingBackoffMs: 60_000,\n lateRetryStartMs: 3_000,\n lateRetryMaxMs: 30_000,\n defaultIntervalSeconds: 300,\n jitterMs: 2_000,\n};\n\ntype Timer = unknown;\n\ninterface WidgetTracker {\n widgetId: string;\n lastSyncAtMs: number | null;\n lastSyncState: WidgetSyncState | undefined;\n failingNotified: boolean;\n lateRetryDelayMs: number | null;\n syncingSinceMs: number | null;\n timer: Timer | null;\n}\n\nexport type Unsubscribe = () => void;\n\nexport function subscribe(\n source: DataSource,\n dashboardId: string,\n callbacks: SubscribeCallbacks,\n options: SubscribeOptions = {},\n): Unsubscribe {\n const opts = {\n ...DEFAULT_OPTIONS,\n ...options,\n };\n const now = opts.now ?? (() => Date.now());\n const setTimer = opts.setTimeout ?? ((cb, ms) => setTimeout(cb, ms));\n const clearTimer = opts.clearTimeout ?? ((h) => clearTimeout(h as never));\n const random = opts.random ?? Math.random;\n const visibility = opts.visibility ?? defaultVisibility();\n\n const trackers = new Map<string, WidgetTracker>();\n let stopped = false;\n let visibilityCleanup: (() => void) | null = null;\n let bootstrapRetryHandle: Timer | null = null;\n let bootstrapSettled = false;\n\n function settleBootstrap(): void {\n if (bootstrapSettled) {\n return;\n }\n bootstrapSettled = true;\n callbacks.onBootstrapped?.();\n }\n\n function schedule(t: WidgetTracker, delayMs: number): void {\n if (stopped) {\n return;\n }\n if (t.timer !== null) {\n clearTimer(t.timer);\n t.timer = null;\n }\n if (visibility && visibility.isHidden()) {\n return;\n }\n const jitter = Math.floor(random() * opts.jitterMs);\n t.timer = setTimer(\n () => {\n t.timer = null;\n void poll(t);\n },\n Math.max(0, delayMs + jitter),\n );\n }\n\n function tracker(widgetId: string): WidgetTracker {\n let t = trackers.get(widgetId);\n if (!t) {\n t = {\n widgetId,\n lastSyncAtMs: null,\n lastSyncState: undefined,\n failingNotified: false,\n lateRetryDelayMs: null,\n syncingSinceMs: null,\n timer: null,\n };\n trackers.set(widgetId, t);\n }\n return t;\n }\n\n function applyWidget(widget: CachedWidget): void {\n const t = tracker(widget.widgetId);\n const nextDelay = handleWidget(t, widget, now(), opts, callbacks);\n schedule(t, nextDelay);\n }\n\n async function poll(t: WidgetTracker): Promise<void> {\n if (stopped) {\n return;\n }\n try {\n const widget = await source.getWidget(dashboardId, t.widgetId);\n if (stopped) {\n return;\n }\n const nextDelay = handleWidget(t, widget, now(), opts, callbacks);\n schedule(t, nextDelay);\n } catch (err) {\n if (stopped) {\n return;\n }\n callbacks.onError?.(err);\n schedule(t, opts.failingBackoffMs);\n }\n }\n\n async function bootstrap(): Promise<void> {\n try {\n const widgets = await source.getWidgets(dashboardId);\n if (stopped) {\n return;\n }\n for (const widget of widgets) {\n applyWidget(widget);\n }\n settleBootstrap();\n } catch (err) {\n if (stopped) {\n return;\n }\n settleBootstrap();\n callbacks.onError?.(err);\n if (bootstrapRetryHandle !== null) {\n clearTimer(bootstrapRetryHandle);\n }\n bootstrapRetryHandle = setTimer(() => {\n bootstrapRetryHandle = null;\n if (!stopped) {\n void bootstrap();\n }\n }, opts.failingBackoffMs);\n }\n }\n\n if (visibility) {\n visibilityCleanup = visibility.onChange(() => {\n if (stopped) {\n return;\n }\n if (visibility.isHidden()) {\n for (const t of trackers.values()) {\n if (t.timer !== null) {\n clearTimer(t.timer);\n t.timer = null;\n }\n }\n } else {\n for (const t of trackers.values()) {\n schedule(t, 0);\n }\n }\n });\n }\n\n void bootstrap();\n\n return () => {\n stopped = true;\n for (const t of trackers.values()) {\n if (t.timer !== null) {\n clearTimer(t.timer);\n t.timer = null;\n }\n }\n if (bootstrapRetryHandle !== null) {\n clearTimer(bootstrapRetryHandle);\n bootstrapRetryHandle = null;\n }\n if (visibilityCleanup) {\n visibilityCleanup();\n }\n };\n}\n\nexport function handleWidget(\n t: WidgetTracker,\n widget: CachedWidget,\n nowMs: number,\n opts: ResolvedOptions,\n callbacks: SubscribeCallbacks,\n): number {\n const incomingSyncAtMs = widget.cachedAt\n ? new Date(widget.cachedAt).getTime()\n : null;\n const rawIntervalSeconds =\n widget.syncIntervalSeconds ?? opts.defaultIntervalSeconds;\n const safeIntervalSeconds =\n Number.isFinite(rawIntervalSeconds) && rawIntervalSeconds > 0\n ? rawIntervalSeconds\n : opts.defaultIntervalSeconds;\n const intervalMs = safeIntervalSeconds * 1000;\n const state = widget.syncState;\n\n const previousSyncAtMs = t.lastSyncAtMs;\n const advanced =\n incomingSyncAtMs !== null && incomingSyncAtMs !== previousSyncAtMs;\n\n switch (state) {\n case 'fresh': {\n if (advanced || previousSyncAtMs === null) {\n t.lastSyncAtMs = incomingSyncAtMs;\n t.lastSyncState = state;\n t.failingNotified = false;\n t.lateRetryDelayMs = null;\n t.syncingSinceMs = null;\n callbacks.onWidgetUpdated(widget);\n if (incomingSyncAtMs === null) {\n return intervalMs;\n }\n const expected = incomingSyncAtMs + intervalMs;\n return Math.max(0, expected - nowMs);\n }\n callbacks.onWidgetUnchanged?.(widget);\n const expected = (incomingSyncAtMs ?? nowMs) + intervalMs;\n const baseDelay = Math.max(0, expected - nowMs);\n const giveUpAtMs = (incomingSyncAtMs ?? nowMs) + 2 * intervalMs;\n if (nowMs >= giveUpAtMs) {\n t.lateRetryDelayMs = null;\n return Math.max(baseDelay, intervalMs);\n }\n const prev = t.lateRetryDelayMs ?? 0;\n const next =\n prev === 0\n ? opts.lateRetryStartMs\n : Math.min(prev * 2, opts.lateRetryMaxMs);\n t.lateRetryDelayMs = next;\n t.lastSyncState = state;\n return next;\n }\n case 'syncing': {\n t.lastSyncState = state;\n if (t.syncingSinceMs === null) {\n t.syncingSinceMs = nowMs;\n }\n callbacks.onWidgetUnchanged?.(widget);\n const elapsed = nowMs - t.syncingSinceMs;\n if (elapsed >= opts.syncingPollMaxMs) {\n return intervalMs;\n }\n return opts.syncingPollMs;\n }\n case 'failing':\n case 'stale': {\n t.lastSyncState = state;\n if (!t.failingNotified) {\n t.failingNotified = true;\n callbacks.onWidgetFailing?.(widget);\n } else {\n callbacks.onWidgetUnchanged?.(widget);\n }\n return opts.failingBackoffMs;\n }\n case 'unsynced': {\n t.lastSyncState = state;\n callbacks.onWidgetUnchanged?.(widget);\n return opts.unsyncedPollMs;\n }\n default: {\n if (advanced || previousSyncAtMs === null) {\n t.lastSyncAtMs = incomingSyncAtMs;\n callbacks.onWidgetUpdated(widget);\n } else {\n callbacks.onWidgetUnchanged?.(widget);\n }\n t.lastSyncState = state;\n return intervalMs;\n }\n }\n}\n\nfunction defaultVisibility(): VisibilitySource | null {\n if (typeof document === 'undefined') {\n return null;\n }\n const doc = document;\n return {\n isHidden: () => doc.hidden === true,\n onChange: (listener) => {\n const handler = () => listener();\n doc.addEventListener('visibilitychange', handler);\n if (typeof window !== 'undefined') {\n window.addEventListener('focus', handler);\n }\n return () => {\n doc.removeEventListener('visibilitychange', handler);\n if (typeof window !== 'undefined') {\n window.removeEventListener('focus', handler);\n }\n };\n },\n };\n}\n"],"mappings":"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","names":["expected"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rawdash/sdk-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Rawdash auto-polling subscription engine for client-side dashboards",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@rawdash/core": "0.
|
|
26
|
-
"@rawdash/sdk-client": "0.
|
|
25
|
+
"@rawdash/core": "0.28.0",
|
|
26
|
+
"@rawdash/sdk-client": "0.28.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"tsup": "^8.0.0",
|