two-stroke 4.3.0 → 4.3.2

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.
Files changed (3) hide show
  1. package/README.md +146 -1
  2. package/package.json +14 -14
  3. package/src/open-api.ts +1 -1
package/README.md CHANGED
@@ -1,3 +1,148 @@
1
1
  # two-stroke
2
2
 
3
- Simple Cloudflare Worker framework.
3
+ A minimalist framework for Cloudflare Workers with built-in routing, authentication, and validation.
4
+
5
+ ## Overview
6
+
7
+ Two-Stroke is a lightweight framework for building APIs with Cloudflare Workers. It provides a structured approach to defining routes, handling authentication, validating requests and responses with Zod, and managing errors with Sentry.
8
+
9
+ ## Features
10
+
11
+ - **Type-safe routing** with path parameter extraction
12
+ - **Schema validation** for request and response bodies using Zod
13
+ - **Built-in authentication** methods (JWT, PBKDF)
14
+ - **Error handling** with Sentry integration
15
+ - **CORS support** out of the box
16
+ - **Queue handling** for background processing
17
+ - **Cron job support** for scheduled tasks
18
+ - **Email handling** capabilities
19
+ - **OpenAPI documentation** generation
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install two-stroke
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```typescript
30
+ import { twoStroke } from "two-stroke";
31
+ import { z } from "zod";
32
+
33
+ // Define your environment type
34
+ type MyEnv = {
35
+ MY_SECRET: string;
36
+ MY_KV: KVNamespace;
37
+ };
38
+
39
+ // Create a Two-Stroke app
40
+ const app = twoStroke<MyEnv>("My API", "1.0.0");
41
+
42
+ // Define routes
43
+ app.get(
44
+ app.noAuth,
45
+ "/hello",
46
+ z.object({ message: z.string() }),
47
+ async ({ env }) => {
48
+ return {
49
+ body: { message: "Hello, World!" },
50
+ };
51
+ }
52
+ );
53
+
54
+ // Define a route with path parameters
55
+ app.get(
56
+ app.noAuth,
57
+ "/users/{userId}",
58
+ z.object({ user: z.object({ id: z.string(), name: z.string() }) }),
59
+ async ({ params }) => {
60
+ return {
61
+ body: { user: { id: params.userId, name: "John Doe" } },
62
+ };
63
+ }
64
+ );
65
+
66
+ // Define a POST route with request validation
67
+ app.post(
68
+ app.noAuth,
69
+ "/messages",
70
+ z.object({ content: z.string().min(1) }),
71
+ z.object({ id: z.string() }),
72
+ async ({ body }) => {
73
+ return {
74
+ body: { id: "msg_123" },
75
+ };
76
+ }
77
+ );
78
+
79
+ // Export the worker handlers
80
+ export default app;
81
+ ```
82
+
83
+ ## Authentication
84
+
85
+ Two-Stroke provides several authentication methods out of the box:
86
+
87
+ ```typescript
88
+ // No authentication
89
+ app.get(app.noAuth, "/public", z.object({ message: z.string() }), async () => ({
90
+ body: { message: "Public endpoint" },
91
+ }));
92
+
93
+ // PBKDF authentication
94
+ app.get(
95
+ app.pbkdf("API_KEY"),
96
+ "/protected",
97
+ z.object({ message: z.string() }),
98
+ async () => ({ body: { message: "Protected endpoint" } })
99
+ );
100
+
101
+ // JWT authentication
102
+ app.get(
103
+ app.jwt<{ userId: string }>("JWT_SECRET", "JWT_AUDIENCE"),
104
+ "/user-data",
105
+ z.object({ userId: z.string() }),
106
+ async ({ claims }) => ({ body: { userId: claims.userId } })
107
+ );
108
+ ```
109
+
110
+ ## Queue Handling
111
+
112
+ ```typescript
113
+ // Define a queue handler
114
+ app.queueHandler(
115
+ z.object({ id: z.string() }),
116
+ async ({ batch, parsedBatch, env }) => {
117
+ for (let i = 0; i < batch.messages.length; i++) {
118
+ if (parsedBatch[i].success) {
119
+ const data = parsedBatch[i].data;
120
+ // Process queue message
121
+ console.log(`Processing message: ${data.id}`);
122
+ }
123
+ }
124
+ }
125
+ );
126
+
127
+ // Add to queue with retry logic
128
+ import { addToQueue } from "two-stroke";
129
+
130
+ await addToQueue(
131
+ env.MY_QUEUE,
132
+ { id: "task_123" },
133
+ {
134
+ retries: 3,
135
+ backoffFactor: 2,
136
+ }
137
+ );
138
+ ```
139
+
140
+ ## Scheduled Tasks
141
+
142
+ ```typescript
143
+ // Define a scheduled task
144
+ app.schedule("*/15 * * * *", async ({ env }) => {
145
+ // Run every 15 minutes
146
+ console.log("Running scheduled task");
147
+ });
148
+ ```
package/package.json CHANGED
@@ -8,21 +8,21 @@
8
8
  "type-check": "./bin/type-check.mjs"
