@stacksjs/ts-cloud 0.7.105 → 0.7.109
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/alerts/delivery.d.ts +10 -0
- package/dist/alerts/model.d.ts +1 -1
- package/dist/api/client.d.ts +53 -0
- package/dist/api/handler.d.ts +10 -0
- package/dist/api/service.d.ts +1 -0
- package/dist/aws/index.js +10 -10
- package/dist/aws/lambda.d.ts +19 -0
- package/dist/bin/cli.js +1677 -1370
- package/dist/bin/dashboard-server.js +926 -620
- package/dist/{chunk-fmt40tpw.js → chunk-53jatzaw.js} +2 -2
- package/dist/{chunk-ef2dwfrg.js → chunk-8kh577qh.js} +5 -5
- package/dist/{chunk-s9c2x5jv.js → chunk-9v2pwxv9.js} +2 -2
- package/dist/chunk-aa2dkn7b.js +205 -0
- package/dist/{chunk-vkd2y0fs.js → chunk-cktwk9hy.js} +7169 -14800
- package/dist/{chunk-dmzez5f8.js → chunk-d3vx2ekv.js} +123 -28
- package/dist/chunk-grx1rp09.js +12163 -0
- package/dist/{chunk-c6db5m63.js → chunk-t2s3gdq6.js} +253 -8
- package/dist/chunk-va2yd85b.js +200 -0
- package/dist/{chunk-3g4r09dd.js → chunk-wmbep3b5.js} +1 -1
- package/dist/{chunk-ybcz6sxc.js → chunk-x6yzgv7y.js} +33 -0
- package/dist/chunk-xrtwv3v2.js +420 -0
- package/dist/control-plane/types.d.ts +1 -1
- package/dist/deploy/dashboard-bundle-encoding.test.d.ts +1 -0
- package/dist/deploy/dashboard-payload.d.ts +20 -0
- package/dist/deploy/dashboard-payload.test.d.ts +1 -0
- package/dist/deploy/dashboard-protection.test.d.ts +1 -0
- package/dist/deploy/dashboard-spend.test.d.ts +1 -0
- package/dist/deploy/dashboard-topology.d.ts +85 -0
- package/dist/deploy/dashboard-topology.test.d.ts +1 -0
- package/dist/deploy/egress-collection.d.ts +60 -0
- package/dist/deploy/index.d.ts +2 -0
- package/dist/deploy/index.js +23 -8
- package/dist/deploy/local-dashboard-server.d.ts +2 -0
- package/dist/drivers/index.js +4 -2
- package/dist/drivers/shared/protection.d.ts +43 -0
- package/dist/drivers/shared/protection.test.d.ts +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +364 -102
- package/dist/protection/controls.d.ts +113 -0
- package/dist/protection/controls.test.d.ts +1 -0
- package/dist/protection/ddos.d.ts +130 -0
- package/dist/protection/ddos.test.d.ts +1 -0
- package/dist/protection/index.d.ts +11 -0
- package/dist/protection/index.js +87 -0
- package/dist/protection/ratelimit.d.ts +154 -0
- package/dist/protection/ratelimit.test.d.ts +1 -0
- package/dist/protection/waf.d.ts +143 -0
- package/dist/protection/waf.test.d.ts +1 -0
- package/dist/spend/anomaly-config.d.ts +130 -0
- package/dist/spend/anomaly-config.test.d.ts +1 -0
- package/dist/spend/anomaly.d.ts +114 -0
- package/dist/spend/anomaly.test.d.ts +1 -0
- package/dist/spend/api.d.ts +42 -0
- package/dist/spend/api.test.d.ts +1 -0
- package/dist/spend/appliers.d.ts +126 -0
- package/dist/spend/docs.test.d.ts +1 -0
- package/dist/spend/enforcement.d.ts +111 -0
- package/dist/spend/enforcement.integration.test.d.ts +1 -0
- package/dist/spend/evaluator.d.ts +70 -0
- package/dist/spend/evaluator.test.d.ts +1 -0
- package/dist/spend/focus.d.ts +93 -0
- package/dist/spend/focus.test.d.ts +1 -0
- package/dist/spend/gate.d.ts +96 -0
- package/dist/spend/index.d.ts +20 -0
- package/dist/spend/index.js +173 -0
- package/dist/spend/lease.d.ts +54 -0
- package/dist/spend/lease.test.d.ts +1 -0
- package/dist/spend/meter.d.ts +70 -0
- package/dist/spend/meter.test.d.ts +1 -0
- package/dist/spend/model.d.ts +228 -0
- package/dist/spend/notifications.d.ts +58 -0
- package/dist/spend/pricing.d.ts +109 -0
- package/dist/spend/pricing.test.d.ts +1 -0
- package/dist/spend/projection.d.ts +36 -0
- package/dist/spend/runner.d.ts +130 -0
- package/dist/spend/service.d.ts +129 -0
- package/dist/spend/signals.d.ts +110 -0
- package/dist/spend/signals.test.d.ts +1 -0
- package/dist/spend/sms-sdk.test.d.ts +1 -0
- package/dist/spend/spend.integration.test.d.ts +1 -0
- package/dist/spend/store.d.ts +166 -0
- package/dist/spend/transports.d.ts +169 -0
- package/dist/spend/transports.test.d.ts +1 -0
- package/dist/spend/window.d.ts +52 -0
- package/dist/spend/window.test.d.ts +1 -0
- package/dist/ui/404.html +2 -0
- package/dist/ui/access-denied.html +2 -2
- package/dist/ui/account/automation.html +4 -4
- package/dist/ui/account/security.html +2 -2
- package/dist/ui/applications/compose.html +4 -4
- package/dist/ui/applications/new.html +2 -2
- package/dist/ui/data/backups.html +22 -9
- package/dist/ui/data/services.html +22 -9
- package/dist/ui/data/volumes.html +22 -9
- package/dist/ui/index.html +22 -9
- package/dist/ui/infrastructure/topology.html +2223 -0
- package/dist/ui/integrations.html +2 -2
- package/dist/ui/operations/alerts.html +22 -9
- package/dist/ui/operations/configuration.html +4 -4
- package/dist/ui/operations/jobs.html +22 -9
- package/dist/ui/operations/maintenance.html +4 -4
- package/dist/ui/operations/observability.html +22 -9
- package/dist/ui/operations/previews.html +4 -4
- package/dist/ui/operations/queue.html +4 -4
- package/dist/ui/operations/regions.html +3 -3
- package/dist/ui/operations/releases.html +3 -3
- package/dist/ui/operations/spend.html +1634 -0
- package/dist/ui/operations/workloads.html +22 -9
- package/dist/ui/security.html +2 -2
- package/dist/ui/server/actions.html +4 -4
- package/dist/ui/server/activity.html +2 -2
- package/dist/ui/server/capacity.html +4 -4
- package/dist/ui/server/database.html +4 -4
- package/dist/ui/server/deployments.html +4 -4
- package/dist/ui/server/diagnostics.html +2 -2
- package/dist/ui/server/firewall.html +100 -161
- package/dist/ui/server/fleet.html +22 -9
- package/dist/ui/server/logs.html +22 -9
- package/dist/ui/server/metrics.html +130 -14
- package/dist/ui/server/security.html +2 -2
- package/dist/ui/server/services.html +1 -1
- package/dist/ui/server/sites.html +22 -9
- package/dist/ui/server/ssh-keys.html +3 -3
- package/dist/ui/server/team.html +4 -4
- package/dist/ui/server/terminal.html +2 -2
- package/dist/ui/serverless/alarms.html +4 -4
- package/dist/ui/serverless/assets.html +2 -2
- package/dist/ui/serverless/cost.html +2 -2
- package/dist/ui/serverless/data.html +4 -4
- package/dist/ui/serverless/deployments.html +2 -2
- package/dist/ui/serverless/firewall.html +2 -2
- package/dist/ui/serverless/functions.html +4 -4
- package/dist/ui/serverless/logs.html +22 -9
- package/dist/ui/serverless/metrics.html +2 -2
- package/dist/ui/serverless/queues.html +4 -4
- package/dist/ui/serverless/secrets.html +4 -4
- package/dist/ui/serverless/traces.html +4 -4
- package/dist/ui/serverless.html +4 -4
- package/dist/ui-src/functions/requestJson.ts +41 -0
- package/dist/ui-src/functions/useOpConfirm.ts +132 -0
- package/dist/ui-src/functions/usePolling.ts +98 -0
- package/dist/ui-src/functions/useTerminal.ts +79 -0
- package/dist/ui-src/pages/infrastructure/topology.stx +517 -0
- package/dist/ui-src/pages/operations/spend.stx +225 -0
- package/dist/ui-src/pages/partials/nav.stx +4 -0
- package/dist/ui-src/pages/server/firewall.stx +89 -0
- package/dist/ui-src/pages/server/metrics.stx +140 -6
- package/dist/ui-src/src/topology.ts +485 -0
- package/package.json +11 -3
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export interface OperationConfirmation {
|
|
2
|
+
operation: string
|
|
3
|
+
confirm: string
|
|
4
|
+
verb: string
|
|
5
|
+
to?: string | null
|
|
6
|
+
apiConfirm?: string
|
|
7
|
+
danger?: boolean
|
|
8
|
+
label?: string
|
|
9
|
+
[key: string]: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface OperationResult {
|
|
13
|
+
output: string
|
|
14
|
+
ok?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type Signal<T> = (() => T) & { set: (value: T) => void }
|
|
18
|
+
|
|
19
|
+
interface HTMLElement {
|
|
20
|
+
focus: () => void
|
|
21
|
+
scrollIntoView: (options?: { behavior?: string; block?: string }) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type HTMLInputElement = HTMLElement
|
|
25
|
+
|
|
26
|
+
export interface OperationConfirmationController {
|
|
27
|
+
opOutput: Signal<string>
|
|
28
|
+
opShown: Signal<boolean>
|
|
29
|
+
pending: Signal<OperationConfirmation | null>
|
|
30
|
+
typed: Signal<string>
|
|
31
|
+
busy: Signal<boolean>
|
|
32
|
+
confirmTok: Signal<string>
|
|
33
|
+
confirmVerb: Signal<string>
|
|
34
|
+
confirmDanger: Signal<boolean>
|
|
35
|
+
confirmLabel: Signal<string>
|
|
36
|
+
canRun: Signal<boolean>
|
|
37
|
+
askOp: (
|
|
38
|
+
operation: string,
|
|
39
|
+
confirm: string,
|
|
40
|
+
verb: string,
|
|
41
|
+
to?: string | null,
|
|
42
|
+
apiConfirm?: string,
|
|
43
|
+
options?: Partial<OperationConfirmation>,
|
|
44
|
+
) => void
|
|
45
|
+
cancelOp: () => void
|
|
46
|
+
runOp: () => Promise<void>
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare function state<T>(_value: T): Signal<T>
|
|
50
|
+
declare function derived<T>(_value: () => T): Signal<T>
|
|
51
|
+
declare function useRef<T = HTMLElement>(_name: string): { current: T | null }
|
|
52
|
+
|
|
53
|
+
const DANGEROUS_OPERATION = /(?:^|:)(?:delete|disable|destroy|purge|remove|restore|rollback|stop)(?::|$)/i
|
|
54
|
+
|
|
55
|
+
export function operationIsDangerous(operation: Pick<OperationConfirmation, 'operation' | 'danger'>): boolean {
|
|
56
|
+
return operation.danger ?? DANGEROUS_OPERATION.test(operation.operation)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* One confirmation lifecycle for dashboard operations. The caller owns only
|
|
61
|
+
* the API request; this composable owns staging, exact-token validation,
|
|
62
|
+
* in-flight locking, focus, output, cancellation, and error presentation.
|
|
63
|
+
*/
|
|
64
|
+
export function useOpConfirm(
|
|
65
|
+
execute: (operation: OperationConfirmation) => Promise<string | OperationResult>,
|
|
66
|
+
): OperationConfirmationController {
|
|
67
|
+
const opOutput = state('')
|
|
68
|
+
const opShown = state(false)
|
|
69
|
+
const pending = state<OperationConfirmation | null>(null)
|
|
70
|
+
const typed = state('')
|
|
71
|
+
const busy = state(false)
|
|
72
|
+
const confirmInput = useRef<HTMLInputElement>('opConfirmInput')
|
|
73
|
+
const confirmTok = derived(() => pending()?.confirm ?? '')
|
|
74
|
+
const confirmVerb = derived(() => pending()?.verb ?? '')
|
|
75
|
+
const confirmDanger = derived(() => (pending() ? operationIsDangerous(pending()!) : false))
|
|
76
|
+
const confirmLabel = derived(() => pending()?.label ?? (confirmDanger() ? 'Confirm operation' : 'Run operation'))
|
|
77
|
+
const canRun = derived(() => !!pending() && !busy() && typed() === pending()!.confirm)
|
|
78
|
+
|
|
79
|
+
function askOp(
|
|
80
|
+
operation: string,
|
|
81
|
+
confirm: string,
|
|
82
|
+
verb: string,
|
|
83
|
+
to?: string | null,
|
|
84
|
+
apiConfirm?: string,
|
|
85
|
+
options: Partial<OperationConfirmation> = {},
|
|
86
|
+
) {
|
|
87
|
+
pending.set({ ...options, operation, confirm, verb, to: to ?? null, apiConfirm: apiConfirm || confirm })
|
|
88
|
+
typed.set('')
|
|
89
|
+
confirmInput.current?.focus()
|
|
90
|
+
confirmInput.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function cancelOp() {
|
|
94
|
+
if (busy()) return
|
|
95
|
+
pending.set(null)
|
|
96
|
+
typed.set('')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function runOp() {
|
|
100
|
+
const operation = pending()
|
|
101
|
+
if (!operation || busy() || typed() !== operation.confirm) return
|
|
102
|
+
busy.set(true)
|
|
103
|
+
opShown.set(true)
|
|
104
|
+
opOutput.set(`Running ${operation.operation}...`)
|
|
105
|
+
try {
|
|
106
|
+
const result = await execute(operation)
|
|
107
|
+
opOutput.set(typeof result === 'string' ? result : result.output)
|
|
108
|
+
pending.set(null)
|
|
109
|
+
typed.set('')
|
|
110
|
+
} catch (error) {
|
|
111
|
+
opOutput.set(`FAILED ${operation.operation}\n\n${error instanceof Error ? error.message : String(error)}`)
|
|
112
|
+
} finally {
|
|
113
|
+
busy.set(false)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
opOutput,
|
|
119
|
+
opShown,
|
|
120
|
+
pending,
|
|
121
|
+
typed,
|
|
122
|
+
busy,
|
|
123
|
+
confirmTok,
|
|
124
|
+
confirmVerb,
|
|
125
|
+
confirmDanger,
|
|
126
|
+
confirmLabel,
|
|
127
|
+
canRun,
|
|
128
|
+
askOp,
|
|
129
|
+
cancelOp,
|
|
130
|
+
runOp,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export type Signal<T> = (() => T) & { set: (value: T) => void }
|
|
2
|
+
declare function state<T>(_value: T): Signal<T>
|
|
3
|
+
declare function derived<T>(_value: () => T): Signal<T>
|
|
4
|
+
declare function onMount(_callback: () => void | (() => void)): void
|
|
5
|
+
declare function useInterval(
|
|
6
|
+
_interval: number,
|
|
7
|
+
_options?: { immediate?: boolean },
|
|
8
|
+
): { subscribe: (callback: () => void) => () => void; pause: () => void }
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `snapshot` means the cockpit was exported without its control plane — the
|
|
12
|
+
* static + htpasswd deploy (`TS_CLOUD_UI_STATIC`). The data baked into the page
|
|
13
|
+
* is real, it just cannot be refreshed, which is a different thing from a
|
|
14
|
+
* refresh that failed and must not be reported as an error.
|
|
15
|
+
*/
|
|
16
|
+
export type PollingState = 'loading' | 'live' | 'stale' | 'snapshot'
|
|
17
|
+
|
|
18
|
+
export interface PollingController {
|
|
19
|
+
pollingState: Signal<PollingState>
|
|
20
|
+
pollingError: Signal<string>
|
|
21
|
+
pollingLabel: Signal<PollingState>
|
|
22
|
+
lastUpdatedAt: Signal<Date | null>
|
|
23
|
+
/** True once the deployment has told us it serves no API. */
|
|
24
|
+
isSnapshot: Signal<boolean>
|
|
25
|
+
refresh: () => Promise<void>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PollingOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Treat a missing API route as "this build has no control plane" instead of
|
|
31
|
+
* as an error.
|
|
32
|
+
*
|
|
33
|
+
* Opt in only from a page whose data is fully baked in at build time, so
|
|
34
|
+
* there is genuinely nothing lost by not polling — the infrastructure map,
|
|
35
|
+
* for instance. A page that manages live resources must NOT set this: its
|
|
36
|
+
* buttons post to that same missing API, so a 404 there is a real fault and
|
|
37
|
+
* calling it a snapshot would hide it. Off by default for that reason.
|
|
38
|
+
*/
|
|
39
|
+
snapshotWhenApiMissing?: boolean
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* A build served without its control plane answers the API route itself rather
|
|
44
|
+
* than failing in transit: a static host 404s the path, and a proxy that knows
|
|
45
|
+
* the route but not the method answers 405/501. Anything else — a 500, a
|
|
46
|
+
* dropped connection, malformed JSON — is a real failure and stays one.
|
|
47
|
+
*
|
|
48
|
+
* Only errors carrying an HTTP status qualify, so the caller must be using
|
|
49
|
+
* `requestJson` (which raises `DashboardRequestError`); a bare `fetch` that
|
|
50
|
+
* throws a plain `Error` keeps the stale-with-error behaviour.
|
|
51
|
+
*/
|
|
52
|
+
function servedWithoutApi(error: unknown): boolean {
|
|
53
|
+
const status = (error as { status?: unknown })?.status
|
|
54
|
+
return status === 404 || status === 405 || status === 501
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Poll immediately and on a lifecycle-owned interval, retaining stale data on failure. */
|
|
58
|
+
export function usePolling(
|
|
59
|
+
task: () => Promise<void>,
|
|
60
|
+
intervalMs: number,
|
|
61
|
+
options: PollingOptions = {},
|
|
62
|
+
): PollingController {
|
|
63
|
+
const pollingState = state<PollingState>('loading')
|
|
64
|
+
const pollingError = state('')
|
|
65
|
+
const lastUpdatedAt = state<Date | null>(null)
|
|
66
|
+
const isSnapshot = state(false)
|
|
67
|
+
const pollingLabel = derived<PollingState>(() => pollingState())
|
|
68
|
+
|
|
69
|
+
const interval = useInterval(intervalMs)
|
|
70
|
+
|
|
71
|
+
async function refresh() {
|
|
72
|
+
try {
|
|
73
|
+
await task()
|
|
74
|
+
pollingState.set('live')
|
|
75
|
+
pollingError.set('')
|
|
76
|
+
isSnapshot.set(false)
|
|
77
|
+
lastUpdatedAt.set(new Date())
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (options.snapshotWhenApiMissing && servedWithoutApi(error)) {
|
|
80
|
+
// Nothing here will ever answer, so stop asking. The page keeps the
|
|
81
|
+
// data it was built with and says it is a snapshot, not an outage.
|
|
82
|
+
isSnapshot.set(true)
|
|
83
|
+
pollingState.set('snapshot')
|
|
84
|
+
pollingError.set('')
|
|
85
|
+
interval.pause()
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
pollingState.set('stale')
|
|
89
|
+
pollingError.set(error instanceof Error ? error.message : String(error))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
interval.subscribe(refresh)
|
|
94
|
+
onMount(() => {
|
|
95
|
+
refresh()
|
|
96
|
+
})
|
|
97
|
+
return { pollingState, pollingError, pollingLabel, lastUpdatedAt, isSnapshot, refresh }
|
|
98
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export type Signal<T> = (() => T) & { set: (value: T) => void }
|
|
2
|
+
|
|
3
|
+
interface HTMLElement {
|
|
4
|
+
scrollTop: number
|
|
5
|
+
scrollHeight: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
declare const location: { protocol: string; host: string }
|
|
9
|
+
declare function state<T>(_value: T): Signal<T>
|
|
10
|
+
declare function derived<T>(_value: () => T): Signal<T>
|
|
11
|
+
declare function effect(_callback: () => void): void
|
|
12
|
+
declare function useRef<T = HTMLElement>(_name: string): { current: T | null }
|
|
13
|
+
declare function useWebSocket(
|
|
14
|
+
_url: string,
|
|
15
|
+
_options?: Record<string, unknown>,
|
|
16
|
+
): {
|
|
17
|
+
status: Signal<'CONNECTING' | 'OPEN' | 'CLOSING' | 'CLOSED'>
|
|
18
|
+
send: (data: string) => void
|
|
19
|
+
close: () => void
|
|
20
|
+
connect: () => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TerminalController {
|
|
24
|
+
buf: Signal<string>
|
|
25
|
+
cmd: Signal<string>
|
|
26
|
+
status: Signal<string>
|
|
27
|
+
send: () => void
|
|
28
|
+
reconnect: () => void
|
|
29
|
+
clearOut: () => void
|
|
30
|
+
statusTone: (value: string) => 'ok' | 'bad' | 'warn'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Terminal transport and viewport behavior, isolated from the stx template. */
|
|
34
|
+
export function useTerminal(path: string = '/api/terminal'): TerminalController {
|
|
35
|
+
const buf = state('')
|
|
36
|
+
const cmd = state('')
|
|
37
|
+
const output = useRef<HTMLElement>('terminalOutput')
|
|
38
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
39
|
+
const url = `${protocol}//${location.host}${path}`
|
|
40
|
+
const socket = useWebSocket(url, {
|
|
41
|
+
reconnect: true,
|
|
42
|
+
maxReconnects: 10,
|
|
43
|
+
reconnectDelay: 1000,
|
|
44
|
+
onMessage(data: unknown) {
|
|
45
|
+
buf.set((buf() + String(data)).slice(-40000))
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
const status = derived(
|
|
49
|
+
() =>
|
|
50
|
+
({ CONNECTING: 'connecting', OPEN: 'connected', CLOSING: 'disconnecting', CLOSED: 'disconnected' })[
|
|
51
|
+
socket.status()
|
|
52
|
+
] || 'error',
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
effect(() => {
|
|
56
|
+
buf()
|
|
57
|
+
const element = output.current
|
|
58
|
+
if (element) element.scrollTop = element.scrollHeight
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
function send() {
|
|
62
|
+
const command = cmd()
|
|
63
|
+
if (!command.trim() || socket.status() !== 'OPEN') return
|
|
64
|
+
socket.send(command + '\n')
|
|
65
|
+
cmd.set('')
|
|
66
|
+
}
|
|
67
|
+
function reconnect() {
|
|
68
|
+
socket.close()
|
|
69
|
+
buf.set('')
|
|
70
|
+
socket.connect()
|
|
71
|
+
}
|
|
72
|
+
function clearOut() {
|
|
73
|
+
buf.set('')
|
|
74
|
+
}
|
|
75
|
+
function statusTone(value: string): 'ok' | 'bad' | 'warn' {
|
|
76
|
+
return value === 'connected' ? 'ok' : value === 'disconnected' || value === 'error' ? 'bad' : 'warn'
|
|
77
|
+
}
|
|
78
|
+
return { buf, cmd, status, send, reconnect, clearOut, statusTone }
|
|
79
|
+
}
|