@xivdyetools/test-utils 1.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/LICENSE +37 -0
- package/README.md +144 -0
- package/dist/assertions/index.d.ts +7 -0
- package/dist/assertions/index.d.ts.map +1 -0
- package/dist/assertions/index.js +7 -0
- package/dist/assertions/index.js.map +1 -0
- package/dist/assertions/response.d.ts +84 -0
- package/dist/assertions/response.d.ts.map +1 -0
- package/dist/assertions/response.js +125 -0
- package/dist/assertions/response.js.map +1 -0
- package/dist/auth/context.d.ts +55 -0
- package/dist/auth/context.d.ts.map +1 -0
- package/dist/auth/context.js +95 -0
- package/dist/auth/context.js.map +1 -0
- package/dist/auth/headers.d.ts +64 -0
- package/dist/auth/headers.d.ts.map +1 -0
- package/dist/auth/headers.js +101 -0
- package/dist/auth/headers.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jwt.d.ts +76 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +77 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/signature.d.ts +64 -0
- package/dist/auth/signature.d.ts.map +1 -0
- package/dist/auth/signature.js +75 -0
- package/dist/auth/signature.js.map +1 -0
- package/dist/cloudflare/analytics.d.ts +55 -0
- package/dist/cloudflare/analytics.d.ts.map +1 -0
- package/dist/cloudflare/analytics.js +44 -0
- package/dist/cloudflare/analytics.js.map +1 -0
- package/dist/cloudflare/d1.d.ts +101 -0
- package/dist/cloudflare/d1.d.ts.map +1 -0
- package/dist/cloudflare/d1.js +148 -0
- package/dist/cloudflare/d1.js.map +1 -0
- package/dist/cloudflare/fetcher.d.ts +72 -0
- package/dist/cloudflare/fetcher.d.ts.map +1 -0
- package/dist/cloudflare/fetcher.js +136 -0
- package/dist/cloudflare/fetcher.js.map +1 -0
- package/dist/cloudflare/index.d.ts +12 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +12 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/cloudflare/kv.d.ts +85 -0
- package/dist/cloudflare/kv.d.ts.map +1 -0
- package/dist/cloudflare/kv.js +138 -0
- package/dist/cloudflare/kv.js.map +1 -0
- package/dist/cloudflare/r2.d.ts +88 -0
- package/dist/cloudflare/r2.d.ts.map +1 -0
- package/dist/cloudflare/r2.js +127 -0
- package/dist/cloudflare/r2.js.map +1 -0
- package/dist/constants/index.d.ts +8 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +8 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/pkce.d.ts +89 -0
- package/dist/constants/pkce.d.ts.map +1 -0
- package/dist/constants/pkce.js +107 -0
- package/dist/constants/pkce.js.map +1 -0
- package/dist/constants/secrets.d.ts +72 -0
- package/dist/constants/secrets.d.ts.map +1 -0
- package/dist/constants/secrets.js +73 -0
- package/dist/constants/secrets.js.map +1 -0
- package/dist/dom/canvas.d.ts +108 -0
- package/dist/dom/canvas.d.ts.map +1 -0
- package/dist/dom/canvas.js +125 -0
- package/dist/dom/canvas.js.map +1 -0
- package/dist/dom/fetch.d.ts +69 -0
- package/dist/dom/fetch.d.ts.map +1 -0
- package/dist/dom/fetch.js +107 -0
- package/dist/dom/fetch.js.map +1 -0
- package/dist/dom/index.d.ts +12 -0
- package/dist/dom/index.d.ts.map +1 -0
- package/dist/dom/index.js +12 -0
- package/dist/dom/index.js.map +1 -0
- package/dist/dom/localStorage.d.ts +80 -0
- package/dist/dom/localStorage.d.ts.map +1 -0
- package/dist/dom/localStorage.js +124 -0
- package/dist/dom/localStorage.js.map +1 -0
- package/dist/dom/matchMedia.d.ts +51 -0
- package/dist/dom/matchMedia.d.ts.map +1 -0
- package/dist/dom/matchMedia.js +120 -0
- package/dist/dom/matchMedia.js.map +1 -0
- package/dist/dom/resizeObserver.d.ts +68 -0
- package/dist/dom/resizeObserver.d.ts.map +1 -0
- package/dist/dom/resizeObserver.js +99 -0
- package/dist/dom/resizeObserver.js.map +1 -0
- package/dist/factories/category.d.ts +74 -0
- package/dist/factories/category.d.ts.map +1 -0
- package/dist/factories/category.js +99 -0
- package/dist/factories/category.js.map +1 -0
- package/dist/factories/dye.d.ts +76 -0
- package/dist/factories/dye.d.ts.map +1 -0
- package/dist/factories/dye.js +211 -0
- package/dist/factories/dye.js.map +1 -0
- package/dist/factories/index.d.ts +13 -0
- package/dist/factories/index.d.ts.map +1 -0
- package/dist/factories/index.js +14 -0
- package/dist/factories/index.js.map +1 -0
- package/dist/factories/preset.d.ts +105 -0
- package/dist/factories/preset.d.ts.map +1 -0
- package/dist/factories/preset.js +170 -0
- package/dist/factories/preset.js.map +1 -0
- package/dist/factories/user.d.ts +74 -0
- package/dist/factories/user.d.ts.map +1 -0
- package/dist/factories/user.js +115 -0
- package/dist/factories/user.js.map +1 -0
- package/dist/factories/vote.d.ts +55 -0
- package/dist/factories/vote.d.ts.map +1 -0
- package/dist/factories/vote.js +68 -0
- package/dist/factories/vote.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/counters.d.ts +35 -0
- package/dist/utils/counters.d.ts.map +1 -0
- package/dist/utils/counters.js +53 -0
- package/dist/utils/counters.js.map +1 -0
- package/dist/utils/crypto.d.ts +42 -0
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/crypto.js +72 -0
- package/dist/utils/crypto.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request header builders for testing API authentication
|
|
3
|
+
*
|
|
4
|
+
* Provides helper functions to build authentication headers
|
|
5
|
+
* for testing API endpoints.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // JWT auth headers
|
|
10
|
+
* const headers = authHeaders(jwt);
|
|
11
|
+
*
|
|
12
|
+
* // Bot auth headers with signature
|
|
13
|
+
* const botHeaders = await authHeadersWithSignature(
|
|
14
|
+
* 'bot-token',
|
|
15
|
+
* 'user-id',
|
|
16
|
+
* 'username'
|
|
17
|
+
* );
|
|
18
|
+
*
|
|
19
|
+
* // Use in requests
|
|
20
|
+
* const response = await fetch('/api/protected', { headers });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
import { createBotSignature, TEST_SIGNING_SECRET } from './signature.js';
|
|
24
|
+
/**
|
|
25
|
+
* Creates basic auth headers with a bearer token
|
|
26
|
+
*
|
|
27
|
+
* @param token - The bearer token (JWT or API key)
|
|
28
|
+
* @param userId - Optional Discord user ID
|
|
29
|
+
* @param userName - Optional Discord username
|
|
30
|
+
* @returns Headers object
|
|
31
|
+
*/
|
|
32
|
+
export function authHeaders(token, userId, userName) {
|
|
33
|
+
const headers = {
|
|
34
|
+
Authorization: `Bearer ${token}`,
|
|
35
|
+
};
|
|
36
|
+
if (userId) {
|
|
37
|
+
headers['X-User-Discord-ID'] = userId;
|
|
38
|
+
}
|
|
39
|
+
if (userName) {
|
|
40
|
+
headers['X-User-Discord-Name'] = userName;
|
|
41
|
+
}
|
|
42
|
+
return headers;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates auth headers with HMAC signature for bot authentication
|
|
46
|
+
*
|
|
47
|
+
* @param token - The bot API token
|
|
48
|
+
* @param userId - Discord user ID (optional)
|
|
49
|
+
* @param userName - Discord username (optional)
|
|
50
|
+
* @param signingSecret - The signing secret (defaults to TEST_SIGNING_SECRET)
|
|
51
|
+
* @returns Headers object with signature
|
|
52
|
+
*/
|
|
53
|
+
export async function authHeadersWithSignature(token, userId, userName, signingSecret = TEST_SIGNING_SECRET) {
|
|
54
|
+
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
55
|
+
const userIdStr = userId ?? '';
|
|
56
|
+
const userNameStr = userName ?? '';
|
|
57
|
+
const signature = await createBotSignature(timestamp, userIdStr, userNameStr, signingSecret);
|
|
58
|
+
const headers = {
|
|
59
|
+
Authorization: `Bearer ${token}`,
|
|
60
|
+
'X-Request-Signature': signature,
|
|
61
|
+
'X-Request-Timestamp': timestamp,
|
|
62
|
+
};
|
|
63
|
+
if (userId) {
|
|
64
|
+
headers['X-User-Discord-ID'] = userId;
|
|
65
|
+
}
|
|
66
|
+
if (userName) {
|
|
67
|
+
headers['X-User-Discord-Name'] = userName;
|
|
68
|
+
}
|
|
69
|
+
return headers;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Creates JSON content headers
|
|
73
|
+
*
|
|
74
|
+
* @returns Headers object with Content-Type: application/json
|
|
75
|
+
*/
|
|
76
|
+
export function jsonHeaders() {
|
|
77
|
+
return {
|
|
78
|
+
'Content-Type': 'application/json',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Merges multiple header objects
|
|
83
|
+
*
|
|
84
|
+
* @param headerObjects - Header objects to merge
|
|
85
|
+
* @returns Merged headers object
|
|
86
|
+
*/
|
|
87
|
+
export function mergeHeaders(...headerObjects) {
|
|
88
|
+
return Object.assign({}, ...headerObjects);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates authenticated JSON request headers
|
|
92
|
+
*
|
|
93
|
+
* @param token - The bearer token
|
|
94
|
+
* @param userId - Optional Discord user ID
|
|
95
|
+
* @param userName - Optional Discord username
|
|
96
|
+
* @returns Headers object with auth and content-type
|
|
97
|
+
*/
|
|
98
|
+
export function authenticatedJsonHeaders(token, userId, userName) {
|
|
99
|
+
return mergeHeaders(jsonHeaders(), authHeaders(token, userId, userName));
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headers.js","sourceRoot":"","sources":["../../src/auth/headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,KAAa,EACb,MAAe,EACf,QAAiB;IAEjB,MAAM,OAAO,GAA2B;QACtC,aAAa,EAAE,UAAU,KAAK,EAAE;KACjC,CAAC;IAEF,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC;IACxC,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,qBAAqB,CAAC,GAAG,QAAQ,CAAC;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAa,EACb,MAAe,EACf,QAAiB,EACjB,gBAAwB,mBAAmB;IAE3C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3D,MAAM,SAAS,GAAG,MAAM,IAAI,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,QAAQ,IAAI,EAAE,CAAC;IAEnC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CACxC,SAAS,EACT,SAAS,EACT,WAAW,EACX,aAAa,CACd,CAAC;IAEF,MAAM,OAAO,GAA2B;QACtC,aAAa,EAAE,UAAU,KAAK,EAAE;QAChC,qBAAqB,EAAE,SAAS;QAChC,qBAAqB,EAAE,SAAS;KACjC,CAAC;IAEF,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC;IACxC,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,CAAC,qBAAqB,CAAC,GAAG,QAAQ,CAAC;IAC5C,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO;QACL,cAAc,EAAE,kBAAkB;KACnC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,GAAG,aAAuC;IAE1C,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,aAAa,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAAa,EACb,MAAe,EACf,QAAiB;IAEjB,OAAO,YAAY,CAAC,WAAW,EAAE,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication helpers for testing
|
|
3
|
+
*
|
|
4
|
+
* Provides JWT creation, HMAC signatures, auth contexts, and header builders.
|
|
5
|
+
*/
|
|
6
|
+
export * from './jwt.js';
|
|
7
|
+
export * from './signature.js';
|
|
8
|
+
export * from './context.js';
|
|
9
|
+
export * from './headers.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication helpers for testing
|
|
3
|
+
*
|
|
4
|
+
* Provides JWT creation, HMAC signatures, auth contexts, and header builders.
|
|
5
|
+
*/
|
|
6
|
+
export * from './jwt.js';
|
|
7
|
+
export * from './signature.js';
|
|
8
|
+
export * from './context.js';
|
|
9
|
+
export * from './headers.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT creation helpers for testing
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to create valid and expired JWTs for testing
|
|
5
|
+
* authentication flows.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // Create a valid JWT
|
|
10
|
+
* const jwt = await createTestJWT('your-secret', {
|
|
11
|
+
* sub: 'user-123',
|
|
12
|
+
* username: 'TestUser',
|
|
13
|
+
* global_name: 'Test User',
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // Create an expired JWT for testing expiration handling
|
|
17
|
+
* const expiredJwt = await createExpiredJWT('your-secret');
|
|
18
|
+
*
|
|
19
|
+
* // Use in request headers
|
|
20
|
+
* const response = await fetch('/api/protected', {
|
|
21
|
+
* headers: { Authorization: `Bearer ${jwt}` },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* JWT payload for test tokens
|
|
27
|
+
*/
|
|
28
|
+
export interface TestJWTPayload {
|
|
29
|
+
/** Subject (user ID) */
|
|
30
|
+
sub: string;
|
|
31
|
+
/** Username */
|
|
32
|
+
username: string;
|
|
33
|
+
/** Display name (optional) */
|
|
34
|
+
global_name?: string | null;
|
|
35
|
+
/** Avatar hash (optional) */
|
|
36
|
+
avatar?: string | null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Full JWT payload including standard claims
|
|
40
|
+
*/
|
|
41
|
+
export interface FullJWTPayload extends TestJWTPayload {
|
|
42
|
+
/** Issued at (Unix timestamp) */
|
|
43
|
+
iat: number;
|
|
44
|
+
/** Expiration (Unix timestamp) */
|
|
45
|
+
exp: number;
|
|
46
|
+
/** Issuer */
|
|
47
|
+
iss: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates a valid JWT for testing
|
|
51
|
+
*
|
|
52
|
+
* @param secret - The JWT signing secret
|
|
53
|
+
* @param payload - The JWT payload (sub, username, etc.)
|
|
54
|
+
* @param expiresInSeconds - How long until the token expires (default: 3600 = 1 hour)
|
|
55
|
+
* @param issuer - The token issuer (default: 'xivdyetools-oauth-worker')
|
|
56
|
+
* @returns A signed JWT string
|
|
57
|
+
*/
|
|
58
|
+
export declare function createTestJWT(secret: string, payload: TestJWTPayload, expiresInSeconds?: number, issuer?: string): Promise<string>;
|
|
59
|
+
/**
|
|
60
|
+
* Creates an expired JWT for testing expiration handling
|
|
61
|
+
*
|
|
62
|
+
* @param secret - The JWT signing secret
|
|
63
|
+
* @param payload - Optional custom payload (defaults provided)
|
|
64
|
+
* @returns A signed but expired JWT string
|
|
65
|
+
*/
|
|
66
|
+
export declare function createExpiredJWT(secret: string, payload?: TestJWTPayload): Promise<string>;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a JWT with a specific expiration time
|
|
69
|
+
*
|
|
70
|
+
* @param secret - The JWT signing secret
|
|
71
|
+
* @param payload - The JWT payload
|
|
72
|
+
* @param expTimestamp - The expiration Unix timestamp
|
|
73
|
+
* @returns A signed JWT string
|
|
74
|
+
*/
|
|
75
|
+
export declare function createJWTWithExpiration(secret: string, payload: TestJWTPayload, expTimestamp: number): Promise<string>;
|
|
76
|
+
//# sourceMappingURL=jwt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAe,SAAQ,cAAc;IACpD,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,kCAAkC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,EACvB,gBAAgB,SAAO,EACvB,MAAM,SAA6B,GAClC,OAAO,CAAC,MAAM,CAAC,CAiCjB;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAiD,GACzD,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED;;;;;;;GAOG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAIjB"}
|
package/dist/auth/jwt.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT creation helpers for testing
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to create valid and expired JWTs for testing
|
|
5
|
+
* authentication flows.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // Create a valid JWT
|
|
10
|
+
* const jwt = await createTestJWT('your-secret', {
|
|
11
|
+
* sub: 'user-123',
|
|
12
|
+
* username: 'TestUser',
|
|
13
|
+
* global_name: 'Test User',
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // Create an expired JWT for testing expiration handling
|
|
17
|
+
* const expiredJwt = await createExpiredJWT('your-secret');
|
|
18
|
+
*
|
|
19
|
+
* // Use in request headers
|
|
20
|
+
* const response = await fetch('/api/protected', {
|
|
21
|
+
* headers: { Authorization: `Bearer ${jwt}` },
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import { base64UrlEncode } from '../utils/crypto.js';
|
|
26
|
+
/**
|
|
27
|
+
* Creates a valid JWT for testing
|
|
28
|
+
*
|
|
29
|
+
* @param secret - The JWT signing secret
|
|
30
|
+
* @param payload - The JWT payload (sub, username, etc.)
|
|
31
|
+
* @param expiresInSeconds - How long until the token expires (default: 3600 = 1 hour)
|
|
32
|
+
* @param issuer - The token issuer (default: 'xivdyetools-oauth-worker')
|
|
33
|
+
* @returns A signed JWT string
|
|
34
|
+
*/
|
|
35
|
+
export async function createTestJWT(secret, payload, expiresInSeconds = 3600, issuer = 'xivdyetools-oauth-worker') {
|
|
36
|
+
const header = { alg: 'HS256', typ: 'JWT' };
|
|
37
|
+
const now = Math.floor(Date.now() / 1000);
|
|
38
|
+
const fullPayload = {
|
|
39
|
+
...payload,
|
|
40
|
+
iat: now,
|
|
41
|
+
exp: now + expiresInSeconds,
|
|
42
|
+
iss: issuer,
|
|
43
|
+
};
|
|
44
|
+
const encoder = new TextEncoder();
|
|
45
|
+
const encodedHeader = base64UrlEncode(JSON.stringify(header));
|
|
46
|
+
const encodedPayload = base64UrlEncode(JSON.stringify(fullPayload));
|
|
47
|
+
const signatureInput = `${encodedHeader}.${encodedPayload}`;
|
|
48
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
49
|
+
const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(signatureInput));
|
|
50
|
+
const encodedSignature = base64UrlEncode(String.fromCharCode(...new Uint8Array(signature)));
|
|
51
|
+
return `${encodedHeader}.${encodedPayload}.${encodedSignature}`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Creates an expired JWT for testing expiration handling
|
|
55
|
+
*
|
|
56
|
+
* @param secret - The JWT signing secret
|
|
57
|
+
* @param payload - Optional custom payload (defaults provided)
|
|
58
|
+
* @returns A signed but expired JWT string
|
|
59
|
+
*/
|
|
60
|
+
export async function createExpiredJWT(secret, payload = { sub: '123', username: 'test' }) {
|
|
61
|
+
// Create token that expired 1 hour ago
|
|
62
|
+
return createTestJWT(secret, payload, -3600);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Creates a JWT with a specific expiration time
|
|
66
|
+
*
|
|
67
|
+
* @param secret - The JWT signing secret
|
|
68
|
+
* @param payload - The JWT payload
|
|
69
|
+
* @param expTimestamp - The expiration Unix timestamp
|
|
70
|
+
* @returns A signed JWT string
|
|
71
|
+
*/
|
|
72
|
+
export async function createJWTWithExpiration(secret, payload, expTimestamp) {
|
|
73
|
+
const now = Math.floor(Date.now() / 1000);
|
|
74
|
+
const expiresInSeconds = expTimestamp - now;
|
|
75
|
+
return createTestJWT(secret, payload, expiresInSeconds);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=jwt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AA4BrD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,OAAuB,EACvB,gBAAgB,GAAG,IAAI,EACvB,MAAM,GAAG,0BAA0B;IAEnC,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAE1C,MAAM,WAAW,GAAmB;QAClC,GAAG,OAAO;QACV,GAAG,EAAE,GAAG;QACR,GAAG,EAAE,GAAG,GAAG,gBAAgB;QAC3B,GAAG,EAAE,MAAM;KACZ,CAAC;IAEF,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAElC,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,MAAM,cAAc,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;IAEpE,MAAM,cAAc,GAAG,GAAG,aAAa,IAAI,cAAc,EAAE,CAAC;IAE5D,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACvC,KAAK,EACL,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EACtB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IAExF,MAAM,gBAAgB,GAAG,eAAe,CACtC,MAAM,CAAC,YAAY,CAAC,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC,CAClD,CAAC;IAEF,OAAO,GAAG,aAAa,IAAI,cAAc,IAAI,gBAAgB,EAAE,CAAC;AAClE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAc,EACd,UAA0B,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE;IAE1D,uCAAuC;IACvC,OAAO,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAAc,EACd,OAAuB,EACvB,YAAoB;IAEpB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,gBAAgB,GAAG,YAAY,GAAG,GAAG,CAAC;IAC5C,OAAO,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMAC signature helpers for testing bot authentication
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to create HMAC-SHA256 signatures for testing
|
|
5
|
+
* bot-to-API authentication flows.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // Create a bot signature
|
|
10
|
+
* const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
11
|
+
* const signature = await createBotSignature(
|
|
12
|
+
* timestamp,
|
|
13
|
+
* 'user-discord-id',
|
|
14
|
+
* 'username',
|
|
15
|
+
* 'signing-secret'
|
|
16
|
+
* );
|
|
17
|
+
*
|
|
18
|
+
* // Use in request headers
|
|
19
|
+
* const headers = {
|
|
20
|
+
* 'X-Request-Signature': signature,
|
|
21
|
+
* 'X-Request-Timestamp': timestamp,
|
|
22
|
+
* 'X-User-Discord-ID': 'user-discord-id',
|
|
23
|
+
* 'X-User-Discord-Name': 'username',
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
/** Default signing secret for tests */
|
|
28
|
+
export declare const TEST_SIGNING_SECRET = "test-signing-secret";
|
|
29
|
+
/**
|
|
30
|
+
* Creates an HMAC-SHA256 signature for bot authentication
|
|
31
|
+
*
|
|
32
|
+
* The signature is created from: `${timestamp}:${userDiscordId}:${userName}`
|
|
33
|
+
*
|
|
34
|
+
* @param timestamp - Unix timestamp string
|
|
35
|
+
* @param userDiscordId - Discord user ID
|
|
36
|
+
* @param userName - Discord username
|
|
37
|
+
* @param secret - Signing secret (defaults to TEST_SIGNING_SECRET)
|
|
38
|
+
* @returns Hex-encoded HMAC-SHA256 signature
|
|
39
|
+
*/
|
|
40
|
+
export declare function createBotSignature(timestamp: string, userDiscordId: string, userName: string, secret?: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a signature with the current timestamp
|
|
43
|
+
*
|
|
44
|
+
* @param userDiscordId - Discord user ID
|
|
45
|
+
* @param userName - Discord username
|
|
46
|
+
* @param secret - Signing secret (defaults to TEST_SIGNING_SECRET)
|
|
47
|
+
* @returns Object with signature and timestamp
|
|
48
|
+
*/
|
|
49
|
+
export declare function createTimestampedSignature(userDiscordId: string, userName: string, secret?: string): Promise<{
|
|
50
|
+
signature: string;
|
|
51
|
+
timestamp: string;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* Verifies an HMAC-SHA256 signature
|
|
55
|
+
*
|
|
56
|
+
* @param signature - The signature to verify (hex string)
|
|
57
|
+
* @param timestamp - Unix timestamp string
|
|
58
|
+
* @param userDiscordId - Discord user ID
|
|
59
|
+
* @param userName - Discord username
|
|
60
|
+
* @param secret - Signing secret
|
|
61
|
+
* @returns True if signature is valid
|
|
62
|
+
*/
|
|
63
|
+
export declare function verifyBotSignature(signature: string, timestamp: string, userDiscordId: string, userName: string, secret?: string): Promise<boolean>;
|
|
64
|
+
//# sourceMappingURL=signature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/auth/signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,uCAAuC;AACvC,eAAO,MAAM,mBAAmB,wBAAwB,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,MAAM,CAAC,CAejB;AAED;;;;;;;GAOG;AACH,wBAAsB,0BAA0B,CAC9C,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAInD;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAA4B,GACnC,OAAO,CAAC,OAAO,CAAC,CAGlB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMAC signature helpers for testing bot authentication
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to create HMAC-SHA256 signatures for testing
|
|
5
|
+
* bot-to-API authentication flows.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* // Create a bot signature
|
|
10
|
+
* const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
11
|
+
* const signature = await createBotSignature(
|
|
12
|
+
* timestamp,
|
|
13
|
+
* 'user-discord-id',
|
|
14
|
+
* 'username',
|
|
15
|
+
* 'signing-secret'
|
|
16
|
+
* );
|
|
17
|
+
*
|
|
18
|
+
* // Use in request headers
|
|
19
|
+
* const headers = {
|
|
20
|
+
* 'X-Request-Signature': signature,
|
|
21
|
+
* 'X-Request-Timestamp': timestamp,
|
|
22
|
+
* 'X-User-Discord-ID': 'user-discord-id',
|
|
23
|
+
* 'X-User-Discord-Name': 'username',
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import { bytesToHex } from '../utils/crypto.js';
|
|
28
|
+
/** Default signing secret for tests */
|
|
29
|
+
export const TEST_SIGNING_SECRET = 'test-signing-secret';
|
|
30
|
+
/**
|
|
31
|
+
* Creates an HMAC-SHA256 signature for bot authentication
|
|
32
|
+
*
|
|
33
|
+
* The signature is created from: `${timestamp}:${userDiscordId}:${userName}`
|
|
34
|
+
*
|
|
35
|
+
* @param timestamp - Unix timestamp string
|
|
36
|
+
* @param userDiscordId - Discord user ID
|
|
37
|
+
* @param userName - Discord username
|
|
38
|
+
* @param secret - Signing secret (defaults to TEST_SIGNING_SECRET)
|
|
39
|
+
* @returns Hex-encoded HMAC-SHA256 signature
|
|
40
|
+
*/
|
|
41
|
+
export async function createBotSignature(timestamp, userDiscordId, userName, secret = TEST_SIGNING_SECRET) {
|
|
42
|
+
const message = `${timestamp}:${userDiscordId}:${userName}`;
|
|
43
|
+
const encoder = new TextEncoder();
|
|
44
|
+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
45
|
+
const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(message));
|
|
46
|
+
return bytesToHex(new Uint8Array(signature));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Creates a signature with the current timestamp
|
|
50
|
+
*
|
|
51
|
+
* @param userDiscordId - Discord user ID
|
|
52
|
+
* @param userName - Discord username
|
|
53
|
+
* @param secret - Signing secret (defaults to TEST_SIGNING_SECRET)
|
|
54
|
+
* @returns Object with signature and timestamp
|
|
55
|
+
*/
|
|
56
|
+
export async function createTimestampedSignature(userDiscordId, userName, secret = TEST_SIGNING_SECRET) {
|
|
57
|
+
const timestamp = Math.floor(Date.now() / 1000).toString();
|
|
58
|
+
const signature = await createBotSignature(timestamp, userDiscordId, userName, secret);
|
|
59
|
+
return { signature, timestamp };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Verifies an HMAC-SHA256 signature
|
|
63
|
+
*
|
|
64
|
+
* @param signature - The signature to verify (hex string)
|
|
65
|
+
* @param timestamp - Unix timestamp string
|
|
66
|
+
* @param userDiscordId - Discord user ID
|
|
67
|
+
* @param userName - Discord username
|
|
68
|
+
* @param secret - Signing secret
|
|
69
|
+
* @returns True if signature is valid
|
|
70
|
+
*/
|
|
71
|
+
export async function verifyBotSignature(signature, timestamp, userDiscordId, userName, secret = TEST_SIGNING_SECRET) {
|
|
72
|
+
const expected = await createBotSignature(timestamp, userDiscordId, userName, secret);
|
|
73
|
+
return signature === expected;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/auth/signature.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,uCAAuC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,aAAqB,EACrB,QAAgB,EAChB,SAAiB,mBAAmB;IAEpC,MAAM,OAAO,GAAG,GAAG,SAAS,IAAI,aAAa,IAAI,QAAQ,EAAE,CAAC;IAC5D,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAElC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACvC,KAAK,EACL,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EACtB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,EACjC,KAAK,EACL,CAAC,MAAM,CAAC,CACT,CAAC;IAEF,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAEjF,OAAO,UAAU,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,aAAqB,EACrB,QAAgB,EAChB,SAAiB,mBAAmB;IAEpC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACvF,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,SAAiB,EACjB,aAAqB,EACrB,QAAgB,EAChB,SAAiB,mBAAmB;IAEpC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACtF,OAAO,SAAS,KAAK,QAAQ,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Analytics Engine for testing Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* Provides a mock implementation of AnalyticsEngineDataset for testing
|
|
5
|
+
* analytics and metrics tracking.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const analytics = createMockAnalyticsEngine();
|
|
10
|
+
*
|
|
11
|
+
* // Use in tests
|
|
12
|
+
* const env = { ANALYTICS: analytics as unknown as AnalyticsEngineDataset };
|
|
13
|
+
*
|
|
14
|
+
* // After running code that writes analytics
|
|
15
|
+
* expect(analytics._dataPoints).toHaveLength(1);
|
|
16
|
+
* expect(analytics._dataPoints[0].indexes).toContain('command_usage');
|
|
17
|
+
*
|
|
18
|
+
* // Reset between tests
|
|
19
|
+
* analytics._reset();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Analytics data point
|
|
24
|
+
*/
|
|
25
|
+
export interface AnalyticsDataPoint {
|
|
26
|
+
/** String indexes (dimensions) */
|
|
27
|
+
indexes?: string[];
|
|
28
|
+
/** Numeric values (metrics) */
|
|
29
|
+
doubles?: number[];
|
|
30
|
+
/** Binary data */
|
|
31
|
+
blobs?: ArrayBuffer[];
|
|
32
|
+
/** Timestamp of when the data point was written */
|
|
33
|
+
timestamp: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extended mock Analytics Engine with test helpers
|
|
37
|
+
*/
|
|
38
|
+
export interface MockAnalyticsEngine {
|
|
39
|
+
writeDataPoint: (dataPoint?: {
|
|
40
|
+
indexes?: string[];
|
|
41
|
+
doubles?: number[];
|
|
42
|
+
blobs?: ArrayBuffer[];
|
|
43
|
+
}) => void;
|
|
44
|
+
/** Array of all data points written (for assertions) */
|
|
45
|
+
_dataPoints: AnalyticsDataPoint[];
|
|
46
|
+
/** Reset all data points */
|
|
47
|
+
_reset: () => void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates a mock Analytics Engine for testing
|
|
51
|
+
*
|
|
52
|
+
* @returns A mock Analytics Engine that can be cast to AnalyticsEngineDataset
|
|
53
|
+
*/
|
|
54
|
+
export declare function createMockAnalyticsEngine(): MockAnalyticsEngine;
|
|
55
|
+
//# sourceMappingURL=analytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../src/cloudflare/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,kBAAkB;IAClB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,CAAC,SAAS,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAA;KAAE,KAAK,IAAI,CAAC;IAExG,wDAAwD;IACxD,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAElC,4BAA4B;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,mBAAmB,CAmB/D"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Analytics Engine for testing Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* Provides a mock implementation of AnalyticsEngineDataset for testing
|
|
5
|
+
* analytics and metrics tracking.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const analytics = createMockAnalyticsEngine();
|
|
10
|
+
*
|
|
11
|
+
* // Use in tests
|
|
12
|
+
* const env = { ANALYTICS: analytics as unknown as AnalyticsEngineDataset };
|
|
13
|
+
*
|
|
14
|
+
* // After running code that writes analytics
|
|
15
|
+
* expect(analytics._dataPoints).toHaveLength(1);
|
|
16
|
+
* expect(analytics._dataPoints[0].indexes).toContain('command_usage');
|
|
17
|
+
*
|
|
18
|
+
* // Reset between tests
|
|
19
|
+
* analytics._reset();
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Creates a mock Analytics Engine for testing
|
|
24
|
+
*
|
|
25
|
+
* @returns A mock Analytics Engine that can be cast to AnalyticsEngineDataset
|
|
26
|
+
*/
|
|
27
|
+
export function createMockAnalyticsEngine() {
|
|
28
|
+
const dataPoints = [];
|
|
29
|
+
return {
|
|
30
|
+
writeDataPoint: (dataPoint) => {
|
|
31
|
+
dataPoints.push({
|
|
32
|
+
indexes: dataPoint?.indexes,
|
|
33
|
+
doubles: dataPoint?.doubles,
|
|
34
|
+
blobs: dataPoint?.blobs,
|
|
35
|
+
timestamp: Date.now(),
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
_dataPoints: dataPoints,
|
|
39
|
+
_reset: () => {
|
|
40
|
+
dataPoints.length = 0;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/cloudflare/analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA6BH;;;;GAIG;AACH,MAAM,UAAU,yBAAyB;IACvC,MAAM,UAAU,GAAyB,EAAE,CAAC;IAE5C,OAAO;QACL,cAAc,EAAE,CAAC,SAA6E,EAAE,EAAE;YAChG,UAAU,CAAC,IAAI,CAAC;gBACd,OAAO,EAAE,SAAS,EAAE,OAAO;gBAC3B,OAAO,EAAE,SAAS,EAAE,OAAO;gBAC3B,KAAK,EAAE,SAAS,EAAE,KAAK;gBACvB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;aACtB,CAAC,CAAC;QACL,CAAC;QAED,WAAW,EAAE,UAAU;QAEvB,MAAM,EAAE,GAAG,EAAE;YACX,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;QACxB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock D1 Database for testing Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* Provides a mock implementation of D1Database that tracks queries
|
|
5
|
+
* and allows custom mock responses for testing.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const db = createMockD1Database();
|
|
10
|
+
*
|
|
11
|
+
* // Setup mock responses
|
|
12
|
+
* db._setupMock((query, bindings) => {
|
|
13
|
+
* if (query.includes('SELECT') && query.includes('presets')) {
|
|
14
|
+
* return [{ id: 'preset-1', name: 'Test Preset' }];
|
|
15
|
+
* }
|
|
16
|
+
* return null;
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Use in tests
|
|
20
|
+
* const env = { DB: db as unknown as D1Database };
|
|
21
|
+
*
|
|
22
|
+
* // Assert queries were made
|
|
23
|
+
* expect(db._queries).toContain('SELECT * FROM presets');
|
|
24
|
+
* expect(db._bindings[0]).toEqual(['param1']);
|
|
25
|
+
*
|
|
26
|
+
* // Reset between tests
|
|
27
|
+
* db._reset();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Function type for mocking query responses
|
|
32
|
+
*/
|
|
33
|
+
export type QueryMockFn = (query: string, bindings: unknown[]) => unknown;
|
|
34
|
+
/**
|
|
35
|
+
* Mock D1 prepared statement interface
|
|
36
|
+
*/
|
|
37
|
+
export interface MockD1PreparedStatement {
|
|
38
|
+
bind: (...values: unknown[]) => MockD1PreparedStatement;
|
|
39
|
+
first: <T = unknown>() => Promise<T | null>;
|
|
40
|
+
all: <T = unknown>() => Promise<{
|
|
41
|
+
results: T[];
|
|
42
|
+
success: boolean;
|
|
43
|
+
meta: D1Meta;
|
|
44
|
+
}>;
|
|
45
|
+
run: () => Promise<{
|
|
46
|
+
success: boolean;
|
|
47
|
+
meta: {
|
|
48
|
+
changes: number;
|
|
49
|
+
duration: number;
|
|
50
|
+
last_row_id: number;
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
raw: <T = unknown[]>() => Promise<T[]>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* D1 result metadata
|
|
57
|
+
*/
|
|
58
|
+
interface D1Meta {
|
|
59
|
+
duration: number;
|
|
60
|
+
changes: number;
|
|
61
|
+
last_row_id: number;
|
|
62
|
+
rows_read: number;
|
|
63
|
+
rows_written: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Extended mock D1 database with test helpers
|
|
67
|
+
*/
|
|
68
|
+
export interface MockD1Database {
|
|
69
|
+
prepare: (query: string) => MockD1PreparedStatement;
|
|
70
|
+
batch: <T = unknown>(statements: MockD1PreparedStatement[]) => Promise<Array<{
|
|
71
|
+
results: T[];
|
|
72
|
+
success: boolean;
|
|
73
|
+
meta: D1Meta;
|
|
74
|
+
}>>;
|
|
75
|
+
exec: (query: string) => Promise<{
|
|
76
|
+
count: number;
|
|
77
|
+
duration: number;
|
|
78
|
+
}>;
|
|
79
|
+
dump: () => Promise<ArrayBuffer>;
|
|
80
|
+
/** Array of all queries executed (for assertions) */
|
|
81
|
+
_queries: string[];
|
|
82
|
+
/** Array of all binding arrays passed to queries */
|
|
83
|
+
_bindings: unknown[][];
|
|
84
|
+
/** Setup a mock function to return custom responses */
|
|
85
|
+
_setupMock: (fn: QueryMockFn) => void;
|
|
86
|
+
/** Reset queries, bindings, and mock function */
|
|
87
|
+
_reset: () => void;
|
|
88
|
+
/** Current mock function (if any) */
|
|
89
|
+
_mockFn?: QueryMockFn;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Creates a mock D1 database for testing
|
|
93
|
+
*
|
|
94
|
+
* The mock tracks all queries and bindings for assertions, and supports
|
|
95
|
+
* custom response functions for simulating database behavior.
|
|
96
|
+
*
|
|
97
|
+
* @returns A mock D1 database that can be cast to D1Database
|
|
98
|
+
*/
|
|
99
|
+
export declare function createMockD1Database(): MockD1Database;
|
|
100
|
+
export {};
|
|
101
|
+
//# sourceMappingURL=d1.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d1.d.ts","sourceRoot":"","sources":["../../src/cloudflare/d1.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,KAAK,uBAAuB,CAAC;IACxD,KAAK,EAAE,CAAC,CAAC,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC5C,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,OAAO,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClF,GAAG,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAC3G,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;CACxC;AAED;;GAEG;AACH,UAAU,MAAM;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,uBAAuB,CAAC;IACpD,KAAK,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,UAAU,EAAE,uBAAuB,EAAE,KAAK,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAChI,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,IAAI,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;IAEjC,qDAAqD;IACrD,QAAQ,EAAE,MAAM,EAAE,CAAC;IAEnB,oDAAoD;IACpD,SAAS,EAAE,OAAO,EAAE,EAAE,CAAC;IAEvB,uDAAuD;IACvD,UAAU,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,CAAC;IAEtC,iDAAiD;IACjD,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,qCAAqC;IACrC,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,cAAc,CA6HrD"}
|