@jango-blockchained/hoox-shared 1.0.8
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/README.md +27 -0
- package/dist/analytics.d.ts +22 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +34 -0
- package/dist/api-client.d.ts +34 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +124 -0
- package/dist/colors.d.ts +34 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +42 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +91 -0
- package/dist/d1/index.d.ts +5 -0
- package/dist/d1/index.d.ts.map +1 -0
- package/dist/d1/index.js +316 -0
- package/dist/d1/repository.d.ts +77 -0
- package/dist/d1/repository.d.ts.map +1 -0
- package/dist/d1/schemas.d.ts +85 -0
- package/dist/d1/schemas.d.ts.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +106 -0
- package/dist/exchange-client.d.ts +84 -0
- package/dist/exchange-client.d.ts.map +1 -0
- package/dist/exchange-client.js +104 -0
- package/dist/format-time.d.ts +24 -0
- package/dist/format-time.d.ts.map +1 -0
- package/dist/format-time.js +57 -0
- package/dist/formatters.d.ts +32 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +155 -0
- package/dist/health.d.ts +19 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +117 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2581 -0
- package/dist/kvKeys.d.ts +72 -0
- package/dist/kvKeys.d.ts.map +1 -0
- package/dist/kvKeys.js +107 -0
- package/dist/kvUtils.d.ts +26 -0
- package/dist/kvUtils.d.ts.map +1 -0
- package/dist/kvUtils.js +127 -0
- package/dist/legal.d.ts +11 -0
- package/dist/legal.d.ts.map +1 -0
- package/dist/legal.js +40 -0
- package/dist/middleware/auth.d.ts +59 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/cors.d.ts +28 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +10 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +375 -0
- package/dist/middleware/logger.d.ts +16 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/rate-limit.d.ts +24 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/security-headers.d.ts +39 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/validate.d.ts +21 -0
- package/dist/middleware/validate.d.ts.map +1 -0
- package/dist/router.d.ts +11 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +123 -0
- package/dist/schemas/index.d.ts +4 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +656 -0
- package/dist/schemas/registry.d.ts +8 -0
- package/dist/schemas/registry.d.ts.map +1 -0
- package/dist/schemas/types.d.ts +79 -0
- package/dist/schemas/types.d.ts.map +1 -0
- package/dist/schemas/validators.d.ts +35 -0
- package/dist/schemas/validators.d.ts.map +1 -0
- package/dist/service-bindings.d.ts +32 -0
- package/dist/service-bindings.d.ts.map +1 -0
- package/dist/service-bindings.js +31 -0
- package/dist/session.d.ts +46 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +79 -0
- package/dist/sse.d.ts +26 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +91 -0
- package/dist/stores/config-store.d.ts +87 -0
- package/dist/stores/config-store.d.ts.map +1 -0
- package/dist/stores/config-store.js +132 -0
- package/dist/stores/service-store.d.ts +206 -0
- package/dist/stores/service-store.d.ts.map +1 -0
- package/dist/stores/service-store.js +359 -0
- package/dist/stores/ui-store.d.ts +63 -0
- package/dist/stores/ui-store.d.ts.map +1 -0
- package/dist/stores/ui-store.js +62 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +1 -0
- package/dist/types/router.d.ts +22 -0
- package/dist/types/router.d.ts.map +1 -0
- package/dist/types/router.js +1 -0
- package/dist/types.d.ts +317 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +70 -0
- package/dist/wizard/engine.d.ts +42 -0
- package/dist/wizard/engine.d.ts.map +1 -0
- package/dist/wizard/index.d.ts +7 -0
- package/dist/wizard/index.d.ts.map +1 -0
- package/dist/wizard/index.js +519 -0
- package/dist/wizard/persistence.d.ts +14 -0
- package/dist/wizard/persistence.d.ts.map +1 -0
- package/dist/wizard/presets.d.ts +34 -0
- package/dist/wizard/presets.d.ts.map +1 -0
- package/dist/wizard/provisioner.d.ts +11 -0
- package/dist/wizard/provisioner.d.ts.map +1 -0
- package/dist/wizard/types.d.ts +72 -0
- package/dist/wizard/types.d.ts.map +1 -0
- package/package.json +83 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker Manifest Schema — canonical definition of what each worker expects.
|
|
3
|
+
*/
|
|
4
|
+
export interface WorkerManifest {
|
|
5
|
+
/** Worker name (e.g. "trade-worker") */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Path relative to project root (e.g. "workers/trade-worker") */
|
|
8
|
+
path: string;
|
|
9
|
+
/** Vars the worker declares in its wrangler.jsonc `vars` section */
|
|
10
|
+
vars: Record<string, VarDef>;
|
|
11
|
+
/** Service bindings this worker calls */
|
|
12
|
+
services: ServiceBindingDef[];
|
|
13
|
+
/** Infrastructure bindings this worker needs */
|
|
14
|
+
infrastructure: InfraBindings;
|
|
15
|
+
/** Middleware this worker imports from @jango-blockchained/hoox-shared */
|
|
16
|
+
middleware: string[];
|
|
17
|
+
/** Cron triggers (empty array = none) */
|
|
18
|
+
cron?: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface VarDef {
|
|
21
|
+
type: "secret" | "plaintext";
|
|
22
|
+
description: string;
|
|
23
|
+
/** Default value for plaintext vars (optional) */
|
|
24
|
+
default?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ServiceBindingDef {
|
|
27
|
+
binding: string;
|
|
28
|
+
service: string;
|
|
29
|
+
description: string;
|
|
30
|
+
}
|
|
31
|
+
export interface InfraBindings {
|
|
32
|
+
kv?: KVBindingDef[];
|
|
33
|
+
d1?: D1BindingDef[];
|
|
34
|
+
r2?: R2BindingDef[];
|
|
35
|
+
queues?: QueueBindingDef;
|
|
36
|
+
ai?: boolean;
|
|
37
|
+
vectorize?: VectorizeBindingDef[];
|
|
38
|
+
analyticsEngine?: boolean;
|
|
39
|
+
durableObjects?: DOBindingDef[];
|
|
40
|
+
browser?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface KVBindingDef {
|
|
43
|
+
binding: string;
|
|
44
|
+
description: string;
|
|
45
|
+
}
|
|
46
|
+
export interface D1BindingDef {
|
|
47
|
+
binding: string;
|
|
48
|
+
database: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
}
|
|
51
|
+
export interface R2BindingDef {
|
|
52
|
+
binding: string;
|
|
53
|
+
bucket: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface QueueBindingDef {
|
|
57
|
+
producer?: string[];
|
|
58
|
+
consumer?: string[];
|
|
59
|
+
}
|
|
60
|
+
export interface VectorizeBindingDef {
|
|
61
|
+
binding: string;
|
|
62
|
+
index: string;
|
|
63
|
+
}
|
|
64
|
+
export interface DOBindingDef {
|
|
65
|
+
name: string;
|
|
66
|
+
className: string;
|
|
67
|
+
}
|
|
68
|
+
export interface ValidationError {
|
|
69
|
+
worker: string;
|
|
70
|
+
severity: "error" | "warning";
|
|
71
|
+
message: string;
|
|
72
|
+
file?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface ValidationReport {
|
|
75
|
+
worker: string;
|
|
76
|
+
passed: boolean;
|
|
77
|
+
errors: ValidationError[];
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/schemas/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,gDAAgD;IAChD,cAAc,EAAE,aAAa,CAAC;IAC9B,0EAA0E;IAC1E,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { WorkerManifest, ValidationError } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate a per-worker wrangler.jsonc against its manifest.
|
|
4
|
+
* Returns an array of errors (empty = perfect match).
|
|
5
|
+
*/
|
|
6
|
+
export declare function validateWranglerJsonc(workerName: string, manifest: WorkerManifest, jsoncContent: string): ValidationError[];
|
|
7
|
+
/**
|
|
8
|
+
* Validate root wrangler.jsonc secrets list against manifest.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateRootSecrets(workerName: string, manifest: WorkerManifest, rootJsoncContent: string): ValidationError[];
|
|
11
|
+
/**
|
|
12
|
+
* Validate .dev.vars content against manifest.
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateDevVars(workerName: string, manifest: WorkerManifest, devVarsContent: string): ValidationError[];
|
|
15
|
+
/**
|
|
16
|
+
* Generate a complete per-worker wrangler.jsonc from a manifest.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateWranglerJsonc(manifest: WorkerManifest): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a .dev.vars template from a manifest.
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateDevVars(manifest: WorkerManifest): string;
|
|
23
|
+
/**
|
|
24
|
+
* Run all validators for a worker.
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateAll(workerName: string, manifest: WorkerManifest, files: {
|
|
27
|
+
wranglerJsonc: string;
|
|
28
|
+
rootWranglerJsonc: string;
|
|
29
|
+
devVars: string;
|
|
30
|
+
}): {
|
|
31
|
+
worker: string;
|
|
32
|
+
passed: boolean;
|
|
33
|
+
errors: ValidationError[];
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/schemas/validators.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElE;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,YAAY,EAAE,MAAM,GACnB,eAAe,EAAE,CA4GnB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,gBAAgB,EAAE,MAAM,GACvB,eAAe,EAAE,CAyCnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,cAAc,EAAE,MAAM,GACrB,eAAe,EAAE,CA6BnB;AAID;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAmItE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAYhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE;IACL,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,GACA;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,eAAe,EAAE,CAAA;CAAE,CAWhE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized helper for inter-worker service binding calls.
|
|
3
|
+
*
|
|
4
|
+
* Uses `Fetcher.fetch(url, init)` directly — avoids the `as unknown as Request`
|
|
5
|
+
* anti-pattern and inconsistent URL schemes used across workers
|
|
6
|
+
* (http://trade-service/webhook, http://localhost/query, https://internal/webhook, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Convention: URL path matches the route the target worker serves.
|
|
9
|
+
* Defaults to POST with JSON Content-Type body.
|
|
10
|
+
*
|
|
11
|
+
* Note: Uses `any` for the binding parameter to avoid `@cloudflare/workers-types`
|
|
12
|
+
* `Fetcher` type incompatibilities that can arise when different workspace
|
|
13
|
+
* packages resolve distinct instances of the types package. At runtime, service
|
|
14
|
+
* bindings from the Workers runtime always have a compatible `fetch` method.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // POST with JSON body (default)
|
|
18
|
+
* const response = await serviceFetch(env.TRADE_SERVICE, "/webhook", payload);
|
|
19
|
+
*
|
|
20
|
+
* // POST with extra headers
|
|
21
|
+
* const response = await serviceFetch(env.D1_SERVICE, "/query", { query, params }, {
|
|
22
|
+
* headers: { "X-Request-ID": requestId },
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // GET request (no body)
|
|
26
|
+
* const response = await serviceFetch(env.D1_SERVICE, "/health", undefined, { method: "GET" });
|
|
27
|
+
*/
|
|
28
|
+
export declare function serviceFetch(binding: any, path: string, body?: unknown, options?: {
|
|
29
|
+
method?: string;
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
}): Promise<Response>;
|
|
32
|
+
//# sourceMappingURL=service-bindings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-bindings.d.ts","sourceRoot":"","sources":["../src/service-bindings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC9D,OAAO,CAAC,QAAQ,CAAC,CASnB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// src/service-bindings.ts
|
|
19
|
+
function serviceFetch(binding, path, body, options) {
|
|
20
|
+
return binding.fetch(`http://internal${path}`, {
|
|
21
|
+
method: options?.method ?? "POST",
|
|
22
|
+
headers: {
|
|
23
|
+
"Content-Type": "application/json",
|
|
24
|
+
...options?.headers
|
|
25
|
+
},
|
|
26
|
+
body: body != null ? JSON.stringify(body) : undefined
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
serviceFetch
|
|
31
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session persistence — saves/restores TUI state to ~/.hoox/session.json.
|
|
3
|
+
*
|
|
4
|
+
* On destroy (app exit): serializes { activeView, sidebarExpanded, windowSize,
|
|
5
|
+
* lastData } so the user returns to the same state on next launch.
|
|
6
|
+
*
|
|
7
|
+
* On startup: restores previous activeView and sidebarExpanded state.
|
|
8
|
+
* Window size and lastData are saved for reference but re-detected/fetched fresh.
|
|
9
|
+
*
|
|
10
|
+
* Uses Bun's native file API for async read/write.
|
|
11
|
+
*/
|
|
12
|
+
import type { ViewId } from "./types";
|
|
13
|
+
export interface SessionState {
|
|
14
|
+
/** Last active view ID (e.g. "dashboard", "workers") */
|
|
15
|
+
activeView: ViewId;
|
|
16
|
+
/** Whether the sidebar was expanded */
|
|
17
|
+
sidebarExpanded: boolean;
|
|
18
|
+
/** Terminal window dimensions (cols × rows) */
|
|
19
|
+
windowSize: {
|
|
20
|
+
cols: number;
|
|
21
|
+
rows: number;
|
|
22
|
+
};
|
|
23
|
+
/** Timestamp of the last successful data fetch (ms) */
|
|
24
|
+
lastData: number;
|
|
25
|
+
/** ISO timestamp of when the session was saved */
|
|
26
|
+
savedAt: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Persist the current UI state to ~/.hoox/session.json.
|
|
30
|
+
* Call this on app destroy / clean shutdown.
|
|
31
|
+
*
|
|
32
|
+
* The ~/.hoox directory is created automatically by Bun.write if needed,
|
|
33
|
+
* but we attempt to verify existence first for clarity.
|
|
34
|
+
*/
|
|
35
|
+
export declare function saveSession(activeView: ViewId, sidebarExpanded: boolean, windowSize: {
|
|
36
|
+
cols: number;
|
|
37
|
+
rows: number;
|
|
38
|
+
}, lastData: number): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Restore the previous session state from ~/.hoox/session.json.
|
|
41
|
+
* Returns defaults if the file doesn't exist or can't be parsed.
|
|
42
|
+
*
|
|
43
|
+
* @returns The saved session state, or defaults
|
|
44
|
+
*/
|
|
45
|
+
export declare function restoreSession(): Promise<SessionState>;
|
|
46
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAItC,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,eAAe,EAAE,OAAO,CAAC;IACzB,+CAA+C;IAC/C,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAkBD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,OAAO,EACxB,UAAU,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC1C,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAkBf;AAID;;;;;GAKG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC,CA6B5D"}
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// src/session.ts
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
import { join } from "path";
|
|
21
|
+
var SESSION_FILE = join(homedir(), ".hoox", "session.json");
|
|
22
|
+
var DEFAULT_SESSION = {
|
|
23
|
+
activeView: "dashboard",
|
|
24
|
+
sidebarExpanded: true,
|
|
25
|
+
windowSize: { cols: 80, rows: 24 },
|
|
26
|
+
lastData: 0,
|
|
27
|
+
savedAt: new Date().toISOString()
|
|
28
|
+
};
|
|
29
|
+
async function saveSession(activeView, sidebarExpanded, windowSize, lastData) {
|
|
30
|
+
try {
|
|
31
|
+
const session = {
|
|
32
|
+
activeView,
|
|
33
|
+
sidebarExpanded,
|
|
34
|
+
windowSize,
|
|
35
|
+
lastData,
|
|
36
|
+
savedAt: new Date().toISOString()
|
|
37
|
+
};
|
|
38
|
+
await Bun.write(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("[session] Failed to save session:", error.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
async function restoreSession() {
|
|
44
|
+
try {
|
|
45
|
+
const file = Bun.file(SESSION_FILE);
|
|
46
|
+
const exists = await file.exists();
|
|
47
|
+
if (!exists)
|
|
48
|
+
return { ...DEFAULT_SESSION };
|
|
49
|
+
const raw = await file.text();
|
|
50
|
+
const parsed = JSON.parse(raw);
|
|
51
|
+
return {
|
|
52
|
+
activeView: validateViewId(parsed.activeView) ? parsed.activeView : DEFAULT_SESSION.activeView,
|
|
53
|
+
sidebarExpanded: typeof parsed.sidebarExpanded === "boolean" ? parsed.sidebarExpanded : DEFAULT_SESSION.sidebarExpanded,
|
|
54
|
+
windowSize: parsed.windowSize ?? DEFAULT_SESSION.windowSize,
|
|
55
|
+
lastData: typeof parsed.lastData === "number" ? parsed.lastData : DEFAULT_SESSION.lastData,
|
|
56
|
+
savedAt: parsed.savedAt ?? DEFAULT_SESSION.savedAt
|
|
57
|
+
};
|
|
58
|
+
} catch {
|
|
59
|
+
return { ...DEFAULT_SESSION };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
var VALID_VIEW_IDS = new Set([
|
|
63
|
+
"dashboard",
|
|
64
|
+
"workers",
|
|
65
|
+
"worker-detail",
|
|
66
|
+
"trade-monitor",
|
|
67
|
+
"logs-viewer",
|
|
68
|
+
"service-manager",
|
|
69
|
+
"config-editor",
|
|
70
|
+
"setup-wizard",
|
|
71
|
+
"settings"
|
|
72
|
+
]);
|
|
73
|
+
function validateViewId(id) {
|
|
74
|
+
return typeof id === "string" && VALID_VIEW_IDS.has(id);
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
saveSession,
|
|
78
|
+
restoreSession
|
|
79
|
+
};
|
package/dist/sse.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE Streaming Client — subscribes to Server-Sent Events from hoox-setup API.
|
|
3
|
+
*
|
|
4
|
+
* Uses Bun's native fetch with ReadableStream reader for SSE parsing.
|
|
5
|
+
* Supports reconnection with exponential backoff.
|
|
6
|
+
* Events must be JSON-encoded in the "data:" field.
|
|
7
|
+
*/
|
|
8
|
+
export type SSECallback<T> = (event: T) => void;
|
|
9
|
+
export type SSEStatusCallback = (status: "connected" | "reconnecting" | "disconnected") => void;
|
|
10
|
+
interface SSESubscription {
|
|
11
|
+
abort: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Subscribe to an SSE endpoint and invoke the callback for each parsed event.
|
|
15
|
+
*
|
|
16
|
+
* Automatically reconnects on connection loss with exponential backoff.
|
|
17
|
+
* Returns an object with an `abort()` method to cancel the subscription.
|
|
18
|
+
*
|
|
19
|
+
* @param path API path (e.g. "/trades/stream")
|
|
20
|
+
* @param callback Called with each parsed event of type T
|
|
21
|
+
* @param onStatus Optional callback for connection status updates
|
|
22
|
+
* @returns Subscription controller with abort method
|
|
23
|
+
*/
|
|
24
|
+
export declare function subscribeSSE<T>(path: string, callback: SSECallback<T>, onStatus?: SSEStatusCallback): SSESubscription;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAYH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAChD,MAAM,MAAM,iBAAiB,GAAG,CAC9B,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,cAAc,KAClD,IAAI,CAAC;AAEV,UAAU,eAAe;IACvB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,EACxB,QAAQ,CAAC,EAAE,iBAAiB,GAC3B,eAAe,CAgFjB"}
|
package/dist/sse.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// src/sse.ts
|
|
19
|
+
var exports_sse = {};
|
|
20
|
+
__export(exports_sse, {
|
|
21
|
+
subscribeSSE: () => subscribeSSE
|
|
22
|
+
});
|
|
23
|
+
function subscribeSSE(path, callback, onStatus) {
|
|
24
|
+
let aborted = false;
|
|
25
|
+
const run = async () => {
|
|
26
|
+
let attempt = 0;
|
|
27
|
+
while (!aborted && attempt < MAX_RECONNECT_ATTEMPTS) {
|
|
28
|
+
try {
|
|
29
|
+
const url = `${API_BASE}${path}`;
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
headers: {
|
|
32
|
+
Accept: "text/event-stream",
|
|
33
|
+
...API_TOKEN ? { Authorization: `Bearer ${API_TOKEN}` } : {}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (!response.ok || !response.body) {
|
|
37
|
+
throw new Error(`SSE connection failed: HTTP ${response.status}`);
|
|
38
|
+
}
|
|
39
|
+
attempt = 0;
|
|
40
|
+
onStatus?.("connected");
|
|
41
|
+
const reader = response.body.getReader();
|
|
42
|
+
const decoder = new TextDecoder;
|
|
43
|
+
let buffer = "";
|
|
44
|
+
while (!aborted) {
|
|
45
|
+
const { done, value } = await reader.read();
|
|
46
|
+
if (done)
|
|
47
|
+
break;
|
|
48
|
+
buffer += decoder.decode(value, { stream: true });
|
|
49
|
+
const lines = buffer.split(`
|
|
50
|
+
`);
|
|
51
|
+
buffer = lines.pop() || "";
|
|
52
|
+
for (const line of lines) {
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
if (trimmed.startsWith("data: ")) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = JSON.parse(trimmed.slice(6));
|
|
57
|
+
callback(parsed);
|
|
58
|
+
} catch {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (aborted)
|
|
64
|
+
break;
|
|
65
|
+
onStatus?.("reconnecting");
|
|
66
|
+
const delay = Math.min(RECONNECT_BASE_MS * Math.pow(2, attempt), RECONNECT_MAX_MS);
|
|
67
|
+
attempt++;
|
|
68
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!aborted) {
|
|
72
|
+
onStatus?.("disconnected");
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
run();
|
|
76
|
+
return {
|
|
77
|
+
abort: () => {
|
|
78
|
+
aborted = true;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
var API_BASE, API_TOKEN, RECONNECT_BASE_MS = 1000, RECONNECT_MAX_MS = 16000, MAX_RECONNECT_ATTEMPTS = 10;
|
|
83
|
+
var init_sse = __esm(() => {
|
|
84
|
+
API_BASE = process.env.HOOX_API_URL || "http://localhost:8787";
|
|
85
|
+
API_TOKEN = process.env.HOOX_API_TOKEN || "";
|
|
86
|
+
});
|
|
87
|
+
init_sse();
|
|
88
|
+
|
|
89
|
+
export {
|
|
90
|
+
subscribeSSE
|
|
91
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ViewId, LogFilter, NotificationPreferences } from "../types";
|
|
2
|
+
export interface ConfigState {
|
|
3
|
+
theme: "dark" | "light";
|
|
4
|
+
refreshIntervalMs: number;
|
|
5
|
+
defaultView: ViewId;
|
|
6
|
+
activeExchanges: string[];
|
|
7
|
+
keyboardShortcuts: Record<string, string>;
|
|
8
|
+
logFilters: LogFilter;
|
|
9
|
+
notifications: NotificationPreferences;
|
|
10
|
+
soundEnabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface ConfigActions {
|
|
13
|
+
/** Merge partial config — only provided fields are updated. */
|
|
14
|
+
updateConfig: (partial: Partial<ConfigState>) => void;
|
|
15
|
+
/** Reset all settings to factory defaults. */
|
|
16
|
+
resetDefaults: () => void;
|
|
17
|
+
/** Update a single keyboard shortcut. */
|
|
18
|
+
setShortcut: (action: string, key: string) => void;
|
|
19
|
+
/** Toggle a specific notification channel. */
|
|
20
|
+
toggleNotification: (channel: keyof NotificationPreferences) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare const useConfigStore: import("zustand").UseBoundStore<Omit<Omit<import("zustand").StoreApi<ConfigState & ConfigActions>, "setState" | "persist"> & {
|
|
23
|
+
setState(partial: (ConfigState & ConfigActions) | Partial<ConfigState & ConfigActions> | ((state: ConfigState & ConfigActions) => (ConfigState & ConfigActions) | Partial<ConfigState & ConfigActions>), replace?: false | undefined): unknown;
|
|
24
|
+
setState(state: (ConfigState & ConfigActions) | ((state: ConfigState & ConfigActions) => ConfigState & ConfigActions), replace: true): unknown;
|
|
25
|
+
persist: {
|
|
26
|
+
setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ConfigState & ConfigActions, unknown, unknown>>) => void;
|
|
27
|
+
clearStorage: () => void;
|
|
28
|
+
rehydrate: () => Promise<void> | void;
|
|
29
|
+
hasHydrated: () => boolean;
|
|
30
|
+
onHydrate: (fn: (state: ConfigState & ConfigActions) => void) => () => void;
|
|
31
|
+
onFinishHydration: (fn: (state: ConfigState & ConfigActions) => void) => () => void;
|
|
32
|
+
getOptions: () => Partial<import("zustand/middleware").PersistOptions<ConfigState & ConfigActions, unknown, unknown>>;
|
|
33
|
+
};
|
|
34
|
+
}, "setState"> & {
|
|
35
|
+
setState(nextStateOrUpdater: (ConfigState & ConfigActions) | Partial<ConfigState & ConfigActions> | ((state: {
|
|
36
|
+
theme: "dark" | "light";
|
|
37
|
+
refreshIntervalMs: number;
|
|
38
|
+
defaultView: ViewId;
|
|
39
|
+
activeExchanges: string[];
|
|
40
|
+
keyboardShortcuts: {
|
|
41
|
+
[x: string]: string;
|
|
42
|
+
};
|
|
43
|
+
logFilters: {
|
|
44
|
+
levels: import("..").LogLevel[];
|
|
45
|
+
workers: string[];
|
|
46
|
+
searchText: string;
|
|
47
|
+
};
|
|
48
|
+
notifications: {
|
|
49
|
+
alerts: boolean;
|
|
50
|
+
trades: boolean;
|
|
51
|
+
debug: boolean;
|
|
52
|
+
system: boolean;
|
|
53
|
+
};
|
|
54
|
+
soundEnabled: boolean;
|
|
55
|
+
updateConfig: (partial: Partial<ConfigState>) => void;
|
|
56
|
+
resetDefaults: () => void;
|
|
57
|
+
setShortcut: (action: string, key: string) => void;
|
|
58
|
+
toggleNotification: (channel: keyof NotificationPreferences) => void;
|
|
59
|
+
}) => void), shouldReplace?: false): unknown;
|
|
60
|
+
setState(nextStateOrUpdater: (ConfigState & ConfigActions) | ((state: {
|
|
61
|
+
theme: "dark" | "light";
|
|
62
|
+
refreshIntervalMs: number;
|
|
63
|
+
defaultView: ViewId;
|
|
64
|
+
activeExchanges: string[];
|
|
65
|
+
keyboardShortcuts: {
|
|
66
|
+
[x: string]: string;
|
|
67
|
+
};
|
|
68
|
+
logFilters: {
|
|
69
|
+
levels: import("..").LogLevel[];
|
|
70
|
+
workers: string[];
|
|
71
|
+
searchText: string;
|
|
72
|
+
};
|
|
73
|
+
notifications: {
|
|
74
|
+
alerts: boolean;
|
|
75
|
+
trades: boolean;
|
|
76
|
+
debug: boolean;
|
|
77
|
+
system: boolean;
|
|
78
|
+
};
|
|
79
|
+
soundEnabled: boolean;
|
|
80
|
+
updateConfig: (partial: Partial<ConfigState>) => void;
|
|
81
|
+
resetDefaults: () => void;
|
|
82
|
+
setShortcut: (action: string, key: string) => void;
|
|
83
|
+
toggleNotification: (channel: keyof NotificationPreferences) => void;
|
|
84
|
+
}) => void), shouldReplace: true): unknown;
|
|
85
|
+
}>;
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=config-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-store.d.ts","sourceRoot":"","sources":["../../src/stores/config-store.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AA2E3E,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,EAAE,SAAS,CAAC;IACtB,aAAa,EAAE,uBAAuB,CAAC;IACvC,YAAY,EAAE,OAAO,CAAC;CACvB;AAID,UAAU,aAAa;IACrB,+DAA+D;IAC/D,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACtD,8CAA8C;IAC9C,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,yCAAyC;IACzC,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnD,8CAA8C;IAC9C,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI,CAAC;CACtE;AAiBD,eAAO,MAAM,cAAc;;;;;;;;;;;;;;eAtClB,MAAM,GAAG,OAAO;2BACJ,MAAM;qBACZ,MAAM;;;;;;;;;;;;;;;;sBAKL,OAAO;sBAOP,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI;uBAEtC,MAAM,IAAI;qBAEZ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI;4BAE9B,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI;;;eApB7D,MAAM,GAAG,OAAO;2BACJ,MAAM;qBACZ,MAAM;;;;;;;;;;;;;;;;sBAKL,OAAO;sBAOP,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI;uBAEtC,MAAM,IAAI;qBAEZ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI;4BAE9B,CAAC,OAAO,EAAE,MAAM,uBAAuB,KAAK,IAAI;;EA+ErE,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// src/stores/config-store.ts
|
|
19
|
+
import { create } from "zustand";
|
|
20
|
+
import { immer } from "zustand/middleware/immer";
|
|
21
|
+
import { persist, createJSONStorage } from "zustand/middleware";
|
|
22
|
+
import { homedir } from "os";
|
|
23
|
+
import { join } from "path";
|
|
24
|
+
var bunConfigStorage = {
|
|
25
|
+
getItem: async (_name) => {
|
|
26
|
+
try {
|
|
27
|
+
const file = Bun.file(join(homedir(), ".hoox", "config.json"));
|
|
28
|
+
const exists = await file.exists();
|
|
29
|
+
if (!exists)
|
|
30
|
+
return null;
|
|
31
|
+
return await file.text();
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
setItem: async (_name, value) => {
|
|
37
|
+
const dir = join(homedir(), ".hoox");
|
|
38
|
+
try {
|
|
39
|
+
await Bun.file(dir).exists();
|
|
40
|
+
} catch {}
|
|
41
|
+
const filePath = join(dir, "config.json");
|
|
42
|
+
await Bun.write(filePath, value);
|
|
43
|
+
},
|
|
44
|
+
removeItem: async (_name) => {}
|
|
45
|
+
};
|
|
46
|
+
var DEFAULT_SHORTCUTS = {
|
|
47
|
+
"command-palette": "Ctrl+P",
|
|
48
|
+
"toggle-sidebar": "Ctrl+B",
|
|
49
|
+
"force-refresh": "Ctrl+R",
|
|
50
|
+
quit: "Ctrl+Q",
|
|
51
|
+
"view-1": "Ctrl+1",
|
|
52
|
+
"view-2": "Ctrl+2",
|
|
53
|
+
"view-3": "Ctrl+3",
|
|
54
|
+
"view-4": "Ctrl+4",
|
|
55
|
+
"view-5": "Ctrl+5",
|
|
56
|
+
"view-6": "Ctrl+6",
|
|
57
|
+
"view-7": "Ctrl+7",
|
|
58
|
+
"view-8": "Ctrl+8",
|
|
59
|
+
"view-9": "Ctrl+9",
|
|
60
|
+
"view-10": "Ctrl+0"
|
|
61
|
+
};
|
|
62
|
+
var DEFAULT_LOG_FILTER = {
|
|
63
|
+
levels: ["info", "warn", "error"],
|
|
64
|
+
workers: [],
|
|
65
|
+
searchText: ""
|
|
66
|
+
};
|
|
67
|
+
var DEFAULT_NOTIFICATIONS = {
|
|
68
|
+
alerts: true,
|
|
69
|
+
trades: false,
|
|
70
|
+
debug: false,
|
|
71
|
+
system: true
|
|
72
|
+
};
|
|
73
|
+
var defaults = {
|
|
74
|
+
theme: "dark",
|
|
75
|
+
refreshIntervalMs: 500,
|
|
76
|
+
defaultView: "dashboard",
|
|
77
|
+
activeExchanges: [],
|
|
78
|
+
keyboardShortcuts: { ...DEFAULT_SHORTCUTS },
|
|
79
|
+
logFilters: { ...DEFAULT_LOG_FILTER },
|
|
80
|
+
notifications: { ...DEFAULT_NOTIFICATIONS },
|
|
81
|
+
soundEnabled: false
|
|
82
|
+
};
|
|
83
|
+
var useConfigStore = create()(persist(immer((set) => ({
|
|
84
|
+
...defaults,
|
|
85
|
+
updateConfig: (partial) => set((state) => {
|
|
86
|
+
if (partial.theme !== undefined)
|
|
87
|
+
state.theme = partial.theme;
|
|
88
|
+
if (partial.refreshIntervalMs !== undefined)
|
|
89
|
+
state.refreshIntervalMs = partial.refreshIntervalMs;
|
|
90
|
+
if (partial.defaultView !== undefined)
|
|
91
|
+
state.defaultView = partial.defaultView;
|
|
92
|
+
if (partial.activeExchanges !== undefined)
|
|
93
|
+
state.activeExchanges = partial.activeExchanges;
|
|
94
|
+
if (partial.keyboardShortcuts !== undefined)
|
|
95
|
+
state.keyboardShortcuts = partial.keyboardShortcuts;
|
|
96
|
+
if (partial.logFilters !== undefined)
|
|
97
|
+
state.logFilters = partial.logFilters;
|
|
98
|
+
if (partial.notifications !== undefined)
|
|
99
|
+
state.notifications = partial.notifications;
|
|
100
|
+
if (partial.soundEnabled !== undefined)
|
|
101
|
+
state.soundEnabled = partial.soundEnabled;
|
|
102
|
+
}),
|
|
103
|
+
resetDefaults: () => set((state) => {
|
|
104
|
+
Object.assign(state, defaults);
|
|
105
|
+
}),
|
|
106
|
+
setShortcut: (action, key) => set((state) => {
|
|
107
|
+
state.keyboardShortcuts[action] = key;
|
|
108
|
+
}),
|
|
109
|
+
toggleNotification: (channel) => set((state) => {
|
|
110
|
+
state.notifications[channel] = !state.notifications[channel];
|
|
111
|
+
})
|
|
112
|
+
})), {
|
|
113
|
+
name: "hoox-config",
|
|
114
|
+
storage: createJSONStorage(() => bunConfigStorage),
|
|
115
|
+
partialize: (state) => ({
|
|
116
|
+
theme: state.theme,
|
|
117
|
+
refreshIntervalMs: state.refreshIntervalMs,
|
|
118
|
+
defaultView: state.defaultView,
|
|
119
|
+
activeExchanges: state.activeExchanges,
|
|
120
|
+
keyboardShortcuts: state.keyboardShortcuts,
|
|
121
|
+
logFilters: state.logFilters,
|
|
122
|
+
notifications: state.notifications,
|
|
123
|
+
soundEnabled: state.soundEnabled
|
|
124
|
+
}),
|
|
125
|
+
merge: (persisted, current) => ({
|
|
126
|
+
...current,
|
|
127
|
+
...persisted
|
|
128
|
+
})
|
|
129
|
+
}));
|
|
130
|
+
export {
|
|
131
|
+
useConfigStore
|
|
132
|
+
};
|