@wcstack/timer 1.14.0 → 1.15.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 +31 -21
- package/dist/index.esm.js +59 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.esm.min.js +1 -1
- package/dist/index.esm.min.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
interface ITagNames {
|
|
2
|
-
readonly timer: string;
|
|
3
|
-
}
|
|
4
|
-
interface IWritableTagNames {
|
|
5
|
-
timer?: string;
|
|
6
|
-
}
|
|
7
|
-
interface IConfig {
|
|
8
|
-
readonly autoTrigger: boolean;
|
|
9
|
-
readonly triggerAttribute: string;
|
|
10
|
-
readonly tagNames: ITagNames;
|
|
11
|
-
}
|
|
12
|
-
interface IWritableConfig {
|
|
13
|
-
autoTrigger?: boolean;
|
|
14
|
-
triggerAttribute?: string;
|
|
15
|
-
tagNames?: IWritableTagNames;
|
|
16
|
-
}
|
|
17
1
|
interface IWcBindableProperty {
|
|
18
2
|
readonly name: string;
|
|
19
3
|
readonly event: string;
|
|
@@ -29,11 +13,29 @@ interface IWcBindableCommand {
|
|
|
29
13
|
}
|
|
30
14
|
interface IWcBindable {
|
|
31
15
|
readonly protocol: "wc-bindable";
|
|
32
|
-
readonly version:
|
|
33
|
-
readonly properties: IWcBindableProperty[];
|
|
34
|
-
readonly inputs?: IWcBindableInput[];
|
|
35
|
-
readonly commands?: IWcBindableCommand[];
|
|
16
|
+
readonly version: 1;
|
|
17
|
+
readonly properties: readonly IWcBindableProperty[];
|
|
18
|
+
readonly inputs?: readonly IWcBindableInput[];
|
|
19
|
+
readonly commands?: readonly IWcBindableCommand[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ITagNames {
|
|
23
|
+
readonly timer: string;
|
|
36
24
|
}
|
|
25
|
+
interface IWritableTagNames {
|
|
26
|
+
timer?: string;
|
|
27
|
+
}
|
|
28
|
+
interface IConfig {
|
|
29
|
+
readonly autoTrigger: boolean;
|
|
30
|
+
readonly triggerAttribute: string;
|
|
31
|
+
readonly tagNames: ITagNames;
|
|
32
|
+
}
|
|
33
|
+
interface IWritableConfig {
|
|
34
|
+
autoTrigger?: boolean;
|
|
35
|
+
triggerAttribute?: string;
|
|
36
|
+
tagNames?: IWritableTagNames;
|
|
37
|
+
}
|
|
38
|
+
|
|
37
39
|
/**
|
|
38
40
|
* Payload carried by the `wcs-timer:tick` event.
|
|
39
41
|
* `count` is the number of ticks fired since the last reset; `elapsed` is the
|
|
@@ -45,7 +47,7 @@ interface WcsTimerTickDetail {
|
|
|
45
47
|
}
|
|
46
48
|
/**
|
|
47
49
|
* Value types for TimerCore (headless) — the observable state properties.
|
|
48
|
-
* Use with `bind()` from
|
|
50
|
+
* Use with `bind()` from `a wc-bindable binding core` for compile-time type checking.
|
|
49
51
|
*
|
|
50
52
|
* @example
|
|
51
53
|
* ```typescript
|
|
@@ -117,6 +119,9 @@ declare class TimerCore extends EventTarget {
|
|
|
117
119
|
static wcBindable: IWcBindable;
|
|
118
120
|
private _target;
|
|
119
121
|
private _timerId;
|
|
122
|
+
private _gen;
|
|
123
|
+
private _runGen;
|
|
124
|
+
private _ready;
|
|
120
125
|
private _tick;
|
|
121
126
|
private _running;
|
|
122
127
|
private _paused;
|
|
@@ -129,6 +134,9 @@ declare class TimerCore extends EventTarget {
|
|
|
129
134
|
get tick(): number;
|
|
130
135
|
get elapsed(): number;
|
|
131
136
|
get running(): boolean;
|
|
137
|
+
get ready(): Promise<void>;
|
|
138
|
+
observe(): Promise<void>;
|
|
139
|
+
dispose(): void;
|
|
132
140
|
private _dispatchTick;
|
|
133
141
|
private _setRunning;
|
|
134
142
|
start(options?: TimerStartOptions): void;
|
|
@@ -150,7 +158,9 @@ declare class Timer extends HTMLElement {
|
|
|
150
158
|
static get observedAttributes(): string[];
|
|
151
159
|
private _core;
|
|
152
160
|
private _trigger;
|
|
161
|
+
private _connectedCallbackPromise;
|
|
153
162
|
constructor();
|
|
163
|
+
get connectedCallbackPromise(): Promise<void>;
|
|
154
164
|
get interval(): number;
|
|
155
165
|
set interval(value: number);
|
|
156
166
|
get once(): boolean;
|
package/dist/index.esm.js
CHANGED
|
@@ -80,6 +80,19 @@ class TimerCore extends EventTarget {
|
|
|
80
80
|
};
|
|
81
81
|
_target;
|
|
82
82
|
_timerId = null;
|
|
83
|
+
// Generation guard (§3.4): bumped on dispose() and at every async start
|
|
84
|
+
// (start()/resume()). A scheduled callback (setInterval/setTimeout) captures the
|
|
85
|
+
// generation live via `_gen`; a fire that was already queued when the timer was
|
|
86
|
+
// torn down — or superseded by a new run — has a stale gen and MUST NOT mutate
|
|
87
|
+
// state or dispatch on a disposed element. _clearTimer() removes the handle, but
|
|
88
|
+
// the guard is the belt-and-braces defense for a callback already in the queue.
|
|
89
|
+
_gen = 0;
|
|
90
|
+
// Generation captured for the currently scheduled run, set at each async start
|
|
91
|
+
// (start()/resume()). A scheduled callback compares it against the live `_gen`;
|
|
92
|
+
// a stale callback (dispose() bumped `_gen` after this run was armed) bails.
|
|
93
|
+
_runGen = 0;
|
|
94
|
+
// SSR (§3.8): the timer does no asynchronous probe, so readiness is immediate.
|
|
95
|
+
_ready = Promise.resolve();
|
|
83
96
|
_tick = 0;
|
|
84
97
|
_running = false;
|
|
85
98
|
_paused = false;
|
|
@@ -113,6 +126,23 @@ class TimerCore extends EventTarget {
|
|
|
113
126
|
get running() {
|
|
114
127
|
return this._running;
|
|
115
128
|
}
|
|
129
|
+
// SSR readiness (§3.8): resolves after the first probe. The timer is
|
|
130
|
+
// command-driven with nothing to probe, so this is an already-resolved promise.
|
|
131
|
+
get ready() {
|
|
132
|
+
return this._ready;
|
|
133
|
+
}
|
|
134
|
+
// Lifecycle (§3.5). The timer is command-driven (start/stop/...) with no
|
|
135
|
+
// ambient subscription to establish, so observe() is an idempotent no-op that
|
|
136
|
+
// resolves once ready (mirroring UploadCore). dispose() tears the timer down —
|
|
137
|
+
// it stops any running interval and bumps the generation so a callback already
|
|
138
|
+
// queued cannot fire onto a torn-down element.
|
|
139
|
+
observe() {
|
|
140
|
+
return this._ready;
|
|
141
|
+
}
|
|
142
|
+
dispose() {
|
|
143
|
+
this._gen++;
|
|
144
|
+
this.stop();
|
|
145
|
+
}
|
|
116
146
|
// --- State setters with event dispatch ---
|
|
117
147
|
_dispatchTick() {
|
|
118
148
|
this._target.dispatchEvent(new CustomEvent("wcs-timer:tick", {
|
|
@@ -163,6 +193,9 @@ class TimerCore extends EventTarget {
|
|
|
163
193
|
// Baseline this run's per-run repeat counting (set after _setRunning so a
|
|
164
194
|
// re-start of a completed bounded timer fires the full N ticks again).
|
|
165
195
|
this._runStartTick = this._tick;
|
|
196
|
+
// Capture this run's generation (§3.4): a scheduled fire that outlives a
|
|
197
|
+
// dispose() (which bumps `_gen`) is then recognised as stale and ignored.
|
|
198
|
+
this._runGen = ++this._gen;
|
|
166
199
|
// Fire immediately on start when requested. _fire() may stop the timer (when
|
|
167
200
|
// repeat is reached), so re-check _running before scheduling the interval.
|
|
168
201
|
if (immediate) {
|
|
@@ -228,6 +261,9 @@ class TimerCore extends EventTarget {
|
|
|
228
261
|
this._paused = false;
|
|
229
262
|
this._setRunning(true);
|
|
230
263
|
this._segmentStart = Date.now();
|
|
264
|
+
// New scheduled run after a pause: capture its generation (§3.4) so a
|
|
265
|
+
// post-dispose() boundary/interval callback is recognised as stale.
|
|
266
|
+
this._runGen = ++this._gen;
|
|
231
267
|
// Invariant: `_interval` is fixed at start() and stays constant for the whole
|
|
232
268
|
// pause/resume cycle — changeInterval() only mutates it while *running* (never
|
|
233
269
|
// while paused), so the remainder arithmetic below can safely assume the same
|
|
@@ -246,6 +282,10 @@ class TimerCore extends EventTarget {
|
|
|
246
282
|
}
|
|
247
283
|
// --- Internal ---
|
|
248
284
|
_onResumeBoundary = () => {
|
|
285
|
+
// Stale-run guard (§3.4): a resume-boundary timeout that fires after dispose()
|
|
286
|
+
// belongs to a torn-down run — drop it without re-arming the interval.
|
|
287
|
+
if (this._runGen !== this._gen)
|
|
288
|
+
return;
|
|
249
289
|
this._timerId = null;
|
|
250
290
|
this._fire();
|
|
251
291
|
// _fire() may have auto-stopped the timer (repeat reached); only re-arm the
|
|
@@ -255,6 +295,10 @@ class TimerCore extends EventTarget {
|
|
|
255
295
|
}
|
|
256
296
|
};
|
|
257
297
|
_fire = () => {
|
|
298
|
+
// Stale-run guard (§3.4): an interval callback queued before dispose() (which
|
|
299
|
+
// bumps `_gen`) must not tick or dispatch onto a torn-down element.
|
|
300
|
+
if (this._runGen !== this._gen)
|
|
301
|
+
return;
|
|
258
302
|
this._tick++;
|
|
259
303
|
this._dispatchTick();
|
|
260
304
|
// Auto-stop once this run has fired the requested number of ticks (repeat=0
|
|
@@ -325,7 +369,7 @@ function registerAutoTrigger() {
|
|
|
325
369
|
}
|
|
326
370
|
|
|
327
371
|
class Timer extends HTMLElement {
|
|
328
|
-
static hasConnectedCallbackPromise =
|
|
372
|
+
static hasConnectedCallbackPromise = true;
|
|
329
373
|
static wcBindable = {
|
|
330
374
|
...TimerCore.wcBindable,
|
|
331
375
|
properties: [
|
|
@@ -350,10 +394,18 @@ class Timer extends HTMLElement {
|
|
|
350
394
|
static get observedAttributes() { return ["interval"]; }
|
|
351
395
|
_core;
|
|
352
396
|
_trigger = false;
|
|
397
|
+
_connectedCallbackPromise = Promise.resolve();
|
|
353
398
|
constructor() {
|
|
354
399
|
super();
|
|
355
400
|
this._core = new TimerCore(this);
|
|
356
401
|
}
|
|
402
|
+
// SSR (§4.1/§4.4): the Shell exposes the Core's readiness so a server-side
|
|
403
|
+
// renderer can await the connect-time probe before snapshotting. The timer has
|
|
404
|
+
// no async probe (observe() resolves immediately), but the contract is uniform
|
|
405
|
+
// across IO nodes.
|
|
406
|
+
get connectedCallbackPromise() {
|
|
407
|
+
return this._connectedCallbackPromise;
|
|
408
|
+
}
|
|
357
409
|
// --- Attribute accessors ---
|
|
358
410
|
get interval() {
|
|
359
411
|
const attr = this.getAttribute("interval");
|
|
@@ -496,12 +548,17 @@ class Timer extends HTMLElement {
|
|
|
496
548
|
if (config.autoTrigger) {
|
|
497
549
|
registerAutoTrigger();
|
|
498
550
|
}
|
|
551
|
+
// Establish monitoring (§3.5). observe() is a no-op that resolves once ready;
|
|
552
|
+
// expose it as connectedCallbackPromise for SSR.
|
|
553
|
+
this._connectedCallbackPromise = this._core.observe();
|
|
499
554
|
if (!this.manual) {
|
|
500
555
|
this.start();
|
|
501
556
|
}
|
|
502
557
|
}
|
|
503
558
|
disconnectedCallback() {
|
|
504
|
-
|
|
559
|
+
// dispose() stops the timer and bumps the generation so a callback already
|
|
560
|
+
// queued cannot fire onto a disconnected element (§3.5 / §4.4).
|
|
561
|
+
this._core.dispose();
|
|
505
562
|
}
|
|
506
563
|
}
|
|
507
564
|
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/TimerCore.ts","../src/autoTrigger.ts","../src/components/Timer.ts","../src/registerComponents.ts","../src/bootstrapTimer.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n timer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-timertarget\",\n tagNames: {\n timer: \"wcs-timer\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal-only live handle to the mutable config. NOT part of the public API\n// (deliberately absent from exports.ts) — it is exported solely so sibling\n// modules in this package can read current settings cheaply. External consumers\n// must use getConfig() (returns a deep-frozen snapshot) / setConfig(). Mutating\n// this object directly bypasses the frozenConfig cache and is unsupported.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable } from \"../types.js\";\n\nexport interface TimerStartOptions {\n interval?: number;\n repeat?: number;\n immediate?: boolean;\n}\n\n/**\n * Headless timer primitive. A thin, framework-agnostic wrapper around\n * `setInterval` exposed through the wc-bindable protocol: it streams `tick`\n * (a monotonically increasing counter), `elapsed` (running time in ms) and a\n * `running` flag, and is driven by the `start` / `stop` / `reset` / `pause` /\n * `resume` commands.\n *\n * `tick` and `elapsed` are both surfaced via the single `wcs-timer:tick` event\n * (read through getters, mirroring how FetchCore exposes value/status from one\n * `wcs-fetch:response` event), so an observer that binds either property is\n * notified on every fire.\n */\nexport class TimerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"tick\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.count },\n { name: \"elapsed\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.elapsed },\n { name: \"running\", event: \"wcs-timer:running-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"reset\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n private _timerId: ReturnType<typeof setInterval> | null = null;\n\n private _tick: number = 0;\n private _running: boolean = false;\n private _paused: boolean = false;\n\n // `_tick` value captured at the start of the current run. `repeat` counts ticks\n // *per run*, so the stop condition compares against this baseline rather than the\n // cumulative `_tick` (which only resets on reset()). Without it, re-starting a\n // completed bounded timer would stop after a single tick.\n private _runStartTick: number = 0;\n\n // Timer configuration (captured on start, reused by pause/resume).\n // `_immediate` is intentionally NOT a field: it is per-run intent consumed\n // entirely within start() (fire once, then schedule), so it lives as a local\n // there rather than lingering as instance state no other method reads.\n private _interval: number = 1000;\n private _repeat: number = 0; // 0 = unlimited\n\n // Elapsed-time bookkeeping. `_accumulatedElapsed` holds the time folded from\n // already-finished running segments; `_segmentStart` is the timestamp the\n // current running segment began (null when not running). The live elapsed is\n // the sum of the two — see _currentElapsed().\n private _accumulatedElapsed: number = 0;\n private _segmentStart: number | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get tick(): number {\n return this._tick;\n }\n\n get elapsed(): number {\n return this._currentElapsed();\n }\n\n get running(): boolean {\n return this._running;\n }\n\n // --- State setters with event dispatch ---\n\n private _dispatchTick(): void {\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:tick\", {\n detail: { count: this._tick, elapsed: this._currentElapsed() },\n bubbles: true,\n }));\n }\n\n private _setRunning(running: boolean): void {\n if (this._running === running) return;\n this._running = running;\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:running-changed\", {\n detail: running,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n start(options: TimerStartOptions = {}): void {\n // Idempotent while running: a redundant start() must not stack a second\n // setInterval (which would leak and double the tick rate). Reconfiguring an\n // active timer is done via stop() + start().\n if (this._running) return;\n\n // start() begins a fresh running segment, so clear any lingering pause from a\n // prior pause()-without-resume(). Without this, the timer would run while\n // _paused stayed true, leaving pause() a no-op and letting resume() overwrite\n // the live timer handle (leak + double fire).\n this._paused = false;\n\n // `interval` is persistent configuration: a non-positive / non-finite value\n // (or an omitted option) keeps the previous interval (default 1000ms). The\n // guard rejects values that would turn setInterval into a hot loop and make\n // resume()'s `accumulated % interval` arithmetic produce NaN. The Shell already\n // falls back to 1000 for invalid attributes; this is the backstop for direct\n // Core API callers.\n if (typeof options.interval === \"number\" && Number.isFinite(options.interval) && options.interval > 0) {\n this._interval = options.interval;\n }\n\n // `repeat` / `immediate` are per-run intent, NOT persistent configuration:\n // every start() re-establishes them from the options, defaulting to\n // \"unlimited\" / \"no immediate fire\" when omitted. This keeps a bare start()\n // after a bounded or one-shot run from silently inheriting the old bounds.\n // `repeat` is a field (pause/resume/_fire read it across the run); `immediate`\n // is consumed here and now, so it stays a local.\n this._repeat = (typeof options.repeat === \"number\" && options.repeat > 0) ? options.repeat : 0;\n const immediate = options.immediate === true;\n\n this._setRunning(true);\n this._segmentStart = Date.now();\n // Baseline this run's per-run repeat counting (set after _setRunning so a\n // re-start of a completed bounded timer fires the full N ticks again).\n this._runStartTick = this._tick;\n\n // Fire immediately on start when requested. _fire() may stop the timer (when\n // repeat is reached), so re-check _running before scheduling the interval.\n if (immediate) {\n this._fire();\n }\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n }\n\n // Swap the tick period of a live timer in place, WITHOUT re-running start().\n // Unlike stop() + start(), this leaves the per-run repeat progress in flight\n // (`_repeat` and its `_runStartTick` baseline) untouched, so a bounded\n // `repeat=\"N\"` run is not re-baselined to fire N more times. It also never goes\n // through start()'s `immediate` path, so an `immediate` timer does not fire an\n // extra tick. Only re-arms the steady interval; pause()/resume() and reset() are\n // unaffected. No-op when not running (interval is then plain config, captured on\n // the next start) or when the new period is non-positive / non-finite (which\n // would turn setInterval into a hot loop and break resume()'s modulo arithmetic).\n changeInterval(interval: number): void {\n if (!this._running) return;\n if (!(typeof interval === \"number\" && Number.isFinite(interval) && interval > 0)) return;\n if (interval === this._interval) return;\n this._interval = interval;\n // Re-arm the steady ticking at the new period. The current period's progress\n // is intentionally discarded (the next tick is a full new interval away),\n // matching the boundary reset of the previous stop()+start() behaviour.\n this._clearTimer();\n this._timerId = setInterval(this._fire, this._interval);\n }\n\n stop(): void {\n this._clearTimer();\n this._foldElapsed();\n this._paused = false;\n this._setRunning(false);\n }\n\n reset(): void {\n this._clearTimer();\n this._paused = false;\n this._tick = 0;\n this._accumulatedElapsed = 0;\n this._segmentStart = null;\n this._setRunning(false);\n // Notify observers that the counter/elapsed have returned to zero.\n this._dispatchTick();\n }\n\n pause(): void {\n // Pause only a live timer; a no-op otherwise so it composes safely with the\n // declarative lifecycle. Unlike stop(), it records `_paused` so resume() can\n // tell an intentional pause from a full stop.\n if (!this._running || this._paused) return;\n this._clearTimer();\n this._foldElapsed();\n this._paused = true;\n this._setRunning(false);\n }\n\n resume(): void {\n if (!this._paused) return;\n this._paused = false;\n this._setRunning(true);\n this._segmentStart = Date.now();\n // Invariant: `_interval` is fixed at start() and stays constant for the whole\n // pause/resume cycle — changeInterval() only mutates it while *running* (never\n // while paused), so the remainder arithmetic below can safely assume the same\n // period was in effect across the paused segment. Consequence for the Shell:\n // because the live interval-attribute path (attributeChangedCallback ->\n // changeInterval) is gated on `running`, an `interval` change made *while\n // paused* is silently not applied here; it is picked up only on the next\n // start() as plain config. This is by design — see README \"Commands\".\n // Resume seamlessly: a tick fires every `interval` ms of *running* time, so\n // honour the partial period consumed before the pause. Wait only the\n // remainder to the next boundary, then fall back to the steady interval.\n // (`accumulated % interval === 0` — paused exactly on a boundary — yields a\n // full interval, which is correct: the next tick is a whole period away.)\n const remainder = this._interval - (this._accumulatedElapsed % this._interval);\n this._timerId = setTimeout(this._onResumeBoundary, remainder);\n }\n\n // --- Internal ---\n\n private _onResumeBoundary = (): void => {\n this._timerId = null;\n this._fire();\n // _fire() may have auto-stopped the timer (repeat reached); only re-arm the\n // steady interval while still running.\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n };\n\n private _fire = (): void => {\n this._tick++;\n this._dispatchTick();\n\n // Auto-stop once this run has fired the requested number of ticks (repeat=0\n // runs forever). Counted per-run via `_runStartTick`, so a re-start after a\n // completed bounded run fires N ticks again. `once` is expressed by the Shell\n // as repeat=1.\n if (this._repeat > 0 && (this._tick - this._runStartTick) >= this._repeat) {\n this._clearTimer();\n this._foldElapsed();\n this._setRunning(false);\n }\n };\n\n private _clearTimer(): void {\n if (this._timerId !== null) {\n // `_timerId` may hold a setInterval handle (steady ticking) or a setTimeout\n // handle (the resume remainder). Clear both — per the HTML spec timers\n // share one list and clearTimeout/clearInterval each remove the entry\n // regardless of which call created it.\n clearTimeout(this._timerId);\n clearInterval(this._timerId);\n this._timerId = null;\n }\n }\n\n private _foldElapsed(): void {\n if (this._segmentStart !== null) {\n this._accumulatedElapsed += Date.now() - this._segmentStart;\n this._segmentStart = null;\n }\n }\n\n private _currentElapsed(): number {\n return this._accumulatedElapsed +\n (this._segmentStart !== null ? Date.now() - this._segmentStart : 0);\n }\n}\n","import { config } from \"./config.js\";\nimport type { Timer } from \"./components/Timer.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const timerId = triggerElement.getAttribute(config.triggerAttribute);\n if (!timerId) return;\n\n // Resolve the registered constructor at call time instead of importing Timer\n // as a value. The value import created a components/Timer.ts ⇄ autoTrigger.ts\n // cycle (Timer.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the exact same identity guarantee\n // — only the registered <wcs-timer> class matches — without the import cycle.\n const TimerCtor = customElements.get(config.tagNames.timer);\n const timerElement = document.getElementById(timerId);\n if (!TimerCtor || !(timerElement instanceof TimerCtor)) return;\n\n // Suppress the element's default action so a timer can start without\n // navigating. Intentional: do not attach data-timertarget to an element whose\n // default action you also want (real <a href> link, form-submit button) — it\n // will be cancelled. See README \"Optional DOM Triggering\".\n event.preventDefault();\n (timerElement as Timer).start();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable } from \"../types.js\";\nimport { TimerCore } from \"../core/TimerCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Timer extends HTMLElement {\n static hasConnectedCallbackPromise = false;\n static wcBindable: IWcBindable = {\n ...TimerCore.wcBindable,\n properties: [\n ...TimerCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-timer:trigger-changed\" },\n ],\n // Shell-level settable surface. `attribute` is a purely descriptive hint\n // (per SPEC-extensions.md the binding core does not act on it) naming the\n // mirrored HTML attribute, matching <wcs-geo> / <wcs-debounce>. `trigger` is a\n // momentary command-property with no backing attribute, so it carries no hint\n // (same as those packages). `start` / `stop` / `reset` / `pause` / `resume`\n // commands are inherited from the Core above.\n inputs: [\n { name: \"interval\", attribute: \"interval\" },\n { name: \"once\", attribute: \"once\" },\n { name: \"repeat\", attribute: \"repeat\" },\n { name: \"immediate\", attribute: \"immediate\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"interval\"]; }\n\n private _core: TimerCore;\n private _trigger: boolean = false;\n\n constructor() {\n super();\n this._core = new TimerCore(this);\n }\n\n // --- Attribute accessors ---\n\n get interval(): number {\n const attr = this.getAttribute(\"interval\");\n if (attr === null || attr.trim() === \"\") return 1000;\n // Strict parse via Number() (unlike parseInt, \"100px\" -> NaN, not 100),\n // matching <wcs-geo> / <wcs-debounce>. Fall back to the 1000ms default for any\n // invalid period — not only NaN but also 0 / negative values, which would\n // otherwise reach setInterval as a hot loop and break resume()'s modulo\n // arithmetic in the Core.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 1000;\n }\n\n set interval(value: number) {\n this.setAttribute(\"interval\", String(value));\n }\n\n get once(): boolean {\n return this.hasAttribute(\"once\");\n }\n\n set once(value: boolean) {\n if (value) {\n this.setAttribute(\"once\", \"\");\n } else {\n this.removeAttribute(\"once\");\n }\n }\n\n get repeat(): number {\n const attr = this.getAttribute(\"repeat\");\n if (attr === null || attr.trim() === \"\") return 0;\n // Strict parse via Number() (\"3px\" -> NaN, not 3), matching <wcs-geo> /\n // <wcs-debounce>. Normalise any non-positive / non-numeric value to 0\n // (= unlimited), mirroring the `interval` getter. Without this a negative\n // `repeat=\"-3\"` would leak through to start(); harmless today (Core treats\n // `_repeat <= 0` as unlimited) but the asymmetry is a trap.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 0;\n }\n\n set repeat(value: number) {\n this.setAttribute(\"repeat\", String(value));\n }\n\n get immediate(): boolean {\n return this.hasAttribute(\"immediate\");\n }\n\n set immediate(value: boolean) {\n if (value) {\n this.setAttribute(\"immediate\", \"\");\n } else {\n this.removeAttribute(\"immediate\");\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get tick(): number {\n return this._core.tick;\n }\n\n get elapsed(): number {\n return this._core.elapsed;\n }\n\n get running(): boolean {\n return this._core.running;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts the timer. Mirrors\n // the trigger flag on <wcs-fetch> / <wcs-ws>. Prefer the command-token\n // protocol (`command.start: $command.tick`) for state-driven starts; this\n // exists mainly for the DOM click trigger and simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n // The `trigger-changed` event reports the momentary flag returning to\n // false, i.e. that the trigger property *changed* — it is deliberately not\n // gated on whether start() actually began a new run. This keeps the\n // wcBindable `trigger` property's change-notification semantics consistent\n // (every false→true write produces exactly one change-back event), even\n // when start() was a no-op because the timer was already running.\n this.dispatchEvent(new CustomEvent(\"wcs-timer:trigger-changed\", {\n detail: false,\n bubbles: true,\n }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n // `once` is sugar for \"fire exactly one tick\": map it to repeat=1, but let an\n // explicit repeat attribute win when both are present.\n const repeat = this.repeat > 0 ? this.repeat : (this.once ? 1 : 0);\n this._core.start({\n interval: this.interval,\n repeat,\n immediate: this.immediate,\n });\n }\n\n stop(): void {\n this._core.stop();\n }\n\n reset(): void {\n this._core.reset();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n // Live interval changes swap the underlying setInterval period in place.\n // `tick` / `elapsed` and the per-run `repeat` progress are preserved — we\n // deliberately do NOT stop()+start(), which would re-run start() and\n // re-evaluate per-run options (re-firing `immediate` and re-baselining\n // `repeat`). `running` alone gates this: swapping the period is orthogonal to\n // *how* the timer was started, so a `manual` timer the user has explicitly\n // started gets live period changes too. A non-running timer needs no swap —\n // its next start() picks up the new attribute as plain config.\n if (name === \"interval\" && oldValue !== newValue && this.isConnected && this.running) {\n this._core.changeInterval(this.interval);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual) {\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this._core.stop();\n }\n}\n","import { Timer } from \"./components/Timer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.timer)) {\n customElements.define(config.tagNames.timer, Timer);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapTimer(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"AAUA,MAAM,OAAO,GAAoB;AAC/B,IAAA,WAAW,EAAE,IAAI;AACjB,IAAA,gBAAgB,EAAE,kBAAkB;AACpC,IAAA,QAAQ,EAAE;AACR,QAAA,KAAK,EAAE,WAAW;AACnB,KAAA;CACF;AAED,SAAS,UAAU,CAAI,GAAM,EAAA;AAC3B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;AACvD,IAAA,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;IAClB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AAClC,QAAA,UAAU,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IACnD;AACA,IAAA,OAAO,GAAG;AACZ;AAEA,SAAS,SAAS,CAAI,GAAM,EAAA;AAC1B,IAAA,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;AAAE,QAAA,OAAO,GAAG;IACvD,MAAM,KAAK,GAA4B,EAAE;IACzC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;QAClC,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAE,GAA+B,CAAC,GAAG,CAAC,CAAC;IAC/D;AACA,IAAA,OAAO,KAAU;AACnB;AAEA,IAAI,YAAY,GAAmB,IAAI;AAEvC;AACA;AACA;AACA;AACA;AACO,MAAM,MAAM,GAAY,OAAkB;SAEjC,SAAS,GAAA;IACvB,IAAI,CAAC,YAAY,EAAE;QACjB,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC/C;AACA,IAAA,OAAO,YAAY;AACrB;AAEM,SAAU,SAAS,CAAC,aAA8B,EAAA;AACtD,IAAA,IAAI,OAAO,aAAa,CAAC,WAAW,KAAK,SAAS,EAAE;AAClD,QAAA,OAAO,CAAC,WAAW,GAAG,aAAa,CAAC,WAAW;IACjD;AACA,IAAA,IAAI,OAAO,aAAa,CAAC,gBAAgB,KAAK,QAAQ,EAAE;AACtD,QAAA,OAAO,CAAC,gBAAgB,GAAG,aAAa,CAAC,gBAAgB;IAC3D;AACA,IAAA,IAAI,aAAa,CAAC,QAAQ,EAAE;QAC1B,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC;IACzD;IACA,YAAY,GAAG,IAAI;AACrB;;ACvDA;;;;;;;;;;;AAWG;AACG,MAAO,SAAU,SAAQ,WAAW,CAAA;IACxC,OAAO,UAAU,GAAgB;AAC/B,QAAA,QAAQ,EAAE,aAAa;AACvB,QAAA,OAAO,EAAE,CAAC;AACV,QAAA,UAAU,EAAE;YACV,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAQ,KAAM,CAAiB,CAAC,MAAM,CAAC,KAAK,EAAE;YAChG,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAQ,KAAM,CAAiB,CAAC,MAAM,CAAC,OAAO,EAAE;AACrG,YAAA,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,2BAA2B,EAAE;AACxD,SAAA;AACD,QAAA,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,OAAO,EAAE;YACjB,EAAE,IAAI,EAAE,MAAM,EAAE;YAChB,EAAE,IAAI,EAAE,OAAO,EAAE;YACjB,EAAE,IAAI,EAAE,OAAO,EAAE;YACjB,EAAE,IAAI,EAAE,QAAQ,EAAE;AACnB,SAAA;KACF;AAEO,IAAA,OAAO;IACP,QAAQ,GAA0C,IAAI;IAEtD,KAAK,GAAW,CAAC;IACjB,QAAQ,GAAY,KAAK;IACzB,OAAO,GAAY,KAAK;;;;;IAMxB,aAAa,GAAW,CAAC;;;;;IAMzB,SAAS,GAAW,IAAI;AACxB,IAAA,OAAO,GAAW,CAAC,CAAC;;;;;IAMpB,mBAAmB,GAAW,CAAC;IAC/B,aAAa,GAAkB,IAAI;AAE3C,IAAA,WAAA,CAAY,MAAoB,EAAA;AAC9B,QAAA,KAAK,EAAE;AACP,QAAA,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,IAAI;IAC/B;AAEA,IAAA,IAAI,IAAI,GAAA;QACN,OAAO,IAAI,CAAC,KAAK;IACnB;AAEA,IAAA,IAAI,OAAO,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,eAAe,EAAE;IAC/B;AAEA,IAAA,IAAI,OAAO,GAAA;QACT,OAAO,IAAI,CAAC,QAAQ;IACtB;;IAIQ,aAAa,GAAA;QACnB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,gBAAgB,EAAE;AAC3D,YAAA,MAAM,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,EAAE;AAC9D,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;AAEQ,IAAA,WAAW,CAAC,OAAgB,EAAA;AAClC,QAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO;YAAE;AAC/B,QAAA,IAAI,CAAC,QAAQ,GAAG,OAAO;QACvB,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,2BAA2B,EAAE;AACtE,YAAA,MAAM,EAAE,OAAO;AACf,YAAA,OAAO,EAAE,IAAI;AACd,SAAA,CAAC,CAAC;IACL;;IAIA,KAAK,CAAC,UAA6B,EAAE,EAAA;;;;QAInC,IAAI,IAAI,CAAC,QAAQ;YAAE;;;;;AAMnB,QAAA,IAAI,CAAC,OAAO,GAAG,KAAK;;;;;;;QAQpB,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,GAAG,CAAC,EAAE;AACrG,YAAA,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,QAAQ;QACnC;;;;;;;QAQA,IAAI,CAAC,OAAO,GAAG,CAAC,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;AAC9F,QAAA,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,KAAK,IAAI;AAE5C,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE;;;AAG/B,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,KAAK;;;QAI/B,IAAI,SAAS,EAAE;YACb,IAAI,CAAC,KAAK,EAAE;QACd;AACA,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AACjB,YAAA,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC;QACzD;IACF;;;;;;;;;;AAWA,IAAA,cAAc,CAAC,QAAgB,EAAA;QAC7B,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE;AACpB,QAAA,IAAI,EAAE,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC;YAAE;AAClF,QAAA,IAAI,QAAQ,KAAK,IAAI,CAAC,SAAS;YAAE;AACjC,QAAA,IAAI,CAAC,SAAS,GAAG,QAAQ;;;;QAIzB,IAAI,CAAC,WAAW,EAAE;AAClB,QAAA,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC;IACzD;IAEA,IAAI,GAAA;QACF,IAAI,CAAC,WAAW,EAAE;QAClB,IAAI,CAAC,YAAY,EAAE;AACnB,QAAA,IAAI,CAAC,OAAO,GAAG,KAAK;AACpB,QAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;IACzB;IAEA,KAAK,GAAA;QACH,IAAI,CAAC,WAAW,EAAE;AAClB,QAAA,IAAI,CAAC,OAAO,GAAG,KAAK;AACpB,QAAA,IAAI,CAAC,KAAK,GAAG,CAAC;AACd,QAAA,IAAI,CAAC,mBAAmB,GAAG,CAAC;AAC5B,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI;AACzB,QAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;;QAEvB,IAAI,CAAC,aAAa,EAAE;IACtB;IAEA,KAAK,GAAA;;;;AAIH,QAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO;YAAE;QACpC,IAAI,CAAC,WAAW,EAAE;QAClB,IAAI,CAAC,YAAY,EAAE;AACnB,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI;AACnB,QAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;IACzB;IAEA,MAAM,GAAA;QACJ,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE;AACnB,QAAA,IAAI,CAAC,OAAO,GAAG,KAAK;AACpB,QAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;AACtB,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE;;;;;;;;;;;;;;AAc/B,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,SAAS,CAAC;QAC9E,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC;IAC/D;;IAIQ,iBAAiB,GAAG,MAAW;AACrC,QAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;QACpB,IAAI,CAAC,KAAK,EAAE;;;AAGZ,QAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AACjB,YAAA,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC;QACzD;AACF,IAAA,CAAC;IAEO,KAAK,GAAG,MAAW;QACzB,IAAI,CAAC,KAAK,EAAE;QACZ,IAAI,CAAC,aAAa,EAAE;;;;;QAMpB,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,KAAK,IAAI,CAAC,OAAO,EAAE;YACzE,IAAI,CAAC,WAAW,EAAE;YAClB,IAAI,CAAC,YAAY,EAAE;AACnB,YAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;QACzB;AACF,IAAA,CAAC;IAEO,WAAW,GAAA;AACjB,QAAA,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE;;;;;AAK1B,YAAA,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC3B,YAAA,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC5B,YAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;QACtB;IACF;IAEQ,YAAY,GAAA;AAClB,QAAA,IAAI,IAAI,CAAC,aAAa,KAAK,IAAI,EAAE;YAC/B,IAAI,CAAC,mBAAmB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa;AAC3D,YAAA,IAAI,CAAC,aAAa,GAAG,IAAI;QAC3B;IACF;IAEQ,eAAe,GAAA;QACrB,OAAO,IAAI,CAAC,mBAAmB;aAC5B,IAAI,CAAC,aAAa,KAAK,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,CAAC,CAAC;IACvE;;;AC3QF,IAAI,UAAU,GAAG,KAAK;AAEtB,SAAS,WAAW,CAAC,KAAY,EAAA;AAC/B,IAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM;AAC3B,IAAA,IAAI,EAAE,MAAM,YAAY,OAAO,CAAC;QAAE;AAElC,IAAA,MAAM,cAAc,GAAG,MAAM,CAAC,OAAO,CAAU,CAAA,CAAA,EAAI,MAAM,CAAC,gBAAgB,CAAA,CAAA,CAAG,CAAC;AAC9E,IAAA,IAAI,CAAC,cAAc;QAAE;IAErB,MAAM,OAAO,GAAG,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,gBAAgB,CAAC;AACpE,IAAA,IAAI,CAAC,OAAO;QAAE;;;;;;AAOd,IAAA,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;IAC3D,MAAM,YAAY,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC;IACrD,IAAI,CAAC,SAAS,IAAI,EAAE,YAAY,YAAY,SAAS,CAAC;QAAE;;;;;IAMxD,KAAK,CAAC,cAAc,EAAE;IACrB,YAAsB,CAAC,KAAK,EAAE;AACjC;SAEgB,mBAAmB,GAAA;AACjC,IAAA,IAAI,UAAU;QAAE;IAChB,UAAU,GAAG,IAAI;AACjB,IAAA,QAAQ,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC;AACjD;;AC/BM,MAAO,KAAM,SAAQ,WAAW,CAAA;AACpC,IAAA,OAAO,2BAA2B,GAAG,KAAK;IAC1C,OAAO,UAAU,GAAgB;QAC/B,GAAG,SAAS,CAAC,UAAU;AACvB,QAAA,UAAU,EAAE;AACV,YAAA,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU;AAClC,YAAA,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,2BAA2B,EAAE;AACxD,SAAA;;;;;;;AAOD,QAAA,MAAM,EAAE;AACN,YAAA,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE;AAC3C,YAAA,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE;AACnC,YAAA,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE;AACvC,YAAA,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE;AAC7C,YAAA,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE;YACvC,EAAE,IAAI,EAAE,SAAS,EAAE;AACpB,SAAA;KACF;IACD,WAAW,kBAAkB,GAAA,EAAe,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;AAEzD,IAAA,KAAK;IACL,QAAQ,GAAY,KAAK;AAEjC,IAAA,WAAA,GAAA;AACE,QAAA,KAAK,EAAE;QACP,IAAI,CAAC,KAAK,GAAG,IAAI,SAAS,CAAC,IAAI,CAAC;IAClC;;AAIA,IAAA,IAAI,QAAQ,GAAA;QACV,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;QAC1C,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;AAAE,YAAA,OAAO,IAAI;;;;;;AAMpD,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,IAAI;IAChE;IAEA,IAAI,QAAQ,CAAC,KAAa,EAAA;QACxB,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9C;AAEA,IAAA,IAAI,IAAI,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;IAClC;IAEA,IAAI,IAAI,CAAC,KAAc,EAAA;QACrB,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,EAAE,CAAC;QAC/B;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC;QAC9B;IACF;AAEA,IAAA,IAAI,MAAM,GAAA;QACR,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;QACxC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;AAAE,YAAA,OAAO,CAAC;;;;;;AAMjD,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC;IAC7D;IAEA,IAAI,MAAM,CAAC,KAAa,EAAA;QACtB,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5C;AAEA,IAAA,IAAI,SAAS,GAAA;AACX,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC;IACvC;IAEA,IAAI,SAAS,CAAC,KAAc,EAAA;QAC1B,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,CAAC;QACpC;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,WAAW,CAAC;QACnC;IACF;AAEA,IAAA,IAAI,MAAM,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;IACpC;IAEA,IAAI,MAAM,CAAC,KAAc,EAAA;QACvB,IAAI,KAAK,EAAE;AACT,YAAA,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;QACjC;aAAO;AACL,YAAA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC;QAChC;IACF;;AAIA,IAAA,IAAI,IAAI,GAAA;AACN,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI;IACxB;AAEA,IAAA,IAAI,OAAO,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO;IAC3B;AAEA,IAAA,IAAI,OAAO,GAAA;AACT,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO;IAC3B;;AAIA,IAAA,IAAI,OAAO,GAAA;QACT,OAAO,IAAI,CAAC,QAAQ;IACtB;IAEA,IAAI,OAAO,CAAC,KAAc,EAAA;;;;;AAKxB,QAAA,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK;QACjB,IAAI,CAAC,EAAE;AACL,YAAA,IAAI,CAAC,QAAQ,GAAG,IAAI;YACpB,IAAI,CAAC,KAAK,EAAE;AACZ,YAAA,IAAI,CAAC,QAAQ,GAAG,KAAK;;;;;;;AAOrB,YAAA,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,2BAA2B,EAAE;AAC9D,gBAAA,MAAM,EAAE,KAAK;AACb,gBAAA,OAAO,EAAE,IAAI;AACd,aAAA,CAAC,CAAC;QACL;IACF;;IAIA,KAAK,GAAA;;;AAGH,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;AAClE,QAAA,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM;YACN,SAAS,EAAE,IAAI,CAAC,SAAS;AAC1B,SAAA,CAAC;IACJ;IAEA,IAAI,GAAA;AACF,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;IACnB;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;IACpB;IAEA,KAAK,GAAA;AACH,QAAA,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE;IACpB;IAEA,MAAM,GAAA;AACJ,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;IACrB;;AAIA,IAAA,wBAAwB,CAAC,IAAY,EAAE,QAAuB,EAAE,QAAuB,EAAA;;;;;;;;;AASrF,QAAA,IAAI,IAAI,KAAK,UAAU,IAAI,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE;YACpF,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC1C;IACF;IAEA,iBAAiB,GAAA;AACf,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM;AAC3B,QAAA,IAAI,MAAM,CAAC,WAAW,EAAE;AACtB,YAAA,mBAAmB,EAAE;QACvB;AACA,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;YAChB,IAAI,CAAC,KAAK,EAAE;QACd;IACF;IAEA,oBAAoB,GAAA;AAClB,QAAA,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;IACnB;;;SC7Mc,kBAAkB,GAAA;AAChC,IAAA,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;QAC9C,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACrD;AACF;;ACHM,SAAU,cAAc,CAAC,UAA4B,EAAA;IACzD,IAAI,UAAU,EAAE;QACd,SAAS,CAAC,UAAU,CAAC;IACvB;AACA,IAAA,kBAAkB,EAAE;AACtB;;;;"}
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/TimerCore.ts","../src/autoTrigger.ts","../src/components/Timer.ts","../src/registerComponents.ts","../src/bootstrapTimer.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n timer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-timertarget\",\n tagNames: {\n timer: \"wcs-timer\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal-only live handle to the mutable config. NOT part of the public API\n// (deliberately absent from exports.ts) — it is exported solely so sibling\n// modules in this package can read current settings cheaply. External consumers\n// must use getConfig() (returns a deep-frozen snapshot) / setConfig(). Mutating\n// this object directly bypasses the frozenConfig cache and is unsupported.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable } from \"../types.js\";\n\nexport interface TimerStartOptions {\n interval?: number;\n repeat?: number;\n immediate?: boolean;\n}\n\n/**\n * Headless timer primitive. A thin, framework-agnostic wrapper around\n * `setInterval` exposed through the wc-bindable protocol: it streams `tick`\n * (a monotonically increasing counter), `elapsed` (running time in ms) and a\n * `running` flag, and is driven by the `start` / `stop` / `reset` / `pause` /\n * `resume` commands.\n *\n * `tick` and `elapsed` are both surfaced via the single `wcs-timer:tick` event\n * (read through getters, mirroring how FetchCore exposes value/status from one\n * `wcs-fetch:response` event), so an observer that binds either property is\n * notified on every fire.\n */\nexport class TimerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"tick\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.count },\n { name: \"elapsed\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.elapsed },\n { name: \"running\", event: \"wcs-timer:running-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"reset\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n private _timerId: ReturnType<typeof setInterval> | null = null;\n\n // Generation guard (§3.4): bumped on dispose() and at every async start\n // (start()/resume()). A scheduled callback (setInterval/setTimeout) captures the\n // generation live via `_gen`; a fire that was already queued when the timer was\n // torn down — or superseded by a new run — has a stale gen and MUST NOT mutate\n // state or dispatch on a disposed element. _clearTimer() removes the handle, but\n // the guard is the belt-and-braces defense for a callback already in the queue.\n private _gen = 0;\n // Generation captured for the currently scheduled run, set at each async start\n // (start()/resume()). A scheduled callback compares it against the live `_gen`;\n // a stale callback (dispose() bumped `_gen` after this run was armed) bails.\n private _runGen = 0;\n // SSR (§3.8): the timer does no asynchronous probe, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n private _tick: number = 0;\n private _running: boolean = false;\n private _paused: boolean = false;\n\n // `_tick` value captured at the start of the current run. `repeat` counts ticks\n // *per run*, so the stop condition compares against this baseline rather than the\n // cumulative `_tick` (which only resets on reset()). Without it, re-starting a\n // completed bounded timer would stop after a single tick.\n private _runStartTick: number = 0;\n\n // Timer configuration (captured on start, reused by pause/resume).\n // `_immediate` is intentionally NOT a field: it is per-run intent consumed\n // entirely within start() (fire once, then schedule), so it lives as a local\n // there rather than lingering as instance state no other method reads.\n private _interval: number = 1000;\n private _repeat: number = 0; // 0 = unlimited\n\n // Elapsed-time bookkeeping. `_accumulatedElapsed` holds the time folded from\n // already-finished running segments; `_segmentStart` is the timestamp the\n // current running segment began (null when not running). The live elapsed is\n // the sum of the two — see _currentElapsed().\n private _accumulatedElapsed: number = 0;\n private _segmentStart: number | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get tick(): number {\n return this._tick;\n }\n\n get elapsed(): number {\n return this._currentElapsed();\n }\n\n get running(): boolean {\n return this._running;\n }\n\n // SSR readiness (§3.8): resolves after the first probe. The timer is\n // command-driven with nothing to probe, so this is an already-resolved promise.\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // Lifecycle (§3.5). The timer is command-driven (start/stop/...) with no\n // ambient subscription to establish, so observe() is an idempotent no-op that\n // resolves once ready (mirroring UploadCore). dispose() tears the timer down —\n // it stops any running interval and bumps the generation so a callback already\n // queued cannot fire onto a torn-down element.\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n this.stop();\n }\n\n // --- State setters with event dispatch ---\n\n private _dispatchTick(): void {\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:tick\", {\n detail: { count: this._tick, elapsed: this._currentElapsed() },\n bubbles: true,\n }));\n }\n\n private _setRunning(running: boolean): void {\n if (this._running === running) return;\n this._running = running;\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:running-changed\", {\n detail: running,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n start(options: TimerStartOptions = {}): void {\n // Idempotent while running: a redundant start() must not stack a second\n // setInterval (which would leak and double the tick rate). Reconfiguring an\n // active timer is done via stop() + start().\n if (this._running) return;\n\n // start() begins a fresh running segment, so clear any lingering pause from a\n // prior pause()-without-resume(). Without this, the timer would run while\n // _paused stayed true, leaving pause() a no-op and letting resume() overwrite\n // the live timer handle (leak + double fire).\n this._paused = false;\n\n // `interval` is persistent configuration: a non-positive / non-finite value\n // (or an omitted option) keeps the previous interval (default 1000ms). The\n // guard rejects values that would turn setInterval into a hot loop and make\n // resume()'s `accumulated % interval` arithmetic produce NaN. The Shell already\n // falls back to 1000 for invalid attributes; this is the backstop for direct\n // Core API callers.\n if (typeof options.interval === \"number\" && Number.isFinite(options.interval) && options.interval > 0) {\n this._interval = options.interval;\n }\n\n // `repeat` / `immediate` are per-run intent, NOT persistent configuration:\n // every start() re-establishes them from the options, defaulting to\n // \"unlimited\" / \"no immediate fire\" when omitted. This keeps a bare start()\n // after a bounded or one-shot run from silently inheriting the old bounds.\n // `repeat` is a field (pause/resume/_fire read it across the run); `immediate`\n // is consumed here and now, so it stays a local.\n this._repeat = (typeof options.repeat === \"number\" && options.repeat > 0) ? options.repeat : 0;\n const immediate = options.immediate === true;\n\n this._setRunning(true);\n this._segmentStart = Date.now();\n // Baseline this run's per-run repeat counting (set after _setRunning so a\n // re-start of a completed bounded timer fires the full N ticks again).\n this._runStartTick = this._tick;\n // Capture this run's generation (§3.4): a scheduled fire that outlives a\n // dispose() (which bumps `_gen`) is then recognised as stale and ignored.\n this._runGen = ++this._gen;\n\n // Fire immediately on start when requested. _fire() may stop the timer (when\n // repeat is reached), so re-check _running before scheduling the interval.\n if (immediate) {\n this._fire();\n }\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n }\n\n // Swap the tick period of a live timer in place, WITHOUT re-running start().\n // Unlike stop() + start(), this leaves the per-run repeat progress in flight\n // (`_repeat` and its `_runStartTick` baseline) untouched, so a bounded\n // `repeat=\"N\"` run is not re-baselined to fire N more times. It also never goes\n // through start()'s `immediate` path, so an `immediate` timer does not fire an\n // extra tick. Only re-arms the steady interval; pause()/resume() and reset() are\n // unaffected. No-op when not running (interval is then plain config, captured on\n // the next start) or when the new period is non-positive / non-finite (which\n // would turn setInterval into a hot loop and break resume()'s modulo arithmetic).\n changeInterval(interval: number): void {\n if (!this._running) return;\n if (!(typeof interval === \"number\" && Number.isFinite(interval) && interval > 0)) return;\n if (interval === this._interval) return;\n this._interval = interval;\n // Re-arm the steady ticking at the new period. The current period's progress\n // is intentionally discarded (the next tick is a full new interval away),\n // matching the boundary reset of the previous stop()+start() behaviour.\n this._clearTimer();\n this._timerId = setInterval(this._fire, this._interval);\n }\n\n stop(): void {\n this._clearTimer();\n this._foldElapsed();\n this._paused = false;\n this._setRunning(false);\n }\n\n reset(): void {\n this._clearTimer();\n this._paused = false;\n this._tick = 0;\n this._accumulatedElapsed = 0;\n this._segmentStart = null;\n this._setRunning(false);\n // Notify observers that the counter/elapsed have returned to zero.\n this._dispatchTick();\n }\n\n pause(): void {\n // Pause only a live timer; a no-op otherwise so it composes safely with the\n // declarative lifecycle. Unlike stop(), it records `_paused` so resume() can\n // tell an intentional pause from a full stop.\n if (!this._running || this._paused) return;\n this._clearTimer();\n this._foldElapsed();\n this._paused = true;\n this._setRunning(false);\n }\n\n resume(): void {\n if (!this._paused) return;\n this._paused = false;\n this._setRunning(true);\n this._segmentStart = Date.now();\n // New scheduled run after a pause: capture its generation (§3.4) so a\n // post-dispose() boundary/interval callback is recognised as stale.\n this._runGen = ++this._gen;\n // Invariant: `_interval` is fixed at start() and stays constant for the whole\n // pause/resume cycle — changeInterval() only mutates it while *running* (never\n // while paused), so the remainder arithmetic below can safely assume the same\n // period was in effect across the paused segment. Consequence for the Shell:\n // because the live interval-attribute path (attributeChangedCallback ->\n // changeInterval) is gated on `running`, an `interval` change made *while\n // paused* is silently not applied here; it is picked up only on the next\n // start() as plain config. This is by design — see README \"Commands\".\n // Resume seamlessly: a tick fires every `interval` ms of *running* time, so\n // honour the partial period consumed before the pause. Wait only the\n // remainder to the next boundary, then fall back to the steady interval.\n // (`accumulated % interval === 0` — paused exactly on a boundary — yields a\n // full interval, which is correct: the next tick is a whole period away.)\n const remainder = this._interval - (this._accumulatedElapsed % this._interval);\n this._timerId = setTimeout(this._onResumeBoundary, remainder);\n }\n\n // --- Internal ---\n\n private _onResumeBoundary = (): void => {\n // Stale-run guard (§3.4): a resume-boundary timeout that fires after dispose()\n // belongs to a torn-down run — drop it without re-arming the interval.\n if (this._runGen !== this._gen) return;\n this._timerId = null;\n this._fire();\n // _fire() may have auto-stopped the timer (repeat reached); only re-arm the\n // steady interval while still running.\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n };\n\n private _fire = (): void => {\n // Stale-run guard (§3.4): an interval callback queued before dispose() (which\n // bumps `_gen`) must not tick or dispatch onto a torn-down element.\n if (this._runGen !== this._gen) return;\n this._tick++;\n this._dispatchTick();\n\n // Auto-stop once this run has fired the requested number of ticks (repeat=0\n // runs forever). Counted per-run via `_runStartTick`, so a re-start after a\n // completed bounded run fires N ticks again. `once` is expressed by the Shell\n // as repeat=1.\n if (this._repeat > 0 && (this._tick - this._runStartTick) >= this._repeat) {\n this._clearTimer();\n this._foldElapsed();\n this._setRunning(false);\n }\n };\n\n private _clearTimer(): void {\n if (this._timerId !== null) {\n // `_timerId` may hold a setInterval handle (steady ticking) or a setTimeout\n // handle (the resume remainder). Clear both — per the HTML spec timers\n // share one list and clearTimeout/clearInterval each remove the entry\n // regardless of which call created it.\n clearTimeout(this._timerId);\n clearInterval(this._timerId);\n this._timerId = null;\n }\n }\n\n private _foldElapsed(): void {\n if (this._segmentStart !== null) {\n this._accumulatedElapsed += Date.now() - this._segmentStart;\n this._segmentStart = null;\n }\n }\n\n private _currentElapsed(): number {\n return this._accumulatedElapsed +\n (this._segmentStart !== null ? Date.now() - this._segmentStart : 0);\n }\n}\n","import { config } from \"./config.js\";\nimport type { Timer } from \"./components/Timer.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const timerId = triggerElement.getAttribute(config.triggerAttribute);\n if (!timerId) return;\n\n // Resolve the registered constructor at call time instead of importing Timer\n // as a value. The value import created a components/Timer.ts ⇄ autoTrigger.ts\n // cycle (Timer.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the exact same identity guarantee\n // — only the registered <wcs-timer> class matches — without the import cycle.\n const TimerCtor = customElements.get(config.tagNames.timer);\n const timerElement = document.getElementById(timerId);\n if (!TimerCtor || !(timerElement instanceof TimerCtor)) return;\n\n // Suppress the element's default action so a timer can start without\n // navigating. Intentional: do not attach data-timertarget to an element whose\n // default action you also want (real <a href> link, form-submit button) — it\n // will be cancelled. See README \"Optional DOM Triggering\".\n event.preventDefault();\n (timerElement as Timer).start();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable } from \"../types.js\";\nimport { TimerCore } from \"../core/TimerCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Timer extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...TimerCore.wcBindable,\n properties: [\n ...TimerCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-timer:trigger-changed\" },\n ],\n // Shell-level settable surface. `attribute` is a purely descriptive hint\n // (per SPEC-extensions.md the binding core does not act on it) naming the\n // mirrored HTML attribute, matching <wcs-geo> / <wcs-debounce>. `trigger` is a\n // momentary command-property with no backing attribute, so it carries no hint\n // (same as those packages). `start` / `stop` / `reset` / `pause` / `resume`\n // commands are inherited from the Core above.\n inputs: [\n { name: \"interval\", attribute: \"interval\" },\n { name: \"once\", attribute: \"once\" },\n { name: \"repeat\", attribute: \"repeat\" },\n { name: \"immediate\", attribute: \"immediate\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"interval\"]; }\n\n private _core: TimerCore;\n private _trigger: boolean = false;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new TimerCore(this);\n }\n\n // SSR (§4.1/§4.4): the Shell exposes the Core's readiness so a server-side\n // renderer can await the connect-time probe before snapshotting. The timer has\n // no async probe (observe() resolves immediately), but the contract is uniform\n // across IO nodes.\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get interval(): number {\n const attr = this.getAttribute(\"interval\");\n if (attr === null || attr.trim() === \"\") return 1000;\n // Strict parse via Number() (unlike parseInt, \"100px\" -> NaN, not 100),\n // matching <wcs-geo> / <wcs-debounce>. Fall back to the 1000ms default for any\n // invalid period — not only NaN but also 0 / negative values, which would\n // otherwise reach setInterval as a hot loop and break resume()'s modulo\n // arithmetic in the Core.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 1000;\n }\n\n set interval(value: number) {\n this.setAttribute(\"interval\", String(value));\n }\n\n get once(): boolean {\n return this.hasAttribute(\"once\");\n }\n\n set once(value: boolean) {\n if (value) {\n this.setAttribute(\"once\", \"\");\n } else {\n this.removeAttribute(\"once\");\n }\n }\n\n get repeat(): number {\n const attr = this.getAttribute(\"repeat\");\n if (attr === null || attr.trim() === \"\") return 0;\n // Strict parse via Number() (\"3px\" -> NaN, not 3), matching <wcs-geo> /\n // <wcs-debounce>. Normalise any non-positive / non-numeric value to 0\n // (= unlimited), mirroring the `interval` getter. Without this a negative\n // `repeat=\"-3\"` would leak through to start(); harmless today (Core treats\n // `_repeat <= 0` as unlimited) but the asymmetry is a trap.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 0;\n }\n\n set repeat(value: number) {\n this.setAttribute(\"repeat\", String(value));\n }\n\n get immediate(): boolean {\n return this.hasAttribute(\"immediate\");\n }\n\n set immediate(value: boolean) {\n if (value) {\n this.setAttribute(\"immediate\", \"\");\n } else {\n this.removeAttribute(\"immediate\");\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get tick(): number {\n return this._core.tick;\n }\n\n get elapsed(): number {\n return this._core.elapsed;\n }\n\n get running(): boolean {\n return this._core.running;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts the timer. Mirrors\n // the trigger flag on <wcs-fetch> / <wcs-ws>. Prefer the command-token\n // protocol (`command.start: $command.tick`) for state-driven starts; this\n // exists mainly for the DOM click trigger and simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n // The `trigger-changed` event reports the momentary flag returning to\n // false, i.e. that the trigger property *changed* — it is deliberately not\n // gated on whether start() actually began a new run. This keeps the\n // wcBindable `trigger` property's change-notification semantics consistent\n // (every false→true write produces exactly one change-back event), even\n // when start() was a no-op because the timer was already running.\n this.dispatchEvent(new CustomEvent(\"wcs-timer:trigger-changed\", {\n detail: false,\n bubbles: true,\n }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n // `once` is sugar for \"fire exactly one tick\": map it to repeat=1, but let an\n // explicit repeat attribute win when both are present.\n const repeat = this.repeat > 0 ? this.repeat : (this.once ? 1 : 0);\n this._core.start({\n interval: this.interval,\n repeat,\n immediate: this.immediate,\n });\n }\n\n stop(): void {\n this._core.stop();\n }\n\n reset(): void {\n this._core.reset();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n // Live interval changes swap the underlying setInterval period in place.\n // `tick` / `elapsed` and the per-run `repeat` progress are preserved — we\n // deliberately do NOT stop()+start(), which would re-run start() and\n // re-evaluate per-run options (re-firing `immediate` and re-baselining\n // `repeat`). `running` alone gates this: swapping the period is orthogonal to\n // *how* the timer was started, so a `manual` timer the user has explicitly\n // started gets live period changes too. A non-running timer needs no swap —\n // its next start() picks up the new attribute as plain config.\n if (name === \"interval\" && oldValue !== newValue && this.isConnected && this.running) {\n this._core.changeInterval(this.interval);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n // Establish monitoring (§3.5). observe() is a no-op that resolves once ready;\n // expose it as connectedCallbackPromise for SSR.\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual) {\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n // dispose() stops the timer and bumps the generation so a callback already\n // queued cannot fire onto a disconnected element (§3.5 / §4.4).\n this._core.dispose();\n }\n}\n","import { Timer } from \"./components/Timer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.timer)) {\n customElements.define(config.tagNames.timer, Timer);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapTimer(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.esm.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const t={autoTrigger:!0,triggerAttribute:"data-timertarget",tagNames:{timer:"wcs-timer"}};function e(t){if(null===t||"object"!=typeof t)return t;Object.freeze(t);for(const i of Object.keys(t))e(t[i]);return t}function i(t){if(null===t||"object"!=typeof t)return t;const e={};for(const r of Object.keys(t))e[r]=i(t[r]);return e}let r=null;const s=t;function n(){return r||(r=e(i(t))),r}class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"tick",event:"wcs-timer:tick",getter:t=>t.detail.count},{name:"elapsed",event:"wcs-timer:tick",getter:t=>t.detail.elapsed},{name:"running",event:"wcs-timer:running-changed"}],commands:[{name:"start"},{name:"stop"},{name:"reset"},{name:"pause"},{name:"resume"}]};_target;_timerId=null;_tick=0;_running=!1;_paused=!1;_runStartTick=0;_interval=1e3;_repeat=0;_accumulatedElapsed=0;_segmentStart=null;constructor(t){super(),this._target=t??this}get tick(){return this._tick}get elapsed(){return this._currentElapsed()}get running(){return this._running}_dispatchTick(){this._target.dispatchEvent(new CustomEvent("wcs-timer:tick",{detail:{count:this._tick,elapsed:this._currentElapsed()},bubbles:!0}))}_setRunning(t){this._running!==t&&(this._running=t,this._target.dispatchEvent(new CustomEvent("wcs-timer:running-changed",{detail:t,bubbles:!0})))}start(t={}){if(this._running)return;this._paused=!1,"number"==typeof t.interval&&Number.isFinite(t.interval)&&t.interval>0&&(this._interval=t.interval),this._repeat="number"==typeof t.repeat&&t.repeat>0?t.repeat:0;const e=!0===t.immediate;this._setRunning(!0),this._segmentStart=Date.now(),this._runStartTick=this._tick,e&&this._fire(),this._running&&(this._timerId=setInterval(this._fire,this._interval))}changeInterval(t){this._running&&"number"==typeof t&&Number.isFinite(t)&&t>0&&t!==this._interval&&(this._interval=t,this._clearTimer(),this._timerId=setInterval(this._fire,this._interval))}stop(){this._clearTimer(),this._foldElapsed(),this._paused=!1,this._setRunning(!1)}reset(){this._clearTimer(),this._paused=!1,this._tick=0,this._accumulatedElapsed=0,this._segmentStart=null,this._setRunning(!1),this._dispatchTick()}pause(){this._running&&!this._paused&&(this._clearTimer(),this._foldElapsed(),this._paused=!0,this._setRunning(!1))}resume(){if(!this._paused)return;this._paused=!1,this._setRunning(!0),this._segmentStart=Date.now();const t=this._interval-this._accumulatedElapsed%this._interval;this._timerId=setTimeout(this._onResumeBoundary,t)}_onResumeBoundary=()=>{this._timerId=null,this._fire(),this._running&&(this._timerId=setInterval(this._fire,this._interval))};_fire=()=>{this._tick++,this._dispatchTick(),this._repeat>0&&this._tick-this._runStartTick>=this._repeat&&(this._clearTimer(),this._foldElapsed(),this._setRunning(!1))};_clearTimer(){null!==this._timerId&&(clearTimeout(this._timerId),clearInterval(this._timerId),this._timerId=null)}_foldElapsed(){null!==this._segmentStart&&(this._accumulatedElapsed+=Date.now()-this._segmentStart,this._segmentStart=null)}_currentElapsed(){return this._accumulatedElapsed+(null!==this._segmentStart?Date.now()-this._segmentStart:0)}}let u=!1;function c(t){const e=t.target;if(!(e instanceof Element))return;const i=e.closest(`[${s.triggerAttribute}]`);if(!i)return;const r=i.getAttribute(s.triggerAttribute);if(!r)return;const n=customElements.get(s.tagNames.timer),a=document.getElementById(r);n&&a instanceof n&&(t.preventDefault(),a.start())}class h extends HTMLElement{static hasConnectedCallbackPromise=!
|
|
1
|
+
const t={autoTrigger:!0,triggerAttribute:"data-timertarget",tagNames:{timer:"wcs-timer"}};function e(t){if(null===t||"object"!=typeof t)return t;Object.freeze(t);for(const i of Object.keys(t))e(t[i]);return t}function i(t){if(null===t||"object"!=typeof t)return t;const e={};for(const r of Object.keys(t))e[r]=i(t[r]);return e}let r=null;const s=t;function n(){return r||(r=e(i(t))),r}class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"tick",event:"wcs-timer:tick",getter:t=>t.detail.count},{name:"elapsed",event:"wcs-timer:tick",getter:t=>t.detail.elapsed},{name:"running",event:"wcs-timer:running-changed"}],commands:[{name:"start"},{name:"stop"},{name:"reset"},{name:"pause"},{name:"resume"}]};_target;_timerId=null;_gen=0;_runGen=0;_ready=Promise.resolve();_tick=0;_running=!1;_paused=!1;_runStartTick=0;_interval=1e3;_repeat=0;_accumulatedElapsed=0;_segmentStart=null;constructor(t){super(),this._target=t??this}get tick(){return this._tick}get elapsed(){return this._currentElapsed()}get running(){return this._running}get ready(){return this._ready}observe(){return this._ready}dispose(){this._gen++,this.stop()}_dispatchTick(){this._target.dispatchEvent(new CustomEvent("wcs-timer:tick",{detail:{count:this._tick,elapsed:this._currentElapsed()},bubbles:!0}))}_setRunning(t){this._running!==t&&(this._running=t,this._target.dispatchEvent(new CustomEvent("wcs-timer:running-changed",{detail:t,bubbles:!0})))}start(t={}){if(this._running)return;this._paused=!1,"number"==typeof t.interval&&Number.isFinite(t.interval)&&t.interval>0&&(this._interval=t.interval),this._repeat="number"==typeof t.repeat&&t.repeat>0?t.repeat:0;const e=!0===t.immediate;this._setRunning(!0),this._segmentStart=Date.now(),this._runStartTick=this._tick,this._runGen=++this._gen,e&&this._fire(),this._running&&(this._timerId=setInterval(this._fire,this._interval))}changeInterval(t){this._running&&"number"==typeof t&&Number.isFinite(t)&&t>0&&t!==this._interval&&(this._interval=t,this._clearTimer(),this._timerId=setInterval(this._fire,this._interval))}stop(){this._clearTimer(),this._foldElapsed(),this._paused=!1,this._setRunning(!1)}reset(){this._clearTimer(),this._paused=!1,this._tick=0,this._accumulatedElapsed=0,this._segmentStart=null,this._setRunning(!1),this._dispatchTick()}pause(){this._running&&!this._paused&&(this._clearTimer(),this._foldElapsed(),this._paused=!0,this._setRunning(!1))}resume(){if(!this._paused)return;this._paused=!1,this._setRunning(!0),this._segmentStart=Date.now(),this._runGen=++this._gen;const t=this._interval-this._accumulatedElapsed%this._interval;this._timerId=setTimeout(this._onResumeBoundary,t)}_onResumeBoundary=()=>{this._runGen===this._gen&&(this._timerId=null,this._fire(),this._running&&(this._timerId=setInterval(this._fire,this._interval)))};_fire=()=>{this._runGen===this._gen&&(this._tick++,this._dispatchTick(),this._repeat>0&&this._tick-this._runStartTick>=this._repeat&&(this._clearTimer(),this._foldElapsed(),this._setRunning(!1)))};_clearTimer(){null!==this._timerId&&(clearTimeout(this._timerId),clearInterval(this._timerId),this._timerId=null)}_foldElapsed(){null!==this._segmentStart&&(this._accumulatedElapsed+=Date.now()-this._segmentStart,this._segmentStart=null)}_currentElapsed(){return this._accumulatedElapsed+(null!==this._segmentStart?Date.now()-this._segmentStart:0)}}let u=!1;function c(t){const e=t.target;if(!(e instanceof Element))return;const i=e.closest(`[${s.triggerAttribute}]`);if(!i)return;const r=i.getAttribute(s.triggerAttribute);if(!r)return;const n=customElements.get(s.tagNames.timer),a=document.getElementById(r);n&&a instanceof n&&(t.preventDefault(),a.start())}class h extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...a.wcBindable,properties:[...a.wcBindable.properties,{name:"trigger",event:"wcs-timer:trigger-changed"}],inputs:[{name:"interval",attribute:"interval"},{name:"once",attribute:"once"},{name:"repeat",attribute:"repeat"},{name:"immediate",attribute:"immediate"},{name:"manual",attribute:"manual"},{name:"trigger"}]};static get observedAttributes(){return["interval"]}_core;_trigger=!1;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new a(this)}get connectedCallbackPromise(){return this._connectedCallbackPromise}get interval(){const t=this.getAttribute("interval");if(null===t||""===t.trim())return 1e3;const e=Number(t);return Number.isFinite(e)&&e>0?e:1e3}set interval(t){this.setAttribute("interval",String(t))}get once(){return this.hasAttribute("once")}set once(t){t?this.setAttribute("once",""):this.removeAttribute("once")}get repeat(){const t=this.getAttribute("repeat");if(null===t||""===t.trim())return 0;const e=Number(t);return Number.isFinite(e)&&e>0?e:0}set repeat(t){this.setAttribute("repeat",String(t))}get immediate(){return this.hasAttribute("immediate")}set immediate(t){t?this.setAttribute("immediate",""):this.removeAttribute("immediate")}get manual(){return this.hasAttribute("manual")}set manual(t){t?this.setAttribute("manual",""):this.removeAttribute("manual")}get tick(){return this._core.tick}get elapsed(){return this._core.elapsed}get running(){return this._core.running}get trigger(){return this._trigger}set trigger(t){!!t&&(this._trigger=!0,this.start(),this._trigger=!1,this.dispatchEvent(new CustomEvent("wcs-timer:trigger-changed",{detail:!1,bubbles:!0})))}start(){const t=this.repeat>0?this.repeat:this.once?1:0;this._core.start({interval:this.interval,repeat:t,immediate:this.immediate})}stop(){this._core.stop()}reset(){this._core.reset()}pause(){this._core.pause()}resume(){this._core.resume()}attributeChangedCallback(t,e,i){"interval"===t&&e!==i&&this.isConnected&&this.running&&this._core.changeInterval(this.interval)}connectedCallback(){this.style.display="none",s.autoTrigger&&(u||(u=!0,document.addEventListener("click",c))),this._connectedCallbackPromise=this._core.observe(),this.manual||this.start()}disconnectedCallback(){this._core.dispose()}}function l(e){var i;e&&("boolean"==typeof(i=e).autoTrigger&&(t.autoTrigger=i.autoTrigger),"string"==typeof i.triggerAttribute&&(t.triggerAttribute=i.triggerAttribute),i.tagNames&&Object.assign(t.tagNames,i.tagNames),r=null),customElements.get(s.tagNames.timer)||customElements.define(s.tagNames.timer,h)}export{a as TimerCore,h as WcsTimer,l as bootstrapTimer,n as getConfig};
|
|
2
2
|
//# sourceMappingURL=index.esm.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/TimerCore.ts","../src/autoTrigger.ts","../src/components/Timer.ts","../src/bootstrapTimer.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n timer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-timertarget\",\n tagNames: {\n timer: \"wcs-timer\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal-only live handle to the mutable config. NOT part of the public API\n// (deliberately absent from exports.ts) — it is exported solely so sibling\n// modules in this package can read current settings cheaply. External consumers\n// must use getConfig() (returns a deep-frozen snapshot) / setConfig(). Mutating\n// this object directly bypasses the frozenConfig cache and is unsupported.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable } from \"../types.js\";\n\nexport interface TimerStartOptions {\n interval?: number;\n repeat?: number;\n immediate?: boolean;\n}\n\n/**\n * Headless timer primitive. A thin, framework-agnostic wrapper around\n * `setInterval` exposed through the wc-bindable protocol: it streams `tick`\n * (a monotonically increasing counter), `elapsed` (running time in ms) and a\n * `running` flag, and is driven by the `start` / `stop` / `reset` / `pause` /\n * `resume` commands.\n *\n * `tick` and `elapsed` are both surfaced via the single `wcs-timer:tick` event\n * (read through getters, mirroring how FetchCore exposes value/status from one\n * `wcs-fetch:response` event), so an observer that binds either property is\n * notified on every fire.\n */\nexport class TimerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"tick\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.count },\n { name: \"elapsed\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.elapsed },\n { name: \"running\", event: \"wcs-timer:running-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"reset\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n private _timerId: ReturnType<typeof setInterval> | null = null;\n\n private _tick: number = 0;\n private _running: boolean = false;\n private _paused: boolean = false;\n\n // `_tick` value captured at the start of the current run. `repeat` counts ticks\n // *per run*, so the stop condition compares against this baseline rather than the\n // cumulative `_tick` (which only resets on reset()). Without it, re-starting a\n // completed bounded timer would stop after a single tick.\n private _runStartTick: number = 0;\n\n // Timer configuration (captured on start, reused by pause/resume).\n // `_immediate` is intentionally NOT a field: it is per-run intent consumed\n // entirely within start() (fire once, then schedule), so it lives as a local\n // there rather than lingering as instance state no other method reads.\n private _interval: number = 1000;\n private _repeat: number = 0; // 0 = unlimited\n\n // Elapsed-time bookkeeping. `_accumulatedElapsed` holds the time folded from\n // already-finished running segments; `_segmentStart` is the timestamp the\n // current running segment began (null when not running). The live elapsed is\n // the sum of the two — see _currentElapsed().\n private _accumulatedElapsed: number = 0;\n private _segmentStart: number | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get tick(): number {\n return this._tick;\n }\n\n get elapsed(): number {\n return this._currentElapsed();\n }\n\n get running(): boolean {\n return this._running;\n }\n\n // --- State setters with event dispatch ---\n\n private _dispatchTick(): void {\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:tick\", {\n detail: { count: this._tick, elapsed: this._currentElapsed() },\n bubbles: true,\n }));\n }\n\n private _setRunning(running: boolean): void {\n if (this._running === running) return;\n this._running = running;\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:running-changed\", {\n detail: running,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n start(options: TimerStartOptions = {}): void {\n // Idempotent while running: a redundant start() must not stack a second\n // setInterval (which would leak and double the tick rate). Reconfiguring an\n // active timer is done via stop() + start().\n if (this._running) return;\n\n // start() begins a fresh running segment, so clear any lingering pause from a\n // prior pause()-without-resume(). Without this, the timer would run while\n // _paused stayed true, leaving pause() a no-op and letting resume() overwrite\n // the live timer handle (leak + double fire).\n this._paused = false;\n\n // `interval` is persistent configuration: a non-positive / non-finite value\n // (or an omitted option) keeps the previous interval (default 1000ms). The\n // guard rejects values that would turn setInterval into a hot loop and make\n // resume()'s `accumulated % interval` arithmetic produce NaN. The Shell already\n // falls back to 1000 for invalid attributes; this is the backstop for direct\n // Core API callers.\n if (typeof options.interval === \"number\" && Number.isFinite(options.interval) && options.interval > 0) {\n this._interval = options.interval;\n }\n\n // `repeat` / `immediate` are per-run intent, NOT persistent configuration:\n // every start() re-establishes them from the options, defaulting to\n // \"unlimited\" / \"no immediate fire\" when omitted. This keeps a bare start()\n // after a bounded or one-shot run from silently inheriting the old bounds.\n // `repeat` is a field (pause/resume/_fire read it across the run); `immediate`\n // is consumed here and now, so it stays a local.\n this._repeat = (typeof options.repeat === \"number\" && options.repeat > 0) ? options.repeat : 0;\n const immediate = options.immediate === true;\n\n this._setRunning(true);\n this._segmentStart = Date.now();\n // Baseline this run's per-run repeat counting (set after _setRunning so a\n // re-start of a completed bounded timer fires the full N ticks again).\n this._runStartTick = this._tick;\n\n // Fire immediately on start when requested. _fire() may stop the timer (when\n // repeat is reached), so re-check _running before scheduling the interval.\n if (immediate) {\n this._fire();\n }\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n }\n\n // Swap the tick period of a live timer in place, WITHOUT re-running start().\n // Unlike stop() + start(), this leaves the per-run repeat progress in flight\n // (`_repeat` and its `_runStartTick` baseline) untouched, so a bounded\n // `repeat=\"N\"` run is not re-baselined to fire N more times. It also never goes\n // through start()'s `immediate` path, so an `immediate` timer does not fire an\n // extra tick. Only re-arms the steady interval; pause()/resume() and reset() are\n // unaffected. No-op when not running (interval is then plain config, captured on\n // the next start) or when the new period is non-positive / non-finite (which\n // would turn setInterval into a hot loop and break resume()'s modulo arithmetic).\n changeInterval(interval: number): void {\n if (!this._running) return;\n if (!(typeof interval === \"number\" && Number.isFinite(interval) && interval > 0)) return;\n if (interval === this._interval) return;\n this._interval = interval;\n // Re-arm the steady ticking at the new period. The current period's progress\n // is intentionally discarded (the next tick is a full new interval away),\n // matching the boundary reset of the previous stop()+start() behaviour.\n this._clearTimer();\n this._timerId = setInterval(this._fire, this._interval);\n }\n\n stop(): void {\n this._clearTimer();\n this._foldElapsed();\n this._paused = false;\n this._setRunning(false);\n }\n\n reset(): void {\n this._clearTimer();\n this._paused = false;\n this._tick = 0;\n this._accumulatedElapsed = 0;\n this._segmentStart = null;\n this._setRunning(false);\n // Notify observers that the counter/elapsed have returned to zero.\n this._dispatchTick();\n }\n\n pause(): void {\n // Pause only a live timer; a no-op otherwise so it composes safely with the\n // declarative lifecycle. Unlike stop(), it records `_paused` so resume() can\n // tell an intentional pause from a full stop.\n if (!this._running || this._paused) return;\n this._clearTimer();\n this._foldElapsed();\n this._paused = true;\n this._setRunning(false);\n }\n\n resume(): void {\n if (!this._paused) return;\n this._paused = false;\n this._setRunning(true);\n this._segmentStart = Date.now();\n // Invariant: `_interval` is fixed at start() and stays constant for the whole\n // pause/resume cycle — changeInterval() only mutates it while *running* (never\n // while paused), so the remainder arithmetic below can safely assume the same\n // period was in effect across the paused segment. Consequence for the Shell:\n // because the live interval-attribute path (attributeChangedCallback ->\n // changeInterval) is gated on `running`, an `interval` change made *while\n // paused* is silently not applied here; it is picked up only on the next\n // start() as plain config. This is by design — see README \"Commands\".\n // Resume seamlessly: a tick fires every `interval` ms of *running* time, so\n // honour the partial period consumed before the pause. Wait only the\n // remainder to the next boundary, then fall back to the steady interval.\n // (`accumulated % interval === 0` — paused exactly on a boundary — yields a\n // full interval, which is correct: the next tick is a whole period away.)\n const remainder = this._interval - (this._accumulatedElapsed % this._interval);\n this._timerId = setTimeout(this._onResumeBoundary, remainder);\n }\n\n // --- Internal ---\n\n private _onResumeBoundary = (): void => {\n this._timerId = null;\n this._fire();\n // _fire() may have auto-stopped the timer (repeat reached); only re-arm the\n // steady interval while still running.\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n };\n\n private _fire = (): void => {\n this._tick++;\n this._dispatchTick();\n\n // Auto-stop once this run has fired the requested number of ticks (repeat=0\n // runs forever). Counted per-run via `_runStartTick`, so a re-start after a\n // completed bounded run fires N ticks again. `once` is expressed by the Shell\n // as repeat=1.\n if (this._repeat > 0 && (this._tick - this._runStartTick) >= this._repeat) {\n this._clearTimer();\n this._foldElapsed();\n this._setRunning(false);\n }\n };\n\n private _clearTimer(): void {\n if (this._timerId !== null) {\n // `_timerId` may hold a setInterval handle (steady ticking) or a setTimeout\n // handle (the resume remainder). Clear both — per the HTML spec timers\n // share one list and clearTimeout/clearInterval each remove the entry\n // regardless of which call created it.\n clearTimeout(this._timerId);\n clearInterval(this._timerId);\n this._timerId = null;\n }\n }\n\n private _foldElapsed(): void {\n if (this._segmentStart !== null) {\n this._accumulatedElapsed += Date.now() - this._segmentStart;\n this._segmentStart = null;\n }\n }\n\n private _currentElapsed(): number {\n return this._accumulatedElapsed +\n (this._segmentStart !== null ? Date.now() - this._segmentStart : 0);\n }\n}\n","import { config } from \"./config.js\";\nimport type { Timer } from \"./components/Timer.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const timerId = triggerElement.getAttribute(config.triggerAttribute);\n if (!timerId) return;\n\n // Resolve the registered constructor at call time instead of importing Timer\n // as a value. The value import created a components/Timer.ts ⇄ autoTrigger.ts\n // cycle (Timer.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the exact same identity guarantee\n // — only the registered <wcs-timer> class matches — without the import cycle.\n const TimerCtor = customElements.get(config.tagNames.timer);\n const timerElement = document.getElementById(timerId);\n if (!TimerCtor || !(timerElement instanceof TimerCtor)) return;\n\n // Suppress the element's default action so a timer can start without\n // navigating. Intentional: do not attach data-timertarget to an element whose\n // default action you also want (real <a href> link, form-submit button) — it\n // will be cancelled. See README \"Optional DOM Triggering\".\n event.preventDefault();\n (timerElement as Timer).start();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable } from \"../types.js\";\nimport { TimerCore } from \"../core/TimerCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Timer extends HTMLElement {\n static hasConnectedCallbackPromise = false;\n static wcBindable: IWcBindable = {\n ...TimerCore.wcBindable,\n properties: [\n ...TimerCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-timer:trigger-changed\" },\n ],\n // Shell-level settable surface. `attribute` is a purely descriptive hint\n // (per SPEC-extensions.md the binding core does not act on it) naming the\n // mirrored HTML attribute, matching <wcs-geo> / <wcs-debounce>. `trigger` is a\n // momentary command-property with no backing attribute, so it carries no hint\n // (same as those packages). `start` / `stop` / `reset` / `pause` / `resume`\n // commands are inherited from the Core above.\n inputs: [\n { name: \"interval\", attribute: \"interval\" },\n { name: \"once\", attribute: \"once\" },\n { name: \"repeat\", attribute: \"repeat\" },\n { name: \"immediate\", attribute: \"immediate\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"interval\"]; }\n\n private _core: TimerCore;\n private _trigger: boolean = false;\n\n constructor() {\n super();\n this._core = new TimerCore(this);\n }\n\n // --- Attribute accessors ---\n\n get interval(): number {\n const attr = this.getAttribute(\"interval\");\n if (attr === null || attr.trim() === \"\") return 1000;\n // Strict parse via Number() (unlike parseInt, \"100px\" -> NaN, not 100),\n // matching <wcs-geo> / <wcs-debounce>. Fall back to the 1000ms default for any\n // invalid period — not only NaN but also 0 / negative values, which would\n // otherwise reach setInterval as a hot loop and break resume()'s modulo\n // arithmetic in the Core.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 1000;\n }\n\n set interval(value: number) {\n this.setAttribute(\"interval\", String(value));\n }\n\n get once(): boolean {\n return this.hasAttribute(\"once\");\n }\n\n set once(value: boolean) {\n if (value) {\n this.setAttribute(\"once\", \"\");\n } else {\n this.removeAttribute(\"once\");\n }\n }\n\n get repeat(): number {\n const attr = this.getAttribute(\"repeat\");\n if (attr === null || attr.trim() === \"\") return 0;\n // Strict parse via Number() (\"3px\" -> NaN, not 3), matching <wcs-geo> /\n // <wcs-debounce>. Normalise any non-positive / non-numeric value to 0\n // (= unlimited), mirroring the `interval` getter. Without this a negative\n // `repeat=\"-3\"` would leak through to start(); harmless today (Core treats\n // `_repeat <= 0` as unlimited) but the asymmetry is a trap.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 0;\n }\n\n set repeat(value: number) {\n this.setAttribute(\"repeat\", String(value));\n }\n\n get immediate(): boolean {\n return this.hasAttribute(\"immediate\");\n }\n\n set immediate(value: boolean) {\n if (value) {\n this.setAttribute(\"immediate\", \"\");\n } else {\n this.removeAttribute(\"immediate\");\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get tick(): number {\n return this._core.tick;\n }\n\n get elapsed(): number {\n return this._core.elapsed;\n }\n\n get running(): boolean {\n return this._core.running;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts the timer. Mirrors\n // the trigger flag on <wcs-fetch> / <wcs-ws>. Prefer the command-token\n // protocol (`command.start: $command.tick`) for state-driven starts; this\n // exists mainly for the DOM click trigger and simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n // The `trigger-changed` event reports the momentary flag returning to\n // false, i.e. that the trigger property *changed* — it is deliberately not\n // gated on whether start() actually began a new run. This keeps the\n // wcBindable `trigger` property's change-notification semantics consistent\n // (every false→true write produces exactly one change-back event), even\n // when start() was a no-op because the timer was already running.\n this.dispatchEvent(new CustomEvent(\"wcs-timer:trigger-changed\", {\n detail: false,\n bubbles: true,\n }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n // `once` is sugar for \"fire exactly one tick\": map it to repeat=1, but let an\n // explicit repeat attribute win when both are present.\n const repeat = this.repeat > 0 ? this.repeat : (this.once ? 1 : 0);\n this._core.start({\n interval: this.interval,\n repeat,\n immediate: this.immediate,\n });\n }\n\n stop(): void {\n this._core.stop();\n }\n\n reset(): void {\n this._core.reset();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n // Live interval changes swap the underlying setInterval period in place.\n // `tick` / `elapsed` and the per-run `repeat` progress are preserved — we\n // deliberately do NOT stop()+start(), which would re-run start() and\n // re-evaluate per-run options (re-firing `immediate` and re-baselining\n // `repeat`). `running` alone gates this: swapping the period is orthogonal to\n // *how* the timer was started, so a `manual` timer the user has explicitly\n // started gets live period changes too. A non-running timer needs no swap —\n // its next start() picks up the new attribute as plain config.\n if (name === \"interval\" && oldValue !== newValue && this.isConnected && this.running) {\n this._core.changeInterval(this.interval);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n if (!this.manual) {\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this._core.stop();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapTimer(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { Timer } from \"./components/Timer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.timer)) {\n customElements.define(config.tagNames.timer, Timer);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","tagNames","timer","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","TimerCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","count","elapsed","commands","_target","_timerId","_tick","_running","_paused","_runStartTick","_interval","_repeat","_accumulatedElapsed","_segmentStart","constructor","target","super","this","tick","_currentElapsed","running","_dispatchTick","dispatchEvent","CustomEvent","bubbles","_setRunning","start","options","interval","Number","isFinite","repeat","immediate","Date","now","_fire","setInterval","changeInterval","_clearTimer","stop","_foldElapsed","reset","pause","resume","remainder","setTimeout","_onResumeBoundary","clearTimeout","clearInterval","registered","handleClick","Element","triggerElement","closest","timerId","getAttribute","TimerCtor","customElements","get","timerElement","document","getElementById","preventDefault","Timer","HTMLElement","wcBindable","inputs","attribute","observedAttributes","_core","_trigger","attr","trim","parsed","value","setAttribute","String","once","hasAttribute","removeAttribute","manual","trigger","attributeChangedCallback","oldValue","newValue","isConnected","connectedCallback","style","display","addEventListener","disconnectedCallback","bootstrapTimer","userConfig","partialConfig","assign","define"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/TimerCore.ts","../src/autoTrigger.ts","../src/components/Timer.ts","../src/bootstrapTimer.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n tagNames: {\n timer: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-timertarget\",\n tagNames: {\n timer: \"wcs-timer\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal-only live handle to the mutable config. NOT part of the public API\n// (deliberately absent from exports.ts) — it is exported solely so sibling\n// modules in this package can read current settings cheaply. External consumers\n// must use getConfig() (returns a deep-frozen snapshot) / setConfig(). Mutating\n// this object directly bypasses the frozenConfig cache and is unsupported.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable } from \"../types.js\";\n\nexport interface TimerStartOptions {\n interval?: number;\n repeat?: number;\n immediate?: boolean;\n}\n\n/**\n * Headless timer primitive. A thin, framework-agnostic wrapper around\n * `setInterval` exposed through the wc-bindable protocol: it streams `tick`\n * (a monotonically increasing counter), `elapsed` (running time in ms) and a\n * `running` flag, and is driven by the `start` / `stop` / `reset` / `pause` /\n * `resume` commands.\n *\n * `tick` and `elapsed` are both surfaced via the single `wcs-timer:tick` event\n * (read through getters, mirroring how FetchCore exposes value/status from one\n * `wcs-fetch:response` event), so an observer that binds either property is\n * notified on every fire.\n */\nexport class TimerCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"tick\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.count },\n { name: \"elapsed\", event: \"wcs-timer:tick\", getter: (e: Event) => (e as CustomEvent).detail.elapsed },\n { name: \"running\", event: \"wcs-timer:running-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"reset\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n private _timerId: ReturnType<typeof setInterval> | null = null;\n\n // Generation guard (§3.4): bumped on dispose() and at every async start\n // (start()/resume()). A scheduled callback (setInterval/setTimeout) captures the\n // generation live via `_gen`; a fire that was already queued when the timer was\n // torn down — or superseded by a new run — has a stale gen and MUST NOT mutate\n // state or dispatch on a disposed element. _clearTimer() removes the handle, but\n // the guard is the belt-and-braces defense for a callback already in the queue.\n private _gen = 0;\n // Generation captured for the currently scheduled run, set at each async start\n // (start()/resume()). A scheduled callback compares it against the live `_gen`;\n // a stale callback (dispose() bumped `_gen` after this run was armed) bails.\n private _runGen = 0;\n // SSR (§3.8): the timer does no asynchronous probe, so readiness is immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n private _tick: number = 0;\n private _running: boolean = false;\n private _paused: boolean = false;\n\n // `_tick` value captured at the start of the current run. `repeat` counts ticks\n // *per run*, so the stop condition compares against this baseline rather than the\n // cumulative `_tick` (which only resets on reset()). Without it, re-starting a\n // completed bounded timer would stop after a single tick.\n private _runStartTick: number = 0;\n\n // Timer configuration (captured on start, reused by pause/resume).\n // `_immediate` is intentionally NOT a field: it is per-run intent consumed\n // entirely within start() (fire once, then schedule), so it lives as a local\n // there rather than lingering as instance state no other method reads.\n private _interval: number = 1000;\n private _repeat: number = 0; // 0 = unlimited\n\n // Elapsed-time bookkeeping. `_accumulatedElapsed` holds the time folded from\n // already-finished running segments; `_segmentStart` is the timestamp the\n // current running segment began (null when not running). The live elapsed is\n // the sum of the two — see _currentElapsed().\n private _accumulatedElapsed: number = 0;\n private _segmentStart: number | null = null;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get tick(): number {\n return this._tick;\n }\n\n get elapsed(): number {\n return this._currentElapsed();\n }\n\n get running(): boolean {\n return this._running;\n }\n\n // SSR readiness (§3.8): resolves after the first probe. The timer is\n // command-driven with nothing to probe, so this is an already-resolved promise.\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // Lifecycle (§3.5). The timer is command-driven (start/stop/...) with no\n // ambient subscription to establish, so observe() is an idempotent no-op that\n // resolves once ready (mirroring UploadCore). dispose() tears the timer down —\n // it stops any running interval and bumps the generation so a callback already\n // queued cannot fire onto a torn-down element.\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this._gen++;\n this.stop();\n }\n\n // --- State setters with event dispatch ---\n\n private _dispatchTick(): void {\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:tick\", {\n detail: { count: this._tick, elapsed: this._currentElapsed() },\n bubbles: true,\n }));\n }\n\n private _setRunning(running: boolean): void {\n if (this._running === running) return;\n this._running = running;\n this._target.dispatchEvent(new CustomEvent(\"wcs-timer:running-changed\", {\n detail: running,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n start(options: TimerStartOptions = {}): void {\n // Idempotent while running: a redundant start() must not stack a second\n // setInterval (which would leak and double the tick rate). Reconfiguring an\n // active timer is done via stop() + start().\n if (this._running) return;\n\n // start() begins a fresh running segment, so clear any lingering pause from a\n // prior pause()-without-resume(). Without this, the timer would run while\n // _paused stayed true, leaving pause() a no-op and letting resume() overwrite\n // the live timer handle (leak + double fire).\n this._paused = false;\n\n // `interval` is persistent configuration: a non-positive / non-finite value\n // (or an omitted option) keeps the previous interval (default 1000ms). The\n // guard rejects values that would turn setInterval into a hot loop and make\n // resume()'s `accumulated % interval` arithmetic produce NaN. The Shell already\n // falls back to 1000 for invalid attributes; this is the backstop for direct\n // Core API callers.\n if (typeof options.interval === \"number\" && Number.isFinite(options.interval) && options.interval > 0) {\n this._interval = options.interval;\n }\n\n // `repeat` / `immediate` are per-run intent, NOT persistent configuration:\n // every start() re-establishes them from the options, defaulting to\n // \"unlimited\" / \"no immediate fire\" when omitted. This keeps a bare start()\n // after a bounded or one-shot run from silently inheriting the old bounds.\n // `repeat` is a field (pause/resume/_fire read it across the run); `immediate`\n // is consumed here and now, so it stays a local.\n this._repeat = (typeof options.repeat === \"number\" && options.repeat > 0) ? options.repeat : 0;\n const immediate = options.immediate === true;\n\n this._setRunning(true);\n this._segmentStart = Date.now();\n // Baseline this run's per-run repeat counting (set after _setRunning so a\n // re-start of a completed bounded timer fires the full N ticks again).\n this._runStartTick = this._tick;\n // Capture this run's generation (§3.4): a scheduled fire that outlives a\n // dispose() (which bumps `_gen`) is then recognised as stale and ignored.\n this._runGen = ++this._gen;\n\n // Fire immediately on start when requested. _fire() may stop the timer (when\n // repeat is reached), so re-check _running before scheduling the interval.\n if (immediate) {\n this._fire();\n }\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n }\n\n // Swap the tick period of a live timer in place, WITHOUT re-running start().\n // Unlike stop() + start(), this leaves the per-run repeat progress in flight\n // (`_repeat` and its `_runStartTick` baseline) untouched, so a bounded\n // `repeat=\"N\"` run is not re-baselined to fire N more times. It also never goes\n // through start()'s `immediate` path, so an `immediate` timer does not fire an\n // extra tick. Only re-arms the steady interval; pause()/resume() and reset() are\n // unaffected. No-op when not running (interval is then plain config, captured on\n // the next start) or when the new period is non-positive / non-finite (which\n // would turn setInterval into a hot loop and break resume()'s modulo arithmetic).\n changeInterval(interval: number): void {\n if (!this._running) return;\n if (!(typeof interval === \"number\" && Number.isFinite(interval) && interval > 0)) return;\n if (interval === this._interval) return;\n this._interval = interval;\n // Re-arm the steady ticking at the new period. The current period's progress\n // is intentionally discarded (the next tick is a full new interval away),\n // matching the boundary reset of the previous stop()+start() behaviour.\n this._clearTimer();\n this._timerId = setInterval(this._fire, this._interval);\n }\n\n stop(): void {\n this._clearTimer();\n this._foldElapsed();\n this._paused = false;\n this._setRunning(false);\n }\n\n reset(): void {\n this._clearTimer();\n this._paused = false;\n this._tick = 0;\n this._accumulatedElapsed = 0;\n this._segmentStart = null;\n this._setRunning(false);\n // Notify observers that the counter/elapsed have returned to zero.\n this._dispatchTick();\n }\n\n pause(): void {\n // Pause only a live timer; a no-op otherwise so it composes safely with the\n // declarative lifecycle. Unlike stop(), it records `_paused` so resume() can\n // tell an intentional pause from a full stop.\n if (!this._running || this._paused) return;\n this._clearTimer();\n this._foldElapsed();\n this._paused = true;\n this._setRunning(false);\n }\n\n resume(): void {\n if (!this._paused) return;\n this._paused = false;\n this._setRunning(true);\n this._segmentStart = Date.now();\n // New scheduled run after a pause: capture its generation (§3.4) so a\n // post-dispose() boundary/interval callback is recognised as stale.\n this._runGen = ++this._gen;\n // Invariant: `_interval` is fixed at start() and stays constant for the whole\n // pause/resume cycle — changeInterval() only mutates it while *running* (never\n // while paused), so the remainder arithmetic below can safely assume the same\n // period was in effect across the paused segment. Consequence for the Shell:\n // because the live interval-attribute path (attributeChangedCallback ->\n // changeInterval) is gated on `running`, an `interval` change made *while\n // paused* is silently not applied here; it is picked up only on the next\n // start() as plain config. This is by design — see README \"Commands\".\n // Resume seamlessly: a tick fires every `interval` ms of *running* time, so\n // honour the partial period consumed before the pause. Wait only the\n // remainder to the next boundary, then fall back to the steady interval.\n // (`accumulated % interval === 0` — paused exactly on a boundary — yields a\n // full interval, which is correct: the next tick is a whole period away.)\n const remainder = this._interval - (this._accumulatedElapsed % this._interval);\n this._timerId = setTimeout(this._onResumeBoundary, remainder);\n }\n\n // --- Internal ---\n\n private _onResumeBoundary = (): void => {\n // Stale-run guard (§3.4): a resume-boundary timeout that fires after dispose()\n // belongs to a torn-down run — drop it without re-arming the interval.\n if (this._runGen !== this._gen) return;\n this._timerId = null;\n this._fire();\n // _fire() may have auto-stopped the timer (repeat reached); only re-arm the\n // steady interval while still running.\n if (this._running) {\n this._timerId = setInterval(this._fire, this._interval);\n }\n };\n\n private _fire = (): void => {\n // Stale-run guard (§3.4): an interval callback queued before dispose() (which\n // bumps `_gen`) must not tick or dispatch onto a torn-down element.\n if (this._runGen !== this._gen) return;\n this._tick++;\n this._dispatchTick();\n\n // Auto-stop once this run has fired the requested number of ticks (repeat=0\n // runs forever). Counted per-run via `_runStartTick`, so a re-start after a\n // completed bounded run fires N ticks again. `once` is expressed by the Shell\n // as repeat=1.\n if (this._repeat > 0 && (this._tick - this._runStartTick) >= this._repeat) {\n this._clearTimer();\n this._foldElapsed();\n this._setRunning(false);\n }\n };\n\n private _clearTimer(): void {\n if (this._timerId !== null) {\n // `_timerId` may hold a setInterval handle (steady ticking) or a setTimeout\n // handle (the resume remainder). Clear both — per the HTML spec timers\n // share one list and clearTimeout/clearInterval each remove the entry\n // regardless of which call created it.\n clearTimeout(this._timerId);\n clearInterval(this._timerId);\n this._timerId = null;\n }\n }\n\n private _foldElapsed(): void {\n if (this._segmentStart !== null) {\n this._accumulatedElapsed += Date.now() - this._segmentStart;\n this._segmentStart = null;\n }\n }\n\n private _currentElapsed(): number {\n return this._accumulatedElapsed +\n (this._segmentStart !== null ? Date.now() - this._segmentStart : 0);\n }\n}\n","import { config } from \"./config.js\";\nimport type { Timer } from \"./components/Timer.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n const triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n if (!triggerElement) return;\n\n const timerId = triggerElement.getAttribute(config.triggerAttribute);\n if (!timerId) return;\n\n // Resolve the registered constructor at call time instead of importing Timer\n // as a value. The value import created a components/Timer.ts ⇄ autoTrigger.ts\n // cycle (Timer.connectedCallback() calls registerAutoTrigger()). instanceof\n // against the customElements registry keeps the exact same identity guarantee\n // — only the registered <wcs-timer> class matches — without the import cycle.\n const TimerCtor = customElements.get(config.tagNames.timer);\n const timerElement = document.getElementById(timerId);\n if (!TimerCtor || !(timerElement instanceof TimerCtor)) return;\n\n // Suppress the element's default action so a timer can start without\n // navigating. Intentional: do not attach data-timertarget to an element whose\n // default action you also want (real <a href> link, form-submit button) — it\n // will be cancelled. See README \"Optional DOM Triggering\".\n event.preventDefault();\n (timerElement as Timer).start();\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable } from \"../types.js\";\nimport { TimerCore } from \"../core/TimerCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\nexport class Timer extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...TimerCore.wcBindable,\n properties: [\n ...TimerCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-timer:trigger-changed\" },\n ],\n // Shell-level settable surface. `attribute` is a purely descriptive hint\n // (per SPEC-extensions.md the binding core does not act on it) naming the\n // mirrored HTML attribute, matching <wcs-geo> / <wcs-debounce>. `trigger` is a\n // momentary command-property with no backing attribute, so it carries no hint\n // (same as those packages). `start` / `stop` / `reset` / `pause` / `resume`\n // commands are inherited from the Core above.\n inputs: [\n { name: \"interval\", attribute: \"interval\" },\n { name: \"once\", attribute: \"once\" },\n { name: \"repeat\", attribute: \"repeat\" },\n { name: \"immediate\", attribute: \"immediate\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n };\n static get observedAttributes(): string[] { return [\"interval\"]; }\n\n private _core: TimerCore;\n private _trigger: boolean = false;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new TimerCore(this);\n }\n\n // SSR (§4.1/§4.4): the Shell exposes the Core's readiness so a server-side\n // renderer can await the connect-time probe before snapshotting. The timer has\n // no async probe (observe() resolves immediately), but the contract is uniform\n // across IO nodes.\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get interval(): number {\n const attr = this.getAttribute(\"interval\");\n if (attr === null || attr.trim() === \"\") return 1000;\n // Strict parse via Number() (unlike parseInt, \"100px\" -> NaN, not 100),\n // matching <wcs-geo> / <wcs-debounce>. Fall back to the 1000ms default for any\n // invalid period — not only NaN but also 0 / negative values, which would\n // otherwise reach setInterval as a hot loop and break resume()'s modulo\n // arithmetic in the Core.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 1000;\n }\n\n set interval(value: number) {\n this.setAttribute(\"interval\", String(value));\n }\n\n get once(): boolean {\n return this.hasAttribute(\"once\");\n }\n\n set once(value: boolean) {\n if (value) {\n this.setAttribute(\"once\", \"\");\n } else {\n this.removeAttribute(\"once\");\n }\n }\n\n get repeat(): number {\n const attr = this.getAttribute(\"repeat\");\n if (attr === null || attr.trim() === \"\") return 0;\n // Strict parse via Number() (\"3px\" -> NaN, not 3), matching <wcs-geo> /\n // <wcs-debounce>. Normalise any non-positive / non-numeric value to 0\n // (= unlimited), mirroring the `interval` getter. Without this a negative\n // `repeat=\"-3\"` would leak through to start(); harmless today (Core treats\n // `_repeat <= 0` as unlimited) but the asymmetry is a trap.\n const parsed = Number(attr);\n return (Number.isFinite(parsed) && parsed > 0) ? parsed : 0;\n }\n\n set repeat(value: number) {\n this.setAttribute(\"repeat\", String(value));\n }\n\n get immediate(): boolean {\n return this.hasAttribute(\"immediate\");\n }\n\n set immediate(value: boolean) {\n if (value) {\n this.setAttribute(\"immediate\", \"\");\n } else {\n this.removeAttribute(\"immediate\");\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get tick(): number {\n return this._core.tick;\n }\n\n get elapsed(): number {\n return this._core.elapsed;\n }\n\n get running(): boolean {\n return this._core.running;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts the timer. Mirrors\n // the trigger flag on <wcs-fetch> / <wcs-ws>. Prefer the command-token\n // protocol (`command.start: $command.tick`) for state-driven starts; this\n // exists mainly for the DOM click trigger and simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n // The `trigger-changed` event reports the momentary flag returning to\n // false, i.e. that the trigger property *changed* — it is deliberately not\n // gated on whether start() actually began a new run. This keeps the\n // wcBindable `trigger` property's change-notification semantics consistent\n // (every false→true write produces exactly one change-back event), even\n // when start() was a no-op because the timer was already running.\n this.dispatchEvent(new CustomEvent(\"wcs-timer:trigger-changed\", {\n detail: false,\n bubbles: true,\n }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n // `once` is sugar for \"fire exactly one tick\": map it to repeat=1, but let an\n // explicit repeat attribute win when both are present.\n const repeat = this.repeat > 0 ? this.repeat : (this.once ? 1 : 0);\n this._core.start({\n interval: this.interval,\n repeat,\n immediate: this.immediate,\n });\n }\n\n stop(): void {\n this._core.stop();\n }\n\n reset(): void {\n this._core.reset();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Lifecycle ---\n\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n // Live interval changes swap the underlying setInterval period in place.\n // `tick` / `elapsed` and the per-run `repeat` progress are preserved — we\n // deliberately do NOT stop()+start(), which would re-run start() and\n // re-evaluate per-run options (re-firing `immediate` and re-baselining\n // `repeat`). `running` alone gates this: swapping the period is orthogonal to\n // *how* the timer was started, so a `manual` timer the user has explicitly\n // started gets live period changes too. A non-running timer needs no swap —\n // its next start() picks up the new attribute as plain config.\n if (name === \"interval\" && oldValue !== newValue && this.isConnected && this.running) {\n this._core.changeInterval(this.interval);\n }\n }\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n // Establish monitoring (§3.5). observe() is a no-op that resolves once ready;\n // expose it as connectedCallbackPromise for SSR.\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual) {\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n // dispose() stops the timer and bumps the generation so a callback already\n // queued cannot fire onto a disconnected element (§3.5 / §4.4).\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapTimer(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { Timer } from \"./components/Timer.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.timer)) {\n customElements.define(config.tagNames.timer, Timer);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","tagNames","timer","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","TimerCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","count","elapsed","commands","_target","_timerId","_gen","_runGen","_ready","Promise","resolve","_tick","_running","_paused","_runStartTick","_interval","_repeat","_accumulatedElapsed","_segmentStart","constructor","target","super","this","tick","_currentElapsed","running","ready","observe","dispose","stop","_dispatchTick","dispatchEvent","CustomEvent","bubbles","_setRunning","start","options","interval","Number","isFinite","repeat","immediate","Date","now","_fire","setInterval","changeInterval","_clearTimer","_foldElapsed","reset","pause","resume","remainder","setTimeout","_onResumeBoundary","clearTimeout","clearInterval","registered","handleClick","Element","triggerElement","closest","timerId","getAttribute","TimerCtor","customElements","get","timerElement","document","getElementById","preventDefault","Timer","HTMLElement","wcBindable","inputs","attribute","observedAttributes","_core","_trigger","_connectedCallbackPromise","connectedCallbackPromise","attr","trim","parsed","value","setAttribute","String","once","hasAttribute","removeAttribute","manual","trigger","attributeChangedCallback","oldValue","newValue","isConnected","connectedCallback","style","display","addEventListener","disconnectedCallback","bootstrapTimer","userConfig","partialConfig","assign","define"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcstack/timer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "Declarative timer component for Web Components. Framework-agnostic interval/timeout primitive via wc-bindable-protocol.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.esm.js",
|