@jango-blockchained/hoox-shared 1.0.8 → 1.1.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/README.md +17 -0
- package/dist/analytics.d.ts +23 -2
- package/dist/analytics.d.ts.map +1 -1
- package/dist/analytics.js +14 -3
- package/dist/api-client.d.ts +12 -2
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +74 -7
- package/dist/colors.d.ts +63 -25
- package/dist/colors.d.ts.map +1 -1
- package/dist/colors.js +60 -20
- package/dist/config.d.ts +34 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +61 -6
- package/dist/cron-handler.d.ts +34 -0
- package/dist/cron-handler.d.ts.map +1 -0
- package/dist/cron-handler.js +48 -0
- package/dist/d1/index.js +50 -6
- package/dist/d1/repository.d.ts +5 -0
- package/dist/d1/repository.d.ts.map +1 -1
- package/dist/d1/repository.js +297 -0
- package/dist/d1/schemas.js +81 -0
- package/dist/errors.d.ts +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +48 -5
- package/dist/exchanges/base-exchange-client.d.ts +68 -0
- package/dist/exchanges/base-exchange-client.d.ts.map +1 -0
- package/dist/exchanges/base-exchange-client.js +120 -0
- package/dist/exchanges/index.d.ts +3 -0
- package/dist/exchanges/index.d.ts.map +1 -0
- package/dist/exchanges/index.js +120 -0
- package/dist/exchanges/types.d.ts +19 -0
- package/dist/exchanges/types.d.ts.map +1 -0
- package/dist/exchanges/types.js +1 -0
- package/dist/health.js +48 -5
- package/dist/index.d.ts +30 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1058 -73
- package/dist/kvUtils.js +48 -5
- package/dist/middleware/auth.d.ts +46 -7
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +151 -0
- package/dist/middleware/cors.d.ts +28 -2
- package/dist/middleware/cors.d.ts.map +1 -1
- package/dist/middleware/cors.js +77 -0
- package/dist/middleware/index.d.ts +2 -2
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +361 -101
- package/dist/middleware/logger.d.ts +2 -1
- package/dist/middleware/logger.d.ts.map +1 -1
- package/dist/middleware/logger.js +202 -0
- package/dist/middleware/rate-limit.d.ts +3 -1
- package/dist/middleware/rate-limit.d.ts.map +1 -1
- package/dist/middleware/rate-limit.js +181 -0
- package/dist/middleware/security-headers.js +58 -0
- package/dist/middleware/validate.js +56 -0
- package/dist/operator-transport.d.ts +61 -0
- package/dist/operator-transport.d.ts.map +1 -0
- package/dist/operator-transport.js +87 -0
- package/dist/path-utils.d.ts +205 -0
- package/dist/path-utils.d.ts.map +1 -0
- package/dist/path-utils.js +188 -0
- package/dist/queue-handler.d.ts +43 -0
- package/dist/queue-handler.d.ts.map +1 -0
- package/dist/queue-handler.js +46 -0
- package/dist/router.d.ts +2 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +15 -8
- package/dist/schemas/index.js +7 -4
- package/dist/schemas/registry.js +387 -0
- package/dist/schemas/types.js +1 -0
- package/dist/schemas/validators.d.ts.map +1 -1
- package/dist/schemas/validators.js +290 -0
- package/dist/service-bindings.d.ts +68 -26
- package/dist/service-bindings.d.ts.map +1 -1
- package/dist/service-bindings.js +219 -2
- package/dist/session.d.ts +9 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +179 -4
- package/dist/sse.d.ts +12 -3
- package/dist/sse.d.ts.map +1 -1
- package/dist/sse.js +70 -7
- package/dist/stores/config-store.d.ts.map +1 -1
- package/dist/stores/config-store.js +129 -6
- package/dist/stores/service-store.d.ts +50 -1
- package/dist/stores/service-store.d.ts.map +1 -1
- package/dist/stores/service-store.js +162 -27
- package/dist/test-utils.d.ts +132 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +128 -0
- package/dist/types/router.d.ts +18 -8
- package/dist/types/router.d.ts.map +1 -1
- package/dist/types.d.ts +124 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +11 -0
- package/dist/wizard/engine.d.ts.map +1 -1
- package/dist/wizard/engine.js +501 -0
- package/dist/wizard/index.js +2 -1
- package/dist/wizard/persistence.js +31 -0
- package/dist/wizard/presets.js +199 -0
- package/dist/wizard/provisioner.js +1 -0
- package/dist/wizard/types.js +1 -0
- package/package.json +46 -12
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Resolution Service for Hoox
|
|
3
|
+
*
|
|
4
|
+
* Provides cross-OS utilities for resolving the $HOME/.hoox directory location
|
|
5
|
+
* and constructing type-safe paths within it.
|
|
6
|
+
*
|
|
7
|
+
* Supports macOS, Linux, and Windows with proper fallback handling.
|
|
8
|
+
*
|
|
9
|
+
* Runtime layout:
|
|
10
|
+
* $HOME/.hoox/ — getHooxHome() (override with HOOX_HOME)
|
|
11
|
+
* $HOME/.hoox/repo/ — managed clone of hoox-setup (getHooxRepoPath)
|
|
12
|
+
* $HOME/.hoox/config/ — user config
|
|
13
|
+
* $HOME/.hoox/data/ — persistent state
|
|
14
|
+
*
|
|
15
|
+
* Tool/runtime resolution (resolveHooxRuntimeRoot):
|
|
16
|
+
* 1. HOOX_REPO env (explicit monorepo path)
|
|
17
|
+
* 2. Walk up from cwd for a local hoox-setup checkout
|
|
18
|
+
* 3. $HOME/.hoox/repo (global managed clone)
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Branded type for Hoox paths to prevent accidental string usage.
|
|
22
|
+
* This ensures type safety when working with paths.
|
|
23
|
+
*/
|
|
24
|
+
export type HooxPath = string & {
|
|
25
|
+
readonly __brand: "HooxPath";
|
|
26
|
+
};
|
|
27
|
+
/** Where resolveHooxRuntimeRoot found (or failed to find) a setup monorepo. */
|
|
28
|
+
export type RuntimeRootSource = "env" | "cwd" | "global" | "none";
|
|
29
|
+
/** Result of resolveHooxRuntimeRoot(). */
|
|
30
|
+
export interface RuntimeRootResult {
|
|
31
|
+
/** Absolute monorepo root, or null if none found. */
|
|
32
|
+
root: string | null;
|
|
33
|
+
/** Which resolution step produced the result. */
|
|
34
|
+
source: RuntimeRootSource;
|
|
35
|
+
/** Paths inspected (for doctor / error messages). */
|
|
36
|
+
checked: {
|
|
37
|
+
env?: string;
|
|
38
|
+
cwd: string | null;
|
|
39
|
+
global: string;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Gets the Hoox home directory location: $HOME/.hoox
|
|
44
|
+
*
|
|
45
|
+
* Behavior:
|
|
46
|
+
* - HOOX_HOME env wins when set (absolute or relative, then resolved)
|
|
47
|
+
* - Else $HOME/.hoox on macOS, Linux, Windows
|
|
48
|
+
* - Falls back to current working directory if HOME is not available
|
|
49
|
+
* - Resolves to absolute path
|
|
50
|
+
*
|
|
51
|
+
* @returns Absolute path to $HOME/.hoox as a branded HooxPath
|
|
52
|
+
* @throws Never — always returns a valid path
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const hooxHome = getHooxHome();
|
|
57
|
+
* // Returns: "/Users/alice/.hoox" (macOS)
|
|
58
|
+
* // Returns: "/home/alice/.hoox" (Linux)
|
|
59
|
+
* // Returns: "C:\\Users\\alice\\.hoox" (Windows)
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function getHooxHome(): HooxPath;
|
|
63
|
+
/**
|
|
64
|
+
* True when `dir` looks like a hoox-setup monorepo root.
|
|
65
|
+
*
|
|
66
|
+
* Markers match CLI verifyRepoRoot: root wrangler.jsonc + packages/cli package.
|
|
67
|
+
*/
|
|
68
|
+
export declare function isHooxSetupRoot(dir: string): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Walk up from `startDir` looking for a hoox-setup monorepo root.
|
|
71
|
+
*
|
|
72
|
+
* @returns Absolute root path, or null if not found
|
|
73
|
+
*/
|
|
74
|
+
export declare function findHooxSetupRoot(startDir?: string): string | null;
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the Hoox tool/runtime monorepo root (local checkout or global clone).
|
|
77
|
+
*
|
|
78
|
+
* Order:
|
|
79
|
+
* 1. HOOX_REPO — must pass isHooxSetupRoot or result is source "env" with root null
|
|
80
|
+
* 2. Walk up from cwd
|
|
81
|
+
* 3. getHooxRepoPath() ($HOME/.hoox/repo or $HOOX_HOME/repo)
|
|
82
|
+
*
|
|
83
|
+
* Project cwd and tool root are intentionally separate: a random project
|
|
84
|
+
* directory can still use the global runtime for TUI / templates.
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveHooxRuntimeRoot(options?: {
|
|
87
|
+
cwd?: string;
|
|
88
|
+
env?: NodeJS.ProcessEnv;
|
|
89
|
+
}): RuntimeRootResult;
|
|
90
|
+
/**
|
|
91
|
+
* Candidate TUI entry files under a monorepo root (source first, then dist).
|
|
92
|
+
*/
|
|
93
|
+
export declare function getTuiEntryCandidates(runtimeRoot: string): string[];
|
|
94
|
+
/**
|
|
95
|
+
* Resolves a relative path within the Hoox home directory.
|
|
96
|
+
*
|
|
97
|
+
* Behavior:
|
|
98
|
+
* - Joins the relative path with $HOME/.hoox
|
|
99
|
+
* - Resolves to absolute path
|
|
100
|
+
* - Prevents path traversal attacks (../ sequences)
|
|
101
|
+
*
|
|
102
|
+
* @param relativePath - Path relative to $HOME/.hoox (e.g., "repo", "config/wrangler.jsonc")
|
|
103
|
+
* @returns Absolute path as a branded HooxPath
|
|
104
|
+
* @throws Error if path contains suspicious traversal patterns
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const repoPath = resolveHooxPath("repo");
|
|
109
|
+
* // Returns: "/Users/alice/.hoox/repo"
|
|
110
|
+
*
|
|
111
|
+
* const configPath = resolveHooxPath("config/wrangler.jsonc");
|
|
112
|
+
* // Returns: "/Users/alice/.hoox/config/wrangler.jsonc"
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export declare function resolveHooxPath(relativePath: string): HooxPath;
|
|
116
|
+
/**
|
|
117
|
+
* Checks if a given path is within the Hoox home directory.
|
|
118
|
+
*
|
|
119
|
+
* @param path - Path to check
|
|
120
|
+
* @returns true if path is within $HOME/.hoox, false otherwise
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* isWithinHooxHome("/Users/alice/.hoox/repo"); // true
|
|
125
|
+
* isWithinHooxHome("/Users/alice/other"); // false
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function isWithinHooxHome(path: string): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Gets the relative path from Hoox home directory.
|
|
131
|
+
*
|
|
132
|
+
* @param absolutePath - Absolute path to resolve
|
|
133
|
+
* @returns Relative path from $HOME/.hoox, or null if path is outside Hoox home
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* getRelativeHooxPath("/Users/alice/.hoox/repo/src");
|
|
138
|
+
* // Returns: "repo/src"
|
|
139
|
+
*
|
|
140
|
+
* getRelativeHooxPath("/Users/alice/other");
|
|
141
|
+
* // Returns: null
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export declare function getRelativeHooxPath(absolutePath: string): string | null;
|
|
145
|
+
/**
|
|
146
|
+
* Constructs a path to the Hoox repository location.
|
|
147
|
+
*
|
|
148
|
+
* @returns Path to $HOME/.hoox/repo as a branded HooxPath
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* const repoPath = getHooxRepoPath();
|
|
153
|
+
* // Returns: "/Users/alice/.hoox/repo"
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare function getHooxRepoPath(): HooxPath;
|
|
157
|
+
/**
|
|
158
|
+
* Constructs a path to the Hoox configuration directory.
|
|
159
|
+
*
|
|
160
|
+
* @returns Path to $HOME/.hoox/config as a branded HooxPath
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* const configDir = getHooxConfigDir();
|
|
165
|
+
* // Returns: "/Users/alice/.hoox/config"
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
export declare function getHooxConfigDir(): HooxPath;
|
|
169
|
+
/**
|
|
170
|
+
* Constructs a path to the Hoox data directory.
|
|
171
|
+
*
|
|
172
|
+
* @returns Path to $HOME/.hoox/data as a branded HooxPath
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const dataDir = getHooxDataDir();
|
|
177
|
+
* // Returns: "/Users/alice/.hoox/data"
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
export declare function getHooxDataDir(): HooxPath;
|
|
181
|
+
/**
|
|
182
|
+
* Constructs a path to the Hoox wrangler configuration file.
|
|
183
|
+
*
|
|
184
|
+
* @returns Path to $HOME/.hoox/config/wrangler.jsonc as a branded HooxPath
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* const wranglerPath = getHooxWranglerPath();
|
|
189
|
+
* // Returns: "/Users/alice/.hoox/config/wrangler.jsonc"
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
export declare function getHooxWranglerPath(): HooxPath;
|
|
193
|
+
/**
|
|
194
|
+
* Constructs a path to the Hoox state file.
|
|
195
|
+
*
|
|
196
|
+
* @returns Path to $HOME/.hoox/data/state.json as a branded HooxPath
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* const statePath = getHooxStatePath();
|
|
201
|
+
* // Returns: "/Users/alice/.hoox/data/state.json"
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export declare function getHooxStatePath(): HooxPath;
|
|
205
|
+
//# sourceMappingURL=path-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAA;CAAE,CAAC;AAEjE,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAElE,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,iDAAiD;IACjD,MAAM,EAAE,iBAAiB,CAAC;IAC1B,qDAAqD;IACrD,OAAO,EAAE;QACP,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAUD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAgBtC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWpD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,GAAE,MAAsB,GAC/B,MAAM,GAAG,IAAI,CAQf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG,iBAAiB,CAoDpB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAOnE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAiC9D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAStD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkBvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,QAAQ,CAE1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAEzC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,IAAI,QAAQ,CAE9C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,IAAI,QAAQ,CAE3C"}
|
|
@@ -0,0 +1,188 @@
|
|
|
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/path-utils.ts
|
|
19
|
+
import { existsSync } from "fs";
|
|
20
|
+
import { homedir } from "os";
|
|
21
|
+
import { dirname, join, resolve } from "path";
|
|
22
|
+
function createHooxPath(path) {
|
|
23
|
+
return path;
|
|
24
|
+
}
|
|
25
|
+
function getHooxHome() {
|
|
26
|
+
try {
|
|
27
|
+
const override = process.env.HOOX_HOME?.trim();
|
|
28
|
+
if (override) {
|
|
29
|
+
return createHooxPath(resolve(override));
|
|
30
|
+
}
|
|
31
|
+
const home = homedir();
|
|
32
|
+
if (!home || home.length === 0) {
|
|
33
|
+
return createHooxPath(resolve(process.cwd(), ".hoox"));
|
|
34
|
+
}
|
|
35
|
+
return createHooxPath(join(home, ".hoox"));
|
|
36
|
+
} catch {
|
|
37
|
+
return createHooxPath(resolve(process.cwd(), ".hoox"));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function isHooxSetupRoot(dir) {
|
|
41
|
+
if (!dir)
|
|
42
|
+
return false;
|
|
43
|
+
try {
|
|
44
|
+
const root = resolve(dir);
|
|
45
|
+
return existsSync(join(root, "wrangler.jsonc")) && existsSync(join(root, "packages", "cli", "package.json"));
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
function findHooxSetupRoot(startDir = process.cwd()) {
|
|
51
|
+
let dir = resolve(startDir);
|
|
52
|
+
for (;; ) {
|
|
53
|
+
if (isHooxSetupRoot(dir))
|
|
54
|
+
return dir;
|
|
55
|
+
const parent = dirname(dir);
|
|
56
|
+
if (parent === dir)
|
|
57
|
+
return null;
|
|
58
|
+
dir = parent;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function resolveHooxRuntimeRoot(options) {
|
|
62
|
+
const env = options?.env ?? process.env;
|
|
63
|
+
const cwd = resolve(options?.cwd ?? process.cwd());
|
|
64
|
+
const globalRepo = getHooxRepoPath();
|
|
65
|
+
const envRepo = env.HOOX_REPO?.trim();
|
|
66
|
+
if (envRepo) {
|
|
67
|
+
const resolved = resolve(envRepo);
|
|
68
|
+
if (isHooxSetupRoot(resolved)) {
|
|
69
|
+
return {
|
|
70
|
+
root: resolved,
|
|
71
|
+
source: "env",
|
|
72
|
+
checked: {
|
|
73
|
+
env: resolved,
|
|
74
|
+
cwd: findHooxSetupRoot(cwd),
|
|
75
|
+
global: globalRepo
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
root: null,
|
|
81
|
+
source: "env",
|
|
82
|
+
checked: {
|
|
83
|
+
env: resolved,
|
|
84
|
+
cwd: findHooxSetupRoot(cwd),
|
|
85
|
+
global: globalRepo
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const local = findHooxSetupRoot(cwd);
|
|
90
|
+
if (local) {
|
|
91
|
+
return {
|
|
92
|
+
root: local,
|
|
93
|
+
source: "cwd",
|
|
94
|
+
checked: { cwd: local, global: globalRepo }
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
if (isHooxSetupRoot(globalRepo)) {
|
|
98
|
+
return {
|
|
99
|
+
root: globalRepo,
|
|
100
|
+
source: "global",
|
|
101
|
+
checked: { cwd: null, global: globalRepo }
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
root: null,
|
|
106
|
+
source: "none",
|
|
107
|
+
checked: { cwd: null, global: globalRepo }
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function getTuiEntryCandidates(runtimeRoot) {
|
|
111
|
+
const root = resolve(runtimeRoot);
|
|
112
|
+
return [
|
|
113
|
+
join(root, "packages", "tui", "src", "main.tsx"),
|
|
114
|
+
join(root, "packages", "tui", "dist", "main.js"),
|
|
115
|
+
join(root, "packages", "tui", "src", "main.ts")
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
function resolveHooxPath(relativePath) {
|
|
119
|
+
if (!relativePath || typeof relativePath !== "string") {
|
|
120
|
+
throw new Error("relativePath must be a non-empty string");
|
|
121
|
+
}
|
|
122
|
+
if (relativePath.includes("..")) {
|
|
123
|
+
throw new Error(`Path traversal detected in relativePath: "${relativePath}"`);
|
|
124
|
+
}
|
|
125
|
+
const normalizedPath = relativePath.replace(/\\/g, "/");
|
|
126
|
+
const hooxHome = getHooxHome();
|
|
127
|
+
const fullPath = join(hooxHome, normalizedPath);
|
|
128
|
+
const resolvedPath = resolve(fullPath);
|
|
129
|
+
const resolvedHome = resolve(hooxHome);
|
|
130
|
+
if (!resolvedPath.startsWith(resolvedHome)) {
|
|
131
|
+
throw new Error(`Resolved path "${resolvedPath}" is outside Hoox home directory`);
|
|
132
|
+
}
|
|
133
|
+
return createHooxPath(resolvedPath);
|
|
134
|
+
}
|
|
135
|
+
function isWithinHooxHome(path) {
|
|
136
|
+
try {
|
|
137
|
+
const hooxHome = getHooxHome();
|
|
138
|
+
const resolvedPath = resolve(path);
|
|
139
|
+
const resolvedHome = resolve(hooxHome);
|
|
140
|
+
return resolvedPath.startsWith(resolvedHome);
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function getRelativeHooxPath(absolutePath) {
|
|
146
|
+
try {
|
|
147
|
+
if (!isWithinHooxHome(absolutePath)) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const hooxHome = getHooxHome();
|
|
151
|
+
const resolvedPath = resolve(absolutePath);
|
|
152
|
+
const resolvedHome = resolve(hooxHome);
|
|
153
|
+
const relativePath = resolvedPath.slice(resolvedHome.length);
|
|
154
|
+
return relativePath.startsWith("/") || relativePath.startsWith("\\") ? relativePath.slice(1) : relativePath;
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function getHooxRepoPath() {
|
|
160
|
+
return resolveHooxPath("repo");
|
|
161
|
+
}
|
|
162
|
+
function getHooxConfigDir() {
|
|
163
|
+
return resolveHooxPath("config");
|
|
164
|
+
}
|
|
165
|
+
function getHooxDataDir() {
|
|
166
|
+
return resolveHooxPath("data");
|
|
167
|
+
}
|
|
168
|
+
function getHooxWranglerPath() {
|
|
169
|
+
return resolveHooxPath("config/wrangler.jsonc");
|
|
170
|
+
}
|
|
171
|
+
function getHooxStatePath() {
|
|
172
|
+
return resolveHooxPath("data/state.json");
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
resolveHooxRuntimeRoot,
|
|
176
|
+
resolveHooxPath,
|
|
177
|
+
isWithinHooxHome,
|
|
178
|
+
isHooxSetupRoot,
|
|
179
|
+
getTuiEntryCandidates,
|
|
180
|
+
getRelativeHooxPath,
|
|
181
|
+
getHooxWranglerPath,
|
|
182
|
+
getHooxStatePath,
|
|
183
|
+
getHooxRepoPath,
|
|
184
|
+
getHooxHome,
|
|
185
|
+
getHooxDataDir,
|
|
186
|
+
getHooxConfigDir,
|
|
187
|
+
findHooxSetupRoot
|
|
188
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { MessageBatch } from "@cloudflare/workers-types";
|
|
2
|
+
export interface QueueHandlerOptions<T> {
|
|
3
|
+
/** Maximum number of retry attempts */
|
|
4
|
+
maxRetries: number;
|
|
5
|
+
/** Array of delay times in seconds for exponential backoff */
|
|
6
|
+
backoffDelays: number[];
|
|
7
|
+
/** Handler function called for each message */
|
|
8
|
+
onMessage: (message: T, attemptNumber: number) => Promise<unknown> | unknown;
|
|
9
|
+
/** Called when a message fails and will be retried */
|
|
10
|
+
onRetry?: (message: T, attemptNumber: number, error: string, delaySeconds: number) => void | Promise<void>;
|
|
11
|
+
/** Called when message is moved to DLQ (max retries exceeded) */
|
|
12
|
+
onDLQ?: (message: T, attemptNumber: number, error: string) => void | Promise<void>;
|
|
13
|
+
/** Logger function for debugging */
|
|
14
|
+
logger?: {
|
|
15
|
+
info(msg: string, data?: unknown): void;
|
|
16
|
+
error(msg: string, data?: unknown): void;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Creates a reusable queue handler with retry + exponential backoff logic.
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const handler = createQueueHandler({
|
|
25
|
+
* maxRetries: 5,
|
|
26
|
+
* backoffDelays: [0, 30, 60, 300, 900],
|
|
27
|
+
* onMessage: async (msg) => {
|
|
28
|
+
* await executeTask(msg);
|
|
29
|
+
* },
|
|
30
|
+
* onDLQ: async (msg, attempts, error) => {
|
|
31
|
+
* await logFailedTask(msg, error);
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* export default {
|
|
36
|
+
* async queue(batch, env, ctx) {
|
|
37
|
+
* return await handler(batch);
|
|
38
|
+
* }
|
|
39
|
+
* };
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function createQueueHandler<T>(options: QueueHandlerOptions<T>): (batch: MessageBatch<T>) => Promise<void>;
|
|
43
|
+
//# sourceMappingURL=queue-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-handler.d.ts","sourceRoot":"","sources":["../src/queue-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,+CAA+C;IAC/C,SAAS,EAAE,CAAC,OAAO,EAAE,CAAC,EAAE,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IAC7E,sDAAsD;IACtD,OAAO,CAAC,EAAE,CACR,OAAO,EAAE,CAAC,EACV,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,KACjB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,iEAAiE;IACjE,KAAK,CAAC,EAAE,CACN,OAAO,EAAE,CAAC,EACV,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,KACV,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,oCAAoC;IACpC,MAAM,CAAC,EAAE;QACP,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QACxC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;KAC1C,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAIrD,OAAO,YAAY,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC,CAmCrD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
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/queue-handler.ts
|
|
19
|
+
function createQueueHandler(options) {
|
|
20
|
+
const { maxRetries, backoffDelays, onMessage, onRetry, onDLQ, logger } = options;
|
|
21
|
+
return async (batch) => {
|
|
22
|
+
for (const msg of batch.messages) {
|
|
23
|
+
const attemptNumber = msg.attempts || 0;
|
|
24
|
+
const logId = `[${msg.id}]`;
|
|
25
|
+
try {
|
|
26
|
+
logger?.info(`${logId} Processing message (attempt ${attemptNumber + 1})`);
|
|
27
|
+
await onMessage(msg.body, attemptNumber);
|
|
28
|
+
logger?.info(`${logId} Message processed successfully`);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
31
|
+
if (attemptNumber < maxRetries) {
|
|
32
|
+
const delaySeconds = backoffDelays[attemptNumber] ?? backoffDelays[backoffDelays.length - 1];
|
|
33
|
+
logger?.info(`${logId} Retrying in ${delaySeconds}s (attempt ${attemptNumber + 2}/${maxRetries + 1})`);
|
|
34
|
+
await onRetry?.(msg.body, attemptNumber, errorMsg, delaySeconds);
|
|
35
|
+
msg.retry({ delaySeconds });
|
|
36
|
+
} else {
|
|
37
|
+
logger?.error(`${logId} Max retries exceeded (${maxRetries + 1} attempts), moving to DLQ`);
|
|
38
|
+
await onDLQ?.(msg.body, attemptNumber, errorMsg);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export {
|
|
45
|
+
createQueueHandler
|
|
46
|
+
};
|
package/dist/router.d.ts
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* Path parameters are extracted and passed as the 4th argument to handlers.
|
|
7
7
|
*/
|
|
8
8
|
import type { Router } from "./types/router";
|
|
9
|
-
export type { Handler, RouteParams } from "./types/router";
|
|
10
|
-
export declare function createRouter<TEnv =
|
|
9
|
+
export type { Handler, MiddlewareHandler, RouteParams } from "./types/router";
|
|
10
|
+
export declare function createRouter<TEnv = unknown>(): Router<TEnv>;
|
|
11
11
|
//# sourceMappingURL=router.d.ts.map
|
package/dist/router.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAKV,MAAM,EACP,MAAM,gBAAgB,CAAC;AAGxB,YAAY,EAAE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE9E,wBAAgB,YAAY,CAAC,IAAI,GAAG,OAAO,KAAK,MAAM,CAAC,IAAI,CAAC,CAsM3D"}
|
package/dist/router.js
CHANGED
|
@@ -52,7 +52,7 @@ function createRouter() {
|
|
|
52
52
|
routes.push(route);
|
|
53
53
|
}
|
|
54
54
|
function matchRoute(path, method) {
|
|
55
|
-
const exact = routes.
|
|
55
|
+
const exact = routes.reduceRight((found, r) => found ?? (r.path === path && r.method === method ? r : null), null);
|
|
56
56
|
if (exact)
|
|
57
57
|
return { route: exact };
|
|
58
58
|
for (let i = routes.length - 1;i >= 0; i--) {
|
|
@@ -85,14 +85,20 @@ function createRouter() {
|
|
|
85
85
|
const method = request.method;
|
|
86
86
|
const match = matchRoute(path, method);
|
|
87
87
|
if (!match) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
let pathExists = false;
|
|
89
|
+
for (let i = routes.length - 1;i >= 0; i--) {
|
|
90
|
+
const r = routes[i];
|
|
91
|
+
if (r.path === path) {
|
|
92
|
+
pathExists = true;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
91
95
|
if (r.params && r.params.length > 0 && r.regex) {
|
|
92
|
-
|
|
96
|
+
if (r.regex.test(path)) {
|
|
97
|
+
pathExists = true;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
93
100
|
}
|
|
94
|
-
|
|
95
|
-
});
|
|
101
|
+
}
|
|
96
102
|
if (pathExists) {
|
|
97
103
|
return new Response(JSON.stringify({
|
|
98
104
|
success: false,
|
|
@@ -114,7 +120,8 @@ function createRouter() {
|
|
|
114
120
|
return result;
|
|
115
121
|
}
|
|
116
122
|
}
|
|
117
|
-
|
|
123
|
+
const handlerResult = await match.route.handler(request, env, ctx, match.params);
|
|
124
|
+
return handlerResult ?? new Response(null, { status: 204 });
|
|
118
125
|
}
|
|
119
126
|
};
|
|
120
127
|
}
|
package/dist/schemas/index.js
CHANGED
|
@@ -36,7 +36,7 @@ function deriveCalledBy(manifests) {
|
|
|
36
36
|
var manifests = {
|
|
37
37
|
hoox: {
|
|
38
38
|
name: "hoox",
|
|
39
|
-
path: "workers/hoox",
|
|
39
|
+
path: "workers/hoox-worker",
|
|
40
40
|
vars: {
|
|
41
41
|
WEBHOOK_API_KEY_BINDING: {
|
|
42
42
|
type: "secret",
|
|
@@ -380,6 +380,7 @@ var manifests = {
|
|
|
380
380
|
var CALLED_BY = deriveCalledBy(manifests);
|
|
381
381
|
var WORKER_MANIFESTS = manifests;
|
|
382
382
|
var WORKER_NAMES = Object.keys(manifests).sort();
|
|
383
|
+
|
|
383
384
|
// src/schemas/validators.ts
|
|
384
385
|
import { parse } from "jsonc-parser";
|
|
385
386
|
function validateWranglerJsonc(workerName, manifest, jsoncContent) {
|
|
@@ -405,7 +406,8 @@ function validateWranglerJsonc(workerName, manifest, jsoncContent) {
|
|
|
405
406
|
});
|
|
406
407
|
return errors;
|
|
407
408
|
}
|
|
408
|
-
const
|
|
409
|
+
const wrangler = parsed;
|
|
410
|
+
const declaredVars = wrangler.vars ?? {};
|
|
409
411
|
for (const [name, def] of Object.entries(manifest.vars)) {
|
|
410
412
|
if (!(name in declaredVars)) {
|
|
411
413
|
errors.push({
|
|
@@ -426,7 +428,7 @@ function validateWranglerJsonc(workerName, manifest, jsoncContent) {
|
|
|
426
428
|
});
|
|
427
429
|
}
|
|
428
430
|
}
|
|
429
|
-
const declaredServices =
|
|
431
|
+
const declaredServices = wrangler.services ?? [];
|
|
430
432
|
for (const expected of manifest.services) {
|
|
431
433
|
const match = declaredServices.find((s) => s.binding === expected.binding && s.service === expected.service);
|
|
432
434
|
if (!match) {
|
|
@@ -482,7 +484,8 @@ function validateRootSecrets(workerName, manifest, rootJsoncContent) {
|
|
|
482
484
|
});
|
|
483
485
|
return errors;
|
|
484
486
|
}
|
|
485
|
-
const
|
|
487
|
+
const rootWrangler = parsed;
|
|
488
|
+
const rootSecrets = rootWrangler.workers?.[workerName]?.secrets ?? [];
|
|
486
489
|
const expectedSecrets = Object.entries(manifest.vars).filter(([_, def]) => def.type === "secret").map(([name]) => name);
|
|
487
490
|
for (const expected of expectedSecrets) {
|
|
488
491
|
if (!rootSecrets.includes(expected)) {
|