@firtoz/worker-helper 1.3.4 → 1.5.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/CHANGELOG.md +12 -0
- package/README.md +14 -0
- package/package.json +3 -3
- package/src/cf-typegen-discovery.ts +110 -0
- package/src/cf-typegen.ts +28 -66
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @firtoz/worker-helper
|
|
2
2
|
|
|
3
|
+
## 1.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`894ee47`](https://github.com/firtoz/fullstack-toolkit/commit/894ee4775393f4c536397e7db869ccfb31d3f045) Thanks [@firtoz](https://github.com/firtoz)! - Allow passing extra arguments to `wrangler types` via cf-typegen. Any arguments after the directory are forwarded to the wrangler command (e.g. `--env-interface WebAppEnv`, `-c wrangler.jsonc`).
|
|
8
|
+
|
|
9
|
+
## 1.4.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`6e96ebb`](https://github.com/firtoz/fullstack-toolkit/commit/6e96ebb05a992cc33ea339e5466b608774be62c3) Thanks [@firtoz](https://github.com/firtoz)! - cf-typegen now discovers wrangler configs from the npm/bun workspace definition (root package.json `workspaces` field) instead of using `git ls-files`. Untracked workspace packages (e.g. new durable objects like fal-user-do) are included in type generation without needing to be committed first.
|
|
14
|
+
|
|
3
15
|
## 1.3.4
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -54,6 +54,20 @@ cd your-worker-package
|
|
|
54
54
|
bun run cf-typegen
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
Any arguments after the directory are passed through to `wrangler types`. For example, to use a custom env interface or config:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bun run cf-typegen -- --env-interface WebAppEnv
|
|
61
|
+
# or with a custom config path
|
|
62
|
+
bun run cf-typegen -- -c wrangler.jsonc --env-interface WebAppEnv
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
When invoking the script directly, pass the directory first, then extra args:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bun --cwd ../../packages/worker-helper cf-typegen $(pwd) --env-interface WebAppEnv
|
|
69
|
+
```
|
|
70
|
+
|
|
57
71
|
**Output:**
|
|
58
72
|
```
|
|
59
73
|
Running CF typegen for: /path/to/your-worker
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firtoz/worker-helper",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Type-safe Web Worker helper with Zod validation and Cloudflare Workers utilities (cf-typegen)",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"module": "./src/index.ts",
|
|
@@ -67,8 +67,8 @@
|
|
|
67
67
|
"zod": "^4.3.6"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
|
-
"@types/node": "^25.2.
|
|
70
|
+
"@types/node": "^25.2.3",
|
|
71
71
|
"@firtoz/maybe-error": "^1.5.2",
|
|
72
|
-
"bun-types": "^1.3.
|
|
72
|
+
"bun-types": "^1.3.9"
|
|
73
73
|
}
|
|
74
74
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Find workspace root by walking up from startPath until we find a package.json
|
|
6
|
+
* with a "workspaces" field (npm/bun convention).
|
|
7
|
+
*/
|
|
8
|
+
export function findWorkspaceRoot(startPath: string): string | null {
|
|
9
|
+
let dir = path.resolve(startPath);
|
|
10
|
+
for (;;) {
|
|
11
|
+
const pkgPath = path.join(dir, "package.json");
|
|
12
|
+
if (fs.existsSync(pkgPath)) {
|
|
13
|
+
try {
|
|
14
|
+
const content = fs.readFileSync(pkgPath, "utf8");
|
|
15
|
+
const pkg = JSON.parse(content) as { workspaces?: unknown };
|
|
16
|
+
if (
|
|
17
|
+
pkg.workspaces != null &&
|
|
18
|
+
(Array.isArray(pkg.workspaces) ||
|
|
19
|
+
(typeof pkg.workspaces === "object" &&
|
|
20
|
+
"packages" in pkg.workspaces))
|
|
21
|
+
) {
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// ignore parse errors
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const parent = path.dirname(dir);
|
|
29
|
+
if (parent === dir) break;
|
|
30
|
+
dir = parent;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Expand workspace glob patterns (e.g. "packages/*", "tests/*") to absolute paths.
|
|
37
|
+
* Only supports single-level globs; each pattern must be exactly "dirname/*".
|
|
38
|
+
*/
|
|
39
|
+
export function expandWorkspacePatterns(
|
|
40
|
+
root: string,
|
|
41
|
+
patterns: string[],
|
|
42
|
+
): string[] {
|
|
43
|
+
const dirs: string[] = [];
|
|
44
|
+
for (const pattern of patterns) {
|
|
45
|
+
if (!pattern.endsWith("/*")) continue;
|
|
46
|
+
const base = pattern.slice(0, -2);
|
|
47
|
+
const basePath = path.join(root, base);
|
|
48
|
+
try {
|
|
49
|
+
const entries = fs.readdirSync(basePath, { withFileTypes: true });
|
|
50
|
+
for (const e of entries) {
|
|
51
|
+
if (e.isDirectory()) {
|
|
52
|
+
dirs.push(path.join(basePath, e.name));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// base path may not exist
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return dirs;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get workspace package paths from root package.json (workspaces field).
|
|
64
|
+
* Returns absolute paths to each workspace member directory.
|
|
65
|
+
*/
|
|
66
|
+
export function getWorkspacePaths(root: string): string[] {
|
|
67
|
+
const pkgPath = path.join(root, "package.json");
|
|
68
|
+
try {
|
|
69
|
+
const content = fs.readFileSync(pkgPath, "utf8");
|
|
70
|
+
const pkg = JSON.parse(content) as {
|
|
71
|
+
workspaces?: string[] | { packages?: string[] };
|
|
72
|
+
};
|
|
73
|
+
const raw = Array.isArray(pkg.workspaces)
|
|
74
|
+
? pkg.workspaces
|
|
75
|
+
: pkg.workspaces?.packages;
|
|
76
|
+
if (!Array.isArray(raw)) return [];
|
|
77
|
+
return expandWorkspacePatterns(root, raw);
|
|
78
|
+
} catch {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Find all wrangler config files under the given workspace member paths.
|
|
85
|
+
* Does not rely on git; includes untracked configs (e.g. new durable objects).
|
|
86
|
+
*/
|
|
87
|
+
export function findWranglerConfigsInPaths(workspacePaths: string[]): string[] {
|
|
88
|
+
const results: string[] = [];
|
|
89
|
+
for (const dir of workspacePaths) {
|
|
90
|
+
for (const name of ["wrangler.json", "wrangler.jsonc"]) {
|
|
91
|
+
const fullPath = path.join(dir, name);
|
|
92
|
+
if (fs.existsSync(fullPath)) {
|
|
93
|
+
results.push(fullPath);
|
|
94
|
+
break; // at most one per dir
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return results.sort((a, b) => (a < b ? -1 : 1));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Discover all wrangler config paths for the workspace containing cwd.
|
|
103
|
+
* Returns empty array if not in a workspace or if root has no workspaces field.
|
|
104
|
+
*/
|
|
105
|
+
export function discoverWranglerConfigs(cwd: string): string[] {
|
|
106
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
107
|
+
if (!workspaceRoot) return [];
|
|
108
|
+
const workspacePaths = getWorkspacePaths(workspaceRoot);
|
|
109
|
+
return findWranglerConfigsInPaths(workspacePaths);
|
|
110
|
+
}
|
package/src/cf-typegen.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import {
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import process from "node:process";
|
|
6
|
+
import {
|
|
7
|
+
discoverWranglerConfigs,
|
|
8
|
+
findWorkspaceRoot,
|
|
9
|
+
} from "./cf-typegen-discovery";
|
|
6
10
|
import { prepareEnvFiles } from "./utils/prepare-env";
|
|
7
11
|
|
|
8
|
-
//
|
|
12
|
+
// First arg: directory (cwd). Remaining args: passed through to `wrangler types`.
|
|
9
13
|
const cwd = process.argv[2];
|
|
14
|
+
const extraWranglerArgs = process.argv.slice(3);
|
|
15
|
+
|
|
10
16
|
if (!cwd || !fs.existsSync(cwd)) {
|
|
11
17
|
console.error(
|
|
12
18
|
"Please specify a directory as the first parameter. Usually $(pwd).",
|
|
@@ -16,72 +22,28 @@ if (!cwd || !fs.existsSync(cwd)) {
|
|
|
16
22
|
|
|
17
23
|
console.log(`Running CF typegen for: ${cwd}`);
|
|
18
24
|
|
|
19
|
-
/**
|
|
20
|
-
* Find the git root directory using git command
|
|
21
|
-
*/
|
|
22
|
-
function findGitRoot(startPath: string): string | null {
|
|
23
|
-
try {
|
|
24
|
-
const output = execSync("git rev-parse --show-toplevel", {
|
|
25
|
-
cwd: startPath,
|
|
26
|
-
encoding: "utf8",
|
|
27
|
-
});
|
|
28
|
-
return output.trim();
|
|
29
|
-
} catch {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Find all wrangler config files using git ls-files
|
|
36
|
-
* This is more efficient and respects .gitignore
|
|
37
|
-
*/
|
|
38
|
-
function findAllWranglerConfigs(gitRoot: string): string[] {
|
|
39
|
-
try {
|
|
40
|
-
// Use git ls-files to find all tracked wrangler configs
|
|
41
|
-
const output = execSync(
|
|
42
|
-
'git ls-files "**/wrangler.json" "**/wrangler.jsonc"',
|
|
43
|
-
{
|
|
44
|
-
cwd: gitRoot,
|
|
45
|
-
encoding: "utf8",
|
|
46
|
-
},
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
return output
|
|
50
|
-
.trim()
|
|
51
|
-
.split("\n")
|
|
52
|
-
.filter((line) => line.length > 0)
|
|
53
|
-
.map((relativePath) => path.join(gitRoot, relativePath))
|
|
54
|
-
.sort((a, b) => (a < b ? -1 : 1));
|
|
55
|
-
} catch (err) {
|
|
56
|
-
console.warn("⚠ Failed to run git ls-files:", err);
|
|
57
|
-
return [];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
25
|
function runWranglerTypes() {
|
|
62
26
|
const envFiles = prepareEnvFiles(cwd);
|
|
63
27
|
|
|
64
28
|
console.log("Running wrangler types...");
|
|
65
29
|
|
|
66
|
-
//
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
console.
|
|
30
|
+
// Discover wrangler configs from npm/bun workspace definition (includes untracked packages)
|
|
31
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
32
|
+
let allConfigs: string[];
|
|
33
|
+
if (workspaceRoot) {
|
|
34
|
+
allConfigs = discoverWranglerConfigs(cwd);
|
|
35
|
+
} else {
|
|
36
|
+
console.warn("⚠ No workspace root found, using current directory only");
|
|
37
|
+
allConfigs = [];
|
|
73
38
|
}
|
|
74
39
|
|
|
75
|
-
// Find all wrangler configs in the workspace
|
|
76
|
-
const allConfigs = gitRoot ? findAllWranglerConfigs(gitRoot) : [];
|
|
77
|
-
|
|
78
40
|
if (allConfigs.length > 0) {
|
|
79
41
|
console.log(` Found ${allConfigs.length} wrangler config(s) in workspace`);
|
|
80
42
|
}
|
|
81
43
|
|
|
82
|
-
// Build
|
|
44
|
+
// Build args for wrangler types: multiple -c flags, --env-file, then any extra args
|
|
83
45
|
// The first config should be the current directory's wrangler.jsonc
|
|
84
|
-
const
|
|
46
|
+
const args: string[] = ["types", "-c", "wrangler.jsonc"];
|
|
85
47
|
|
|
86
48
|
// Add other configs (relative to cwd for better readability)
|
|
87
49
|
const currentWranglerJsonc = path.join(cwd, "wrangler.jsonc");
|
|
@@ -98,27 +60,27 @@ function runWranglerTypes() {
|
|
|
98
60
|
}
|
|
99
61
|
// Make path relative to cwd
|
|
100
62
|
const relativePath = path.relative(cwd, configPath);
|
|
101
|
-
|
|
63
|
+
args.push("-c", relativePath);
|
|
102
64
|
}
|
|
103
65
|
|
|
104
66
|
for (const envFile of envFiles) {
|
|
105
|
-
|
|
67
|
+
args.push("--env-file", envFile);
|
|
106
68
|
}
|
|
107
69
|
|
|
108
|
-
|
|
70
|
+
args.push(...extraWranglerArgs);
|
|
109
71
|
|
|
72
|
+
const command = `wrangler ${args.join(" ")}`;
|
|
110
73
|
console.log(` Command: ${command}`);
|
|
111
74
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
console.log("✓ Wrangler types generated with all workspace bindings");
|
|
118
|
-
} catch {
|
|
75
|
+
const result = spawnSync("wrangler", args, {
|
|
76
|
+
cwd,
|
|
77
|
+
stdio: "inherit",
|
|
78
|
+
});
|
|
79
|
+
if (result.status !== 0) {
|
|
119
80
|
console.error("Failed to run wrangler types");
|
|
120
81
|
process.exit(1);
|
|
121
82
|
}
|
|
83
|
+
console.log("✓ Wrangler types generated with all workspace bindings");
|
|
122
84
|
}
|
|
123
85
|
|
|
124
86
|
// Run all steps
|