two-stroke 3.0.0 → 4.0.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/bin/test.mjs +21 -17
- package/package.json +13 -13
- package/src/test.ts +52 -132
- package/src/types.ts +2 -1
- package/tsconfig.json +7 -2
package/bin/test.mjs
CHANGED
|
@@ -13,6 +13,8 @@ if (fs.existsSync("wrangler.toml")) {
|
|
|
13
13
|
const miniflare = new Miniflare({
|
|
14
14
|
modules: true,
|
|
15
15
|
scriptPath: "dist/index.js",
|
|
16
|
+
compatibilityDate: "2024-09-23",
|
|
17
|
+
compatibilityFlags: ["nodejs_compat"],
|
|
16
18
|
});
|
|
17
19
|
const request = await fetch(
|
|
18
20
|
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
@@ -20,25 +22,27 @@ if (fs.existsSync("wrangler.toml")) {
|
|
|
20
22
|
{ SENTRY_DSN: null, SENTRY_ENVIRONMENT: null },
|
|
21
23
|
null,
|
|
22
24
|
);
|
|
23
|
-
const types = await openapiTS(await consumers.json(request.body));
|
|
24
25
|
await miniflare.dispose();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
if (request.status === 200) {
|
|
27
|
+
const types = await openapiTS(await consumers.json(request.body));
|
|
28
|
+
const printer = ts.createPrinter({});
|
|
29
|
+
const resultFile = ts.createSourceFile(
|
|
30
|
+
"test/api.d.ts",
|
|
31
|
+
"",
|
|
32
|
+
ts.ScriptTarget.Latest,
|
|
33
|
+
);
|
|
34
|
+
const result = printer.printNode(
|
|
35
|
+
ts.EmitHint.Unspecified,
|
|
36
|
+
types[0],
|
|
37
|
+
resultFile,
|
|
38
|
+
);
|
|
39
|
+
fs.writeFileSync(
|
|
40
|
+
"test/api.d.ts",
|
|
41
|
+
await prettier.format(result, { parser: "typescript" }),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
40
44
|
}
|
|
41
|
-
cmd("vitest
|
|
45
|
+
cmd("vitest", [
|
|
42
46
|
...(!process.argv.slice(2).includes("-w") &&
|
|
43
47
|
!process.argv.slice(2).includes("--watch")
|
|
44
48
|
? ["--run"]
|
package/package.json
CHANGED
|
@@ -10,22 +10,22 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@anatine/zod-openapi": "^2.2.6",
|
|
12
12
|
"@asteasolutions/zod-to-openapi": "^7.2.0",
|
|
13
|
-
"@cloudflare/workers
|
|
14
|
-
"@
|
|
15
|
-
"@
|
|
16
|
-
"@typescript-eslint/
|
|
17
|
-
"@
|
|
13
|
+
"@cloudflare/vitest-pool-workers": "^0.5.22",
|
|
14
|
+
"@cloudflare/workers-types": "^4.20241022.0",
|
|
15
|
+
"@sentry/cli": "^2.38.0",
|
|
16
|
+
"@typescript-eslint/eslint-plugin": "^8.11.0",
|
|
17
|
+
"@typescript-eslint/parser": "^8.11.0",
|
|
18
|
+
"@vitest/coverage-istanbul": "^2.1.3",
|
|
18
19
|
"eslint-config-prettier": "^9.1.0",
|
|
19
20
|
"eslint-config-two-stroke": "^1.1.5",
|
|
20
21
|
"eslint-config-typescript": "^3.0.0",
|
|
21
|
-
"jose": "^5.9.
|
|
22
|
+
"jose": "^5.9.6",
|
|
22
23
|
"jwk-subtle": "^1.0.7",
|
|
23
|
-
"miniflare": "^3.
|
|
24
|
-
"openapi-fetch": "^0.
|
|
25
|
-
"openapi-typescript": "^7.4.
|
|
24
|
+
"miniflare": "^3.20241022.0",
|
|
25
|
+
"openapi-fetch": "^0.13.0",
|
|
26
|
+
"openapi-typescript": "^7.4.2",
|
|
26
27
|
"openapi3-ts": "^4.4.0",
|
|
27
28
|
"pbkdf-subtle": "^1.0.0",
|
|
28
|
-
"toml": "^3.0.0",
|
|
29
29
|
"toucan-js": "^4.0.0",
|
|
30
30
|
"zod": "^3.23.8"
|
|
31
31
|
},
|
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
"name": "two-stroke",
|
|
52
52
|
"packageManager": "yarn@4.3.0",
|
|
53
53
|
"type": "module",
|
|
54
|
-
"version": "
|
|
54
|
+
"version": "4.0.0",
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/eslint": "^9.6.1",
|
|
57
|
-
"@types/node": "^22.
|
|
58
|
-
"eslint": "^9.
|
|
57
|
+
"@types/node": "^22.8.1",
|
|
58
|
+
"eslint": "^9.13.0",
|
|
59
59
|
"prettier": "^3.3.3",
|
|
60
60
|
"typescript": "^5.6.3",
|
|
61
61
|
"vitest": "^2.1.3"
|
package/src/test.ts
CHANGED
|
@@ -1,113 +1,24 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
1
|
import { JWTPayload, SignJWT, exportJWK, generateKeyPair } from "jose";
|
|
3
|
-
import {
|
|
4
|
-
import nodefs from "node:fs/promises";
|
|
5
|
-
import path from "node:path";
|
|
2
|
+
import { fetchMock, SELF, env } from "cloudflare:test";
|
|
6
3
|
import createClient from "openapi-fetch";
|
|
7
4
|
import consumers from "stream/consumers";
|
|
8
|
-
import toml from "toml";
|
|
9
|
-
import { URLSearchParams } from "url";
|
|
10
|
-
import { Env } from "./types";
|
|
11
5
|
|
|
12
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
13
|
-
export const setupTests = async <Paths extends {}>(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const config = toml.parse(fs.readFileSync("wrangler.toml", "utf8")) as {
|
|
18
|
-
compatibility_date: string;
|
|
19
|
-
compatibility_flags: string[];
|
|
20
|
-
queues?: {
|
|
21
|
-
consumers?: { queue: string }[];
|
|
22
|
-
producers?: { queue: string; binding: string }[];
|
|
23
|
-
};
|
|
24
|
-
r2_buckets?: { binding: string }[];
|
|
25
|
-
kv_namespaces?: { binding: string }[];
|
|
26
|
-
d1_databases?: { binding: string }[];
|
|
27
|
-
services?: { binding: string; service: string }[];
|
|
28
|
-
durable_objects?: { bindings?: { name: string; class_name: string }[] };
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const miniflare = new Miniflare({
|
|
32
|
-
modules: true,
|
|
33
|
-
scriptPath: "dist/index.js",
|
|
34
|
-
compatibilityDate: config.compatibility_date,
|
|
35
|
-
compatibilityFlags: config.compatibility_flags,
|
|
36
|
-
bindings: {
|
|
37
|
-
TOKEN_HASH:
|
|
38
|
-
"djAxlhzT1IU9QIP3UKdipECQPAGGoPQK86/GnTBcbHLtPC3ni6JkTQ/iIeF0KG0y1CZ+J+9W",
|
|
39
|
-
...bindings,
|
|
40
|
-
},
|
|
41
|
-
queueConsumers: (config.queues?.consumers ?? []).map(({ queue }) => queue),
|
|
42
|
-
queueProducers: Object.fromEntries(
|
|
43
|
-
(config.queues?.producers ?? []).map(({ binding, queue: queueName }) => [
|
|
44
|
-
binding,
|
|
45
|
-
{ queueName },
|
|
46
|
-
]),
|
|
47
|
-
),
|
|
48
|
-
r2Buckets: (config.r2_buckets ?? []).map(({ binding }) => binding),
|
|
49
|
-
kvNamespaces: (config.kv_namespaces ?? []).map(({ binding }) => binding),
|
|
50
|
-
d1Databases: (config.d1_databases ?? []).map(({ binding }) => binding),
|
|
51
|
-
serviceBindings: Object.fromEntries(
|
|
52
|
-
(config.services ?? []).map(({ binding, service }) => [binding, service]),
|
|
53
|
-
),
|
|
54
|
-
fetchMock,
|
|
55
|
-
durableObjects: Object.fromEntries(
|
|
56
|
-
(config.durable_objects?.bindings ?? []).map(({ name, class_name }) => [
|
|
57
|
-
name,
|
|
58
|
-
class_name,
|
|
59
|
-
]),
|
|
60
|
-
),
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/require-await
|
|
7
|
+
export const setupTests = async <Paths extends {}>() => {
|
|
8
|
+
beforeAll(() => {
|
|
9
|
+
fetchMock.activate();
|
|
10
|
+
fetchMock.disableNetConnect();
|
|
61
11
|
});
|
|
62
12
|
|
|
63
|
-
const url =
|
|
64
|
-
|
|
65
|
-
// Hack until miniflare supports auto running D1 migrations.
|
|
66
|
-
// Note: Each migration file can contain only a single statement.
|
|
67
|
-
await Promise.all(
|
|
68
|
-
(config.d1_databases ?? []).map(
|
|
69
|
-
async ({ binding }: { binding: string }) => {
|
|
70
|
-
const d1 = await miniflare.getD1Database(binding);
|
|
71
|
-
|
|
72
|
-
const migrationsDir = "./migrations";
|
|
73
|
-
let fileNames: string[] = [];
|
|
74
|
-
try {
|
|
75
|
-
fileNames = await nodefs.readdir(migrationsDir);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
console.log("Error loading migrations", error);
|
|
78
|
-
}
|
|
13
|
+
const url = new URL("https://example.com/");
|
|
79
14
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const migrationNumberA = parseInt(a.split("_")[0]!);
|
|
85
|
-
const migrationNumberB = parseInt(b.split("_")[0]!);
|
|
86
|
-
if (migrationNumberA < migrationNumberB) {
|
|
87
|
-
return -1;
|
|
88
|
-
}
|
|
89
|
-
if (migrationNumberA > migrationNumberB) {
|
|
90
|
-
return 1;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// numbers must be equal
|
|
94
|
-
return 0;
|
|
95
|
-
})) {
|
|
96
|
-
const migrationPath = path.join(migrationsDir, migrationName);
|
|
97
|
-
let migration = await nodefs.readFile(migrationPath, "utf8");
|
|
98
|
-
// Remove migration comment
|
|
99
|
-
migration = migration.split("\n").slice(1).join(" ");
|
|
100
|
-
await d1.exec(migration);
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
),
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
const client = createClient<Paths>({ baseUrl: url.toString() });
|
|
15
|
+
const client = createClient<Paths>({
|
|
16
|
+
baseUrl: url.toString(),
|
|
17
|
+
fetch: (...r) => SELF.fetch(...r),
|
|
18
|
+
});
|
|
107
19
|
|
|
108
20
|
return {
|
|
109
21
|
url,
|
|
110
|
-
miniflare,
|
|
111
22
|
fetchMock,
|
|
112
23
|
client,
|
|
113
24
|
async waitForQueue(trigger: () => Promise<void>) {
|
|
@@ -122,48 +33,57 @@ export const setupTests = async <Paths extends {}>(bindings: Env) => {
|
|
|
122
33
|
await waitUntil(() => expect(log).contains("Queue batch finished"));
|
|
123
34
|
console.log = orig;
|
|
124
35
|
},
|
|
125
|
-
async fakeJWK(
|
|
36
|
+
async fakeJWK(
|
|
37
|
+
issuer: keyof typeof env,
|
|
38
|
+
audience: keyof typeof env,
|
|
39
|
+
claims: JWTPayload,
|
|
40
|
+
) {
|
|
126
41
|
const { publicKey, privateKey } = await generateKeyPair("RS256");
|
|
127
42
|
const jwk = await exportJWK(publicKey);
|
|
128
43
|
jwk.kid = "test";
|
|
129
44
|
jwk.alg = "RS256";
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
45
|
+
beforeAll(() => {
|
|
46
|
+
fetchMock
|
|
47
|
+
.get(env[issuer] as string)
|
|
48
|
+
.intercept({
|
|
49
|
+
method: "GET",
|
|
50
|
+
path: "/.well-known/openid-configuration",
|
|
51
|
+
})
|
|
52
|
+
.reply(
|
|
53
|
+
200,
|
|
54
|
+
{
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
56
|
+
jwks_uri: `${env[issuer]}/.well-known/jwks`,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
headers: {
|
|
60
|
+
"Content-Type": "application/json",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
.persist();
|
|
65
|
+
fetchMock
|
|
66
|
+
.get(env[issuer] as string)
|
|
67
|
+
.intercept({ method: "GET", path: "/.well-known/jwks" })
|
|
68
|
+
.reply(
|
|
69
|
+
200,
|
|
70
|
+
{
|
|
71
|
+
keys: [jwk],
|
|
142
72
|
},
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.get(bindings[issuer] as string)
|
|
148
|
-
.intercept({ method: "GET", path: "/.well-known/jwks" })
|
|
149
|
-
.reply(
|
|
150
|
-
200,
|
|
151
|
-
{
|
|
152
|
-
keys: [jwk],
|
|
153
|
-
},
|
|
154
|
-
{
|
|
155
|
-
headers: {
|
|
156
|
-
"Content-Type": "application/json",
|
|
73
|
+
{
|
|
74
|
+
headers: {
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
},
|
|
157
77
|
},
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
78
|
+
)
|
|
79
|
+
.persist();
|
|
80
|
+
});
|
|
161
81
|
return await new SignJWT(claims)
|
|
162
82
|
.setProtectedHeader({ typ: "JWT", alg: "RS256", kid: jwk.kid })
|
|
163
83
|
.setIssuedAt()
|
|
164
84
|
.setNotBefore("5 minutes ago")
|
|
165
|
-
.setIssuer(
|
|
166
|
-
.setAudience([
|
|
85
|
+
.setIssuer(env[issuer] as string)
|
|
86
|
+
.setAudience([env[audience] as string])
|
|
167
87
|
.setExpirationTime("1h")
|
|
168
88
|
.sign(privateKey);
|
|
169
89
|
},
|
package/src/types.ts
CHANGED
package/tsconfig.json
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
"skipLibCheck": true,
|
|
12
12
|
"strict": true,
|
|
13
13
|
"target": "ESNext",
|
|
14
|
-
"types": [
|
|
15
|
-
|
|
14
|
+
"types": [
|
|
15
|
+
"@cloudflare/workers-types",
|
|
16
|
+
"vitest/globals",
|
|
17
|
+
"@cloudflare/vitest-pool-workers"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"exclude": ["../../dist"]
|
|
16
21
|
}
|