@firtoz/worker-helper 1.0.0 → 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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @firtoz/worker-helper
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`b0f7893`](https://github.com/firtoz/fullstack-toolkit/commit/b0f789314c4ee85d8c08466b968baad2977a2581) Thanks [@firtoz](https://github.com/firtoz)! - Added cf-typegen script utility for generating Cloudflare Workers TypeScript types
8
+
9
+ - Added cf-typegen script that runs wrangler types command for specified worker directory
10
+ - Utility used by test packages to generate worker-configuration.d.ts
11
+ - Simplified type generation workflow for Cloudflare Workers projects
12
+
3
13
  ## 1.0.0
4
14
 
5
15
  ### Major Changes
package/README.md CHANGED
@@ -6,19 +6,79 @@ Type-safe Web Worker helper with Zod validation for input and output messages. T
6
6
 
7
7
  ## Features
8
8
 
9
+ ### Web Workers
10
+
9
11
  - šŸ”’ **Type-safe**: Full TypeScript support with automatic type inference
10
12
  - āœ… **Zod Validation**: Automatic validation of both input and output messages
11
13
  - šŸŽÆ **Custom Error Handlers**: Mandatory error handlers give you complete control over error handling
12
14
  - šŸ”„ **Async Support**: Built-in support for async message handlers
13
15
  - 🧩 **Discriminated Unions**: Works great with Zod's discriminated unions for type-safe message routing
14
16
 
17
+ ### Cloudflare Workers
18
+
19
+ - šŸ”§ **cf-typegen**: Automatic `.env.local` creation from `wrangler.jsonc` vars
20
+ - šŸ“ **Type Generation**: Wrapper around `wrangler types` with env preparation
21
+
15
22
  ## Installation
16
23
 
17
24
  ```bash
18
25
  bun add @firtoz/worker-helper zod
19
26
  ```
20
27
 
21
- ## Usage
28
+ ## cf-typegen (Cloudflare Workers Utility)
29
+
30
+ Automatic TypeScript type generation and `.env.local` management for Cloudflare Workers projects.
31
+
32
+ ### Setup
33
+
34
+ Add the script to your Cloudflare Workers package:
35
+
36
+ ```json
37
+ {
38
+ "scripts": {
39
+ "cf-typegen": "bun --cwd ../../packages/worker-helper cf-typegen $(pwd)"
40
+ }
41
+ }
42
+ ```
43
+
44
+ ### What It Does
45
+
46
+ 1. **Reads `.env.local.example`** to find required env vars
47
+ 2. **Creates/updates `.env.local`** with any missing vars (as empty strings)
48
+ 3. **Runs `wrangler types`** to generate TypeScript definitions
49
+
50
+ ### Example
51
+
52
+ ```bash
53
+ cd your-worker-package
54
+ bun run cf-typegen
55
+ ```
56
+
57
+ **Output:**
58
+ ```
59
+ Running CF typegen for: /path/to/your-worker
60
+ āœ“ Added missing env vars: OPENROUTER_API_KEY, DATABASE_URL
61
+ Running wrangler types...
62
+ āœ“ Wrangler types generated
63
+ āœ“ CF typegen completed successfully
64
+ ```
65
+
66
+ **Generated `.env.local`:**
67
+ ```env
68
+ OPENROUTER_API_KEY=
69
+ DATABASE_URL=
70
+ ```
71
+
72
+ ### Why This Matters
73
+
74
+ - Ensures `wrangler types` always succeeds (needs `.env.local` or `.dev.vars`)
75
+ - Keeps `.env.local` in sync with `.env.local.example`
76
+ - Avoids accidentally binding empty vars at runtime via `wrangler.jsonc` `vars`
77
+ - Developers can fill in actual values without committing them to git
78
+ - CI/CD can generate types without needing actual secrets
79
+ - `.env.local.example` serves as documentation for required env vars
80
+
81
+ ## Web Worker Usage
22
82
 
23
83
  ### 1. Define Your Schemas
24
84
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firtoz/worker-helper",
3
- "version": "1.0.0",
4
- "description": "Type-safe Web Worker helper with Zod validation for input and output messages",
3
+ "version": "1.1.0",
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",
7
7
  "types": "./src/index.ts",
@@ -28,15 +28,20 @@
28
28
  "lint:ci": "biome ci src",
29
29
  "format": "biome format src --write",
30
30
  "test": "bun test",
31
- "test:watch": "bun test --watch"
31
+ "test:watch": "bun test --watch",
32
+ "cf-typegen": "bun ./src/cf-typegen.ts"
32
33
  },
33
34
  "keywords": [
34
35
  "typescript",
35
36
  "web-worker",
36
37
  "worker",
38
+ "cloudflare-workers",
39
+ "cloudflare",
37
40
  "zod",
38
41
  "validation",
39
- "type-safe"
42
+ "type-safe",
43
+ "wrangler",
44
+ "typegen"
40
45
  ],
41
46
  "author": "Firtina Ozbalikchi <firtoz@github.com>",
42
47
  "license": "MIT",
@@ -56,11 +61,11 @@
56
61
  "access": "public"
57
62
  },