9
9
  },
10
10
  "dependencies": {
11
- "@anatine/zod-openapi": "^2.2.7",
11
+ "@anatine/zod-openapi": "^2.2.8",
12
12
  "@asteasolutions/zod-to-openapi": "^7.3.0",
13
- "@cloudflare/vitest-pool-workers": "^0.8.8",
14
- "@cloudflare/workers-types": "^4.20250327.0",
13
+ "@cloudflare/vitest-pool-workers": "^0.8.17",
14
+ "@cloudflare/workers-types": "^4.20250416.0",
15
15
  "@sentry/cli": "^2.43.0",
16
- "@types/node": "^22.13.14",
17
- "@typescript-eslint/eslint-plugin": "^8.28.0",
18
- "@typescript-eslint/parser": "^8.28.0",
19
- "@vitest/coverage-istanbul": "^3.0.9",
20
- "eslint-config-prettier": "^10.1.1",
21
- "eslint-config-two-stroke": "^1.1.12",
16
+ "@types/node": "^22.14.1",
17
+ "@typescript-eslint/eslint-plugin": "^8.30.1",
18
+ "@typescript-eslint/parser": "^8.30.1",
19
+ "@vitest/coverage-istanbul": "^3.1.1",
20
+ "eslint-config-prettier": "^10.1.2",
21
+ "eslint-config-two-stroke": "^1.1.13",
22
22
  "eslint-config-typescript": "^3.0.0",
23
23
  "jose": "^6.0.10",
24
24
  "jwk-subtle": "^1.0.7",
25
- "miniflare": "^4.20250321.1",
25
+ "miniflare": "^4.20250410.1",
26
26
  "openapi-fetch": "^0.13.5",
27
27
  "openapi-typescript": "^7.6.1",
28
28
  "openapi3-ts": "^4.4.0",
@@ -52,12 +52,12 @@
52
52
  "name": "two-stroke",
53
53
  "packageManager": "yarn@4.6.0",
54
54
  "type": "module",
55
- "version": "4.3.0",
55
+ "version": "4.3.2",
56
56
  "devDependencies": {
57
57
  "@types/eslint": "^9.6.1",
58
- "eslint": "^9.23.0",
58
+ "eslint": "^9.24.0",
59
59
  "prettier": "^3.5.3",
60
- "typescript": "^5.8.2",
61
- "vitest": "^3.0.9"
60
+ "typescript": "^5.8.3",
61
+ "vitest": "^3.1.1"
62
62
  }
63
63
  }
package/src/open-api.ts CHANGED
@@ -108,7 +108,7 @@ export const openAPI =
108
108
  const generator = new OpenApiGeneratorV31(openAPIRegistry.definitions);
109
109
  return {
110
110
  body: generator.generateDocument({
111
- openapi: "3.1",
111
+ openapi: "3.1.0",
112
112
  info: {
113
113
  title,
114
114
  version: release,