@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 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.4",
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.1",
70
+ "@types/node": "^25.2.3",
71
71
  "@firtoz/maybe-error": "^1.5.2",
72
- "bun-types": "^1.3.8"
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 { execSync } from "node:child_process";
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
- // Use the current working directory
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
- // Find git root to discover all wrangler configs
67
- const gitRoot = findGitRoot(cwd);
68
- if (!gitRoot) {
69
- console.warn(
70
- "⚠ Could not find git root, skipping workspace config discovery",
71
- );
72
- console.log(" Generating types for current directory only");
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 the command with multiple -c flags
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 configFlags = ["-c wrangler.jsonc"];
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
- configFlags.push(`-c ${relativePath}`);
63
+ args.push("-c", relativePath);
102
64
  }
103
65
 
104
66
  for (const envFile of envFiles) {
105
- configFlags.push(`--env-file ${envFile}`);
67
+ args.push("--env-file", envFile);
106
68
  }
107
69
 
108
- const command = `wrangler types ${configFlags.join(" ")}`;
70
+ args.push(...extraWranglerArgs);
109
71
 
72
+ const command = `wrangler ${args.join(" ")}`;
110
73
  console.log(` Command: ${command}`);
111
74
 
112
- try {
113
- execSync(command, {
114
- cwd,
115
- stdio: "inherit",
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