58
63
  "dependencies": {
59
- "zod": "^4.1.12"
64
+ "zod": "^4.3.5"
60
65
  },
61
66
  "devDependencies": {
62
- "@types/node": "^24.10.1",
63
- "@firtoz/maybe-error": "^1.5.1",
64
- "bun-types": "^1.3.2"
67
+ "@types/node": "^25.0.9",
68
+ "@firtoz/maybe-error": "^1.5.2",
69
+ "bun-types": "^1.3.6"
65
70
  }
66
71
  }
@@ -0,0 +1,138 @@
1
+ import { execSync } from "node:child_process";
2
+ import * as fs from "node:fs";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+
6
+ // Use the current working directory
7
+ const cwd = process.argv[2];
8
+ if (!cwd || !fs.existsSync(cwd)) {
9
+ console.error(
10
+ "Please specify a directory as the first parameter. Usually $(pwd).",
11
+ );
12
+ process.exit(1);
13
+ }
14
+
15
+ console.log(`Running CF typegen for: ${cwd}`);
16
+
17
+ /**
18
+ * Extracts required env vars from .env.local.example
19
+ */
20
+ function getRequiredEnvVars(examplePath: string): string[] {
21
+ try {
22
+ if (!fs.existsSync(examplePath)) {
23
+ return [];
24
+ }
25
+
26
+ const content = fs.readFileSync(examplePath, "utf8");
27
+ const vars: string[] = [];
28
+
29
+ // Parse .env format: VAR_NAME=value
30
+ for (const line of content.split("\n")) {
31
+ const trimmed = line.trim();
32
+ // Skip comments and empty lines
33
+ if (!trimmed || trimmed.startsWith("#")) continue;
34
+
35
+ const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)=/);
36
+ if (match) {
37
+ vars.push(match[1]);
38
+ }
39
+ }
40
+
41
+ return vars;
42
+ } catch (err) {
43
+ console.warn(`Failed to read ${examplePath}:`, err);
44
+ return [];
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Ensures .env.local exists with required env vars from .env.local.example
50
+ */
51
+ function prepareEnvLocal(): { created: boolean; added: string[] } {
52
+ const envPath = path.join(cwd, ".env.local");
53
+ const examplePath = path.join(cwd, ".env.local.example");
54
+
55
+ if (!fs.existsSync(examplePath)) {
56
+ console.log("No .env.local.example found, skipping env preparation");
57
+ return { created: false, added: [] };
58
+ }
59
+
60
+ const requiredVars = getRequiredEnvVars(examplePath);
61
+ if (requiredVars.length === 0) {
62
+ console.log("No vars found in .env.local.example");
63
+ return { created: false, added: [] };
64
+ }
65
+
66
+ let content = "";
67
+ let created = false;
68
+ const added: string[] = [];
69
+
70
+ if (fs.existsSync(envPath)) {
71
+ content = fs.readFileSync(envPath, "utf8");
72
+ } else {
73
+ created = true;
74
+ }
75
+
76
+ // Check which vars are missing
77
+ for (const varName of requiredVars) {
78
+ const regex = new RegExp(`^${varName}=`, "m");
79
+ if (!regex.test(content)) {
80
+ if (content && !content.endsWith("\n")) {
81
+ content += "\n";
82
+ }
83
+ content += `${varName}=\n`;
84
+ added.push(varName);
85
+ }
86
+ }
87
+
88
+ // Write if there were any changes
89
+ if (created || added.length > 0) {
90
+ fs.writeFileSync(envPath, content);
91
+ }
92
+
93
+ return { created, added };
94
+ }
95
+
96
+ // Step 1: Prepare .env.local with required vars from wrangler.jsonc
97
+ function prepareEnv() {
98
+ try {
99
+ const updates = prepareEnvLocal();
100
+ if (updates.created) {
101
+ console.log("āœ“ Created .env.local");
102
+ }
103
+ if (updates.added.length > 0) {
104
+ console.log(`āœ“ Added missing env vars: ${updates.added.join(", ")}`);
105
+ }
106
+ if (!updates.created && updates.added.length === 0) {
107
+ console.log("āœ“ .env.local file already has all required vars");
108
+ }
109
+ } catch (error) {
110
+ console.error(String(error));
111
+ process.exit(1);
112
+ }
113
+ }
114
+
115
+ // Step 2: Run wrangler types
116
+ function runWranglerTypes() {
117
+ console.log("Running wrangler types...");
118
+ try {
119
+ execSync("wrangler types -c wrangler.jsonc --env-file .env.local", {
120
+ cwd,
121
+ stdio: "inherit",
122
+ });
123
+ console.log("āœ“ Wrangler types generated");
124
+ } catch {
125
+ console.error("Failed to run wrangler types");
126
+ process.exit(1);
127
+ }
128
+ }
129
+
130
+ // Run all steps
131
+ try {
132
+ prepareEnv();
133
+ runWranglerTypes();
134
+ console.log("\nāœ“ CF typegen completed successfully");
135
+ } catch (error: unknown) {
136
+ console.error("\nāœ— CF typegen failed:", error);
137
+ process.exit(1);
138
+ }