@xyo-network/sentinel-memory 2.85.5
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/LICENSE +165 -0
- package/README.md +13 -0
- package/dist/browser/MemorySentinel.d.cts +16 -0
- package/dist/browser/MemorySentinel.d.cts.map +1 -0
- package/dist/browser/MemorySentinel.d.mts +16 -0
- package/dist/browser/MemorySentinel.d.mts.map +1 -0
- package/dist/browser/MemorySentinel.d.ts +16 -0
- package/dist/browser/MemorySentinel.d.ts.map +1 -0
- package/dist/browser/SentinelIntervalAutomationWrapper.d.cts +21 -0
- package/dist/browser/SentinelIntervalAutomationWrapper.d.cts.map +1 -0
- package/dist/browser/SentinelIntervalAutomationWrapper.d.mts +21 -0
- package/dist/browser/SentinelIntervalAutomationWrapper.d.mts.map +1 -0
- package/dist/browser/SentinelIntervalAutomationWrapper.d.ts +21 -0
- package/dist/browser/SentinelIntervalAutomationWrapper.d.ts.map +1 -0
- package/dist/browser/SentinelRunner.d.cts +23 -0
- package/dist/browser/SentinelRunner.d.cts.map +1 -0
- package/dist/browser/SentinelRunner.d.mts +23 -0
- package/dist/browser/SentinelRunner.d.mts.map +1 -0
- package/dist/browser/SentinelRunner.d.ts +23 -0
- package/dist/browser/SentinelRunner.d.ts.map +1 -0
- package/dist/browser/index.cjs +310 -0
- package/dist/browser/index.cjs.map +1 -0
- package/dist/browser/index.d.cts +2 -0
- package/dist/browser/index.d.cts.map +1 -0
- package/dist/browser/index.d.mts +2 -0
- package/dist/browser/index.d.mts.map +1 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +289 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/node/MemorySentinel.d.cts +16 -0
- package/dist/node/MemorySentinel.d.cts.map +1 -0
- package/dist/node/MemorySentinel.d.mts +16 -0
- package/dist/node/MemorySentinel.d.mts.map +1 -0
- package/dist/node/MemorySentinel.d.ts +16 -0
- package/dist/node/MemorySentinel.d.ts.map +1 -0
- package/dist/node/SentinelIntervalAutomationWrapper.d.cts +21 -0
- package/dist/node/SentinelIntervalAutomationWrapper.d.cts.map +1 -0
- package/dist/node/SentinelIntervalAutomationWrapper.d.mts +21 -0
- package/dist/node/SentinelIntervalAutomationWrapper.d.mts.map +1 -0
- package/dist/node/SentinelIntervalAutomationWrapper.d.ts +21 -0
- package/dist/node/SentinelIntervalAutomationWrapper.d.ts.map +1 -0
- package/dist/node/SentinelRunner.d.cts +23 -0
- package/dist/node/SentinelRunner.d.cts.map +1 -0
- package/dist/node/SentinelRunner.d.mts +23 -0
- package/dist/node/SentinelRunner.d.mts.map +1 -0
- package/dist/node/SentinelRunner.d.ts +23 -0
- package/dist/node/SentinelRunner.d.ts.map +1 -0
- package/dist/node/index.cjs +322 -0
- package/dist/node/index.cjs.map +1 -0
- package/dist/node/index.d.cts +2 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.mts +2 -0
- package/dist/node/index.d.mts.map +1 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +297 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +77 -0
- package/src/MemorySentinel.ts +128 -0
- package/src/SentinelIntervalAutomationWrapper.ts +76 -0
- package/src/SentinelRunner.ts +114 -0
- package/src/index.ts +1 -0
- package/typedoc.json +5 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { PayloadWrapper } from '@xyo-network/payload-wrapper'
|
|
2
|
+
import { SentinelIntervalAutomationPayload } from '@xyo-network/sentinel-model'
|
|
3
|
+
|
|
4
|
+
export class SentinelIntervalAutomationWrapper<
|
|
5
|
+
T extends SentinelIntervalAutomationPayload = SentinelIntervalAutomationPayload,
|
|
6
|
+
> extends PayloadWrapper<T> {
|
|
7
|
+
constructor(payload: T) {
|
|
8
|
+
super(payload)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
protected get frequencyMillis() {
|
|
12
|
+
const frequency = this.jsonPayload().frequency
|
|
13
|
+
if (frequency === undefined) return Number.POSITIVE_INFINITY
|
|
14
|
+
const frequencyUnits = this.jsonPayload().frequencyUnits
|
|
15
|
+
switch (frequencyUnits ?? 'hour') {
|
|
16
|
+
case 'second': {
|
|
17
|
+
return frequency * 1000
|
|
18
|
+
}
|
|
19
|
+
case 'minute': {
|
|
20
|
+
return frequency * 60 * 1000
|
|
21
|
+
}
|
|
22
|
+
case 'hour': {
|
|
23
|
+
return frequency * 60 * 60 * 1000
|
|
24
|
+
}
|
|
25
|
+
case 'day': {
|
|
26
|
+
return frequency * 24 * 60 * 60 * 1000
|
|
27
|
+
}
|
|
28
|
+
default: {
|
|
29
|
+
return Number.POSITIVE_INFINITY
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected get remaining() {
|
|
35
|
+
return this.jsonPayload().remaining ?? Number.POSITIVE_INFINITY
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
next() {
|
|
39
|
+
const now = Date.now()
|
|
40
|
+
const previousStart = this.jsonPayload()?.start ?? now
|
|
41
|
+
const start = Math.max(previousStart, now)
|
|
42
|
+
const nextStart = start + this.frequencyMillis
|
|
43
|
+
this.setStart(nextStart)
|
|
44
|
+
this.consumeRemaining()
|
|
45
|
+
this.checkEnd()
|
|
46
|
+
return this
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
protected checkEnd() {
|
|
50
|
+
if (this.jsonPayload().start > (this.jsonPayload().end ?? Number.POSITIVE_INFINITY)) {
|
|
51
|
+
this.setStart(Number.POSITIVE_INFINITY)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected consumeRemaining(count = 1) {
|
|
56
|
+
const remaining = Math.max(this.remaining - count, 0)
|
|
57
|
+
this.setRemaining(remaining)
|
|
58
|
+
if (remaining <= 0) this.setStart(Number.POSITIVE_INFINITY)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Sets the remaining of the wrapped automation
|
|
63
|
+
* @param remaining The remaining time in milliseconds
|
|
64
|
+
*/
|
|
65
|
+
protected setRemaining(remaining: number) {
|
|
66
|
+
this.obj.remaining = remaining
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Sets the start of the wrapped automation
|
|
71
|
+
* @param start The start time in milliseconds
|
|
72
|
+
*/
|
|
73
|
+
protected setStart(start: number) {
|
|
74
|
+
this.obj.start = start
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import { Payload } from '@xyo-network/payload-model'
|
|
3
|
+
import { PayloadWrapper } from '@xyo-network/payload-wrapper'
|
|
4
|
+
import {
|
|
5
|
+
isSentinelIntervalAutomation,
|
|
6
|
+
SentinelAutomationPayload,
|
|
7
|
+
SentinelInstance,
|
|
8
|
+
SentinelIntervalAutomationPayload,
|
|
9
|
+
} from '@xyo-network/sentinel-model'
|
|
10
|
+
|
|
11
|
+
import { SentinelIntervalAutomationWrapper } from './SentinelIntervalAutomationWrapper'
|
|
12
|
+
|
|
13
|
+
export type OnSentinelRunnerTriggerResult = (result: Payload[]) => void
|
|
14
|
+
|
|
15
|
+
export class SentinelRunner {
|
|
16
|
+
protected _automations: Record<string, SentinelAutomationPayload> = {}
|
|
17
|
+
protected onTriggerResult: OnSentinelRunnerTriggerResult | undefined
|
|
18
|
+
protected sentinel: SentinelInstance
|
|
19
|
+
protected timeoutId?: NodeJS.Timeout | string | number
|
|
20
|
+
|
|
21
|
+
constructor(sentinel: SentinelInstance, automations?: SentinelAutomationPayload[], onTriggerResult?: OnSentinelRunnerTriggerResult) {
|
|
22
|
+
this.sentinel = sentinel
|
|
23
|
+
this.onTriggerResult = onTriggerResult
|
|
24
|
+
if (automations) for (const automation of automations) this.add(automation)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get automations() {
|
|
28
|
+
return this._automations
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private get next() {
|
|
32
|
+
// eslint-disable-next-line unicorn/no-array-reduce
|
|
33
|
+
return Object.values(this._automations).reduce<SentinelAutomationPayload | undefined>((previous, current) => {
|
|
34
|
+
if (isSentinelIntervalAutomation(current) && isSentinelIntervalAutomation(previous)) {
|
|
35
|
+
return current.start < (previous?.start ?? Number.POSITIVE_INFINITY) ? current : previous
|
|
36
|
+
}
|
|
37
|
+
return current
|
|
38
|
+
// eslint-disable-next-line unicorn/no-useless-undefined
|
|
39
|
+
}, undefined)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async add(automation: SentinelAutomationPayload, restart = true) {
|
|
43
|
+
const hash = await PayloadWrapper.hashAsync(automation)
|
|
44
|
+
this._automations[hash] = automation
|
|
45
|
+
if (restart) await this.restart()
|
|
46
|
+
return hash
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
find(hash: string) {
|
|
50
|
+
Object.entries(this._automations).find(([key]) => key === hash)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async remove(hash: string, restart = true) {
|
|
54
|
+
delete this._automations[hash]
|
|
55
|
+
if (restart) await this.restart()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
removeAll() {
|
|
59
|
+
this.stop()
|
|
60
|
+
this._automations = {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async restart() {
|
|
64
|
+
this.stop()
|
|
65
|
+
await this.start()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async start() {
|
|
69
|
+
// NOTE: Keep async to match module start signature
|
|
70
|
+
await Promise.resolve()
|
|
71
|
+
assertEx(this.timeoutId === undefined, 'Already started')
|
|
72
|
+
const automation = this.next
|
|
73
|
+
if (isSentinelIntervalAutomation(automation)) {
|
|
74
|
+
const now = Date.now()
|
|
75
|
+
const start = Math.max(automation.start ?? now, now)
|
|
76
|
+
const delay = Math.max(start - now, 0)
|
|
77
|
+
if (delay < Number.POSITIVE_INFINITY) {
|
|
78
|
+
this.timeoutId = setTimeout(async () => {
|
|
79
|
+
try {
|
|
80
|
+
// Run the automation
|
|
81
|
+
await this.trigger(automation)
|
|
82
|
+
this.stop()
|
|
83
|
+
} finally {
|
|
84
|
+
// No matter what start the next automation
|
|
85
|
+
await this.start()
|
|
86
|
+
}
|
|
87
|
+
}, delay)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
stop() {
|
|
93
|
+
if (this.timeoutId) {
|
|
94
|
+
clearTimeout(this.timeoutId)
|
|
95
|
+
this.timeoutId = undefined
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async update(hash: string, automation: SentinelAutomationPayload, restart = true) {
|
|
100
|
+
await this.remove(hash, false)
|
|
101
|
+
await this.add(automation, false)
|
|
102
|
+
if (restart) await this.restart()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private async trigger(automation: SentinelIntervalAutomationPayload) {
|
|
106
|
+
const wrapper = new SentinelIntervalAutomationWrapper(automation)
|
|
107
|
+
await this.remove(await wrapper.hashAsync(), false)
|
|
108
|
+
wrapper.next()
|
|
109
|
+
await this.add(wrapper.jsonPayload(), false)
|
|
110
|
+
const triggerResult = await this.sentinel.report()
|
|
111
|
+
this.onTriggerResult?.(triggerResult)
|
|
112
|
+
// await this.start()
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MemorySentinel'
|