two-stroke 7.5.0 → 7.6.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/src/open-api.ts CHANGED
@@ -1,13 +1,8 @@
1
1
  import { z, ZodType } from "zod/v4";
2
- import { type Env, type Route } from "./types";
2
+ import { type Route } from "./types";
3
3
 
4
4
  export const openAPI =
5
- <T extends Env, A>(
6
- title: string,
7
- release: string,
8
- noAuth: () => A,
9
- routes: Route<T, A>[],
10
- ) =>
5
+ <T, A>(title: string, release: string, noAuth: () => A, routes: Route<T, A>[]) =>
11
6
  // eslint-disable-next-line @typescript-eslint/require-await
12
7
  async () => ({
13
8
  body: {
@@ -25,68 +20,63 @@ export const openAPI =
25
20
  },
26
21
  },
27
22
  paths: Object.fromEntries(
28
- Object.entries(Object.groupBy(routes, ({ path }) => path)).map(
29
- ([path, rs]) => [
30
- path,
31
- Object.fromEntries(
32
- (rs ?? []).map((r) => [
33
- r.method.toLocaleLowerCase(),
34
- {
35
- parameters: [
36
- ...Object.entries(
37
- (r.params?.shape ?? {}) as Record<string, ZodType>,
38
- ).map(([k, v]) => ({
23
+ Object.entries(Object.groupBy(routes, ({ path }) => path)).map(([path, rs]) => [
24
+ path,
25
+ Object.fromEntries(
26
+ (rs ?? []).map((r) => [
27
+ r.method.toLocaleLowerCase(),
28
+ {
29
+ parameters: [
30
+ ...Object.entries((r.params?.shape ?? {}) as Record<string, ZodType>).map(
31
+ ([k, v]) => ({
39
32
  name: k,
40
33
  in: "query",
41
34
  required: !v.safeParse(undefined).success,
42
35
  schema: z.toJSONSchema(v, { io: "input" }),
43
- })),
44
- ...Array.from(
45
- r.path.matchAll(/\/{(?<name>[^}]*)}/g),
46
- (match) => ({
47
- name: match.groups!.name,
48
- in: "path",
36
+ }),
37
+ ),
38
+ ...Array.from(r.path.matchAll(/\/{(?<name>[^}]*)}/g), (match) => ({
39
+ name: match.groups!.name,
40
+ in: "path",
41
+ required: true,
42
+ schema: {
43
+ type: "string",
44
+ },
45
+ })),
46
+ ],
47
+ ...(r.auth === noAuth ? {} : { security: [{ auth: [] }] }),
48
+ ...(r.method === "POST" || r.method === "PUT"
49
+ ? {
50
+ requestBody: {
49
51
  required: true,
50
- schema: {
51
- type: "string",
52
- },
53
- }),
54
- ),
55
- ],
56
- ...(r.auth === noAuth ? {} : { security: [{ auth: [] }] }),
57
- ...(r.method === "POST" || r.method === "PUT"
58
- ? {
59
- requestBody: {
60
- required: true,
61
- content: {
62
- "application/json": r.input
63
- ? {
64
- schema: z.toJSONSchema(r.input, {
65
- io: "input",
66
- }),
67
- }
68
- : undefined,
69
- },
70
- },
71
- }
72
- : {}),
73
- responses: {
74
- "200": {
75
- description: "OK",
76
- content: {
77
- "application/json": {
78
- schema: z.toJSONSchema(r.output),
52
+ content: {
53
+ "application/json": r.input
54
+ ? {
55
+ schema: z.toJSONSchema(r.input, {
56
+ io: "input",
57
+ }),
58
+ }
59
+ : undefined,
79
60
  },
80
61
  },
62
+ }
63
+ : {}),
64
+ responses: {
65
+ "200": {
66
+ description: "OK",
67
+ content: {
68
+ "application/json": {
69
+ schema: z.toJSONSchema(r.output),
70
+ },
81
71
  },
82
- "400": status400,
83
- "500": status500,
84
72
  },
73
+ "400": status400,
74
+ "500": status500,
85
75
  },
86
- ]),
87
- ),
88
- ],
89
- ),
76
+ },
77
+ ]),
78
+ ),
79
+ ]),
90
80
  ),
91
81
  },
92
82
  });
package/src/test.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { env, exports } from "cloudflare:workers";
2
2
  import { type JWTPayload, SignJWT, exportJWK, generateKeyPair } from "jose";
3
3
  import createClient from "openapi-fetch";
4
- import { http, HttpResponse } from 'msw'
5
- import { setupServer } from 'msw/node'
4
+ import { http, HttpResponse } from "msw";
5
+ import { setupServer } from "msw/node";
6
6
 
7
7
  const msw = setupServer();
8
8
  msw.listen();
@@ -32,11 +32,7 @@ export const setupTests = async <Paths extends {}>() => {
32
32
  await waitUntil(() => expect(log).contains("Queue batch finished"));
33
33
  console.log = orig;
34
34
  },
35
- async fakeJWK(
36
- issuer: keyof typeof env,
37
- audience: keyof typeof env,
38
- claims: JWTPayload,
39
- ) {
35
+ async fakeJWK(issuer: keyof typeof env, audience: keyof typeof env, claims: JWTPayload) {
40
36
  const { publicKey, privateKey } = await generateKeyPair("RS256");
41
37
  // Hack around Cloudflare not setting
42
38
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -49,16 +45,20 @@ export const setupTests = async <Paths extends {}>() => {
49
45
  jwk.kid = "test";
50
46
  jwk.alg = "RS256";
51
47
  beforeAll(() => {
52
- msw.use(...[
53
- http.get(`${env[issuer]}/.well-known/openid-configuration`, () =>
54
- HttpResponse.json({
55
- jwks_uri: `${env[issuer]}/.well-known/jwks`,
56
- })),
57
- http.get(`${env[issuer]}/.well-known/jwks`, () =>
58
- HttpResponse.json({
59
- keys: [jwk],
60
- }))
61
- ])
48
+ msw.use(
49
+ ...[
50
+ http.get(`${env[issuer]}/.well-known/openid-configuration`, () =>
51
+ HttpResponse.json({
52
+ jwks_uri: `${env[issuer]}/.well-known/jwks`,
53
+ }),
54
+ ),
55
+ http.get(`${env[issuer]}/.well-known/jwks`, () =>
56
+ HttpResponse.json({
57
+ keys: [jwk],
58
+ }),
59
+ ),
60
+ ],
61
+ );
62
62
  });
63
63
  return await new SignJWT(claims)
64
64
  .setProtectedHeader({ typ: "JWT", alg: "RS256", kid: jwk.kid })
@@ -107,11 +107,7 @@ export function recordFormRequest(
107
107
  },
108
108
  ) {
109
109
  return ({ body }: { body?: BodyInit }) => {
110
- cb(
111
- Object.fromEntries(
112
- new URLSearchParams((body?.valueOf() as string) ?? "").entries(),
113
- ),
114
- );
110
+ cb(Object.fromEntries(new URLSearchParams((body?.valueOf() as string) ?? "").entries()));
115
111
  return { statusCode, data, responseOptions: responseOptions ?? {} };
116
112
  };
117
113
  }
package/src/types.ts CHANGED
@@ -1,20 +1,7 @@
1
1
  import { Toucan } from "toucan-js";
2
2
  import { ZodType, ZodObject, z } from "zod/v4";
3
- export type Env = {
4
- [k: string]:
5
- | string
6
- | Queue
7
- | KVNamespace
8
- | R2Bucket
9
- | D1Database
10
- | Fetcher
11
- | Hyperdrive
12
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
- | DurableObjectNamespace<any>
14
- | Vectorize
15
- | ImagesBinding;
16
- };
17
- export type Route<T extends Env, A> =
3
+
4
+ export type Route<T, A> =
18
5
  | {
19
6
  auth: (c: { req: Request; env: T }) => Promise<A>;
20
7
  method: "GET" | "DELETE";
@@ -41,12 +28,11 @@ export type Route<T extends Env, A> =
41
28
 
42
29
  export type ExtractParameterNames<S extends string> =
43
30
  S extends `${string}{${infer Name}}${infer Rest}`
44
- ?
45
- Record<Name, string> & ExtractParameterNames<Rest>
31
+ ? Record<Name, string> & ExtractParameterNames<Rest>
46
32
  : Record<string, string>;
47
33
 
48
34
  export type Handler<
49
- T extends Env,
35
+ T,
50
36
  I extends ZodType | undefined,
51
37
  O extends ZodType,
52
38
  A,
package/tsconfig.json CHANGED
@@ -3,9 +3,7 @@
3
3
  "erasableSyntaxOnly": true,
4
4
  "esModuleInterop": true,
5
5
  "isolatedModules": true,
6
- "lib": [
7
- "ESNext"
8
- ],
6
+ "lib": ["ESNext"],
9
7
  "module": "ESNext",
10
8
  "moduleResolution": "bundler",
11
9
  "noUncheckedIndexedAccess": true,
@@ -20,7 +18,5 @@
20
18
  ],
21
19
  "verbatimModuleSyntax": true
22
20
  },
23
- "exclude": [
24
- "../../dist"
25
- ]
21
+ "exclude": ["../../dist"]
26
22
  }