@bytescale/sdk 3.60.0 → 3.62.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/README.md +91 -0
- package/dist/browser/cjs/main.js +508 -161
- package/dist/browser/esm/main.mjs +508 -161
- package/dist/node/cjs/main.js +351 -97
- package/dist/node/esm/main.mjs +351 -97
- package/dist/types/private/AuthSessionState.d.ts +2 -0
- package/dist/types/private/Scheduler.d.ts +12 -3
- package/dist/types/private/dtos/AuthSwConfigEntryDto.d.ts +3 -1
- package/dist/types/private/model/AuthSession.d.ts +4 -0
- package/dist/types/private/model/AuthSessionConfigBase.d.ts +8 -0
- package/dist/types/public/browser/AuthManagerBrowser.d.ts +3 -0
- package/dist/types/public/shared/generated/runtime.d.ts +6 -0
- package/dist/worker/cjs/main.js +351 -97
- package/dist/worker/esm/main.mjs +351 -97
- package/package.json +1 -1
- package/tests/ApiClientAuth.test.ts +115 -2
- package/tests/AuthManagerBrowser.test.ts +145 -2
- package/tests/AuthServiceWorkerRequestScope.test.ts +259 -0
- package/tests/AuthServiceWorkerRewrite.test.ts +17 -152
- package/tests/Scheduler.test.ts +135 -0
- package/tests/UploadManagerAuth.test.ts +30 -0
- package/tests/fixtures/auth-sw-3.61.0.js +272 -0
- package/tests/utils/AuthServiceWorkerHarness.ts +183 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { jest } from "@jest/globals";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { runInNewContext } from "node:vm";
|
|
5
|
+
import { Headers as NodeFetchHeaders, Request as NodeFetchRequest, Response as NodeFetchResponse } from "node-fetch";
|
|
6
|
+
import type { RequestInit as NodeFetchRequestInit } from "node-fetch";
|
|
7
|
+
import type { AuthSwConfigEntryDto, UrlRewriteRule } from "../../src/index.browser";
|
|
8
|
+
import type { AuthSwSetConfigDto } from "../../src/private/dtos/AuthSwSetConfigDto";
|
|
9
|
+
|
|
10
|
+
const currentWorkerSource = readFileSync(resolve(process.cwd(), "src/index.auth-sw.js"), "utf8");
|
|
11
|
+
|
|
12
|
+
interface FetchOptions {
|
|
13
|
+
clientId?: string;
|
|
14
|
+
headers?: HeadersInit;
|
|
15
|
+
method?: string;
|
|
16
|
+
navigation?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface FetchResult {
|
|
20
|
+
outboundRequest: TestRequest | undefined;
|
|
21
|
+
responded: boolean;
|
|
22
|
+
response: NodeFetchResponse | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type WorkerEventListener = (event: unknown) => void;
|
|
26
|
+
|
|
27
|
+
export class AuthServiceWorkerHarness {
|
|
28
|
+
private readonly clientsById = new Map<string, { type: "window"; url: string }>();
|
|
29
|
+
private cacheReadDelayMilliseconds = 0;
|
|
30
|
+
private readonly context: {
|
|
31
|
+
getRewrittenUrl: (url: string, rules: UrlRewriteRule[]) => string | undefined;
|
|
32
|
+
setConfig: (config: AuthSwConfigEntryDto[], rules?: UrlRewriteRule[]) => Promise<void>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
private readonly messageListener: WorkerEventListener;
|
|
36
|
+
private readonly fetchListener: WorkerEventListener;
|
|
37
|
+
private readonly fetchMock: jest.MockedFunction<(request: TestRequest) => Promise<NodeFetchResponse>>;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
private readonly upstreamResponse: NodeFetchResponse = new NodeFetchResponse("ok"),
|
|
41
|
+
private readonly workerSource: string = currentWorkerSource,
|
|
42
|
+
private readonly cacheEntries = new Map<string, NodeFetchResponse>()
|
|
43
|
+
) {
|
|
44
|
+
const listeners = new Map<string, WorkerEventListener>();
|
|
45
|
+
this.fetchMock = jest.fn(async (_request: TestRequest): Promise<NodeFetchResponse> => this.upstreamResponse);
|
|
46
|
+
|
|
47
|
+
const self = {
|
|
48
|
+
addEventListener: (type: string, listener: WorkerEventListener): void => {
|
|
49
|
+
listeners.set(type, listener);
|
|
50
|
+
},
|
|
51
|
+
clients: {
|
|
52
|
+
claim: async (): Promise<void> => {},
|
|
53
|
+
get: async (clientId: string): Promise<{ type: "window"; url: string } | undefined> =>
|
|
54
|
+
this.clientsById.get(clientId)
|
|
55
|
+
},
|
|
56
|
+
skipWaiting: async (): Promise<void> => {}
|
|
57
|
+
};
|
|
58
|
+
const cache = {
|
|
59
|
+
match: async (key: string): Promise<NodeFetchResponse | undefined> => {
|
|
60
|
+
if (this.cacheReadDelayMilliseconds > 0) {
|
|
61
|
+
await new Promise(resolve => setTimeout(resolve, this.cacheReadDelayMilliseconds));
|
|
62
|
+
}
|
|
63
|
+
return this.cacheEntries.get(key)?.clone();
|
|
64
|
+
},
|
|
65
|
+
put: async (key: string, value: NodeFetchResponse): Promise<void> => {
|
|
66
|
+
this.cacheEntries.set(key, value.clone());
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const sandbox = {
|
|
70
|
+
caches: { open: async (): Promise<typeof cache> => cache },
|
|
71
|
+
console: { error: jest.fn(), log: jest.fn() },
|
|
72
|
+
fetch: this.fetchMock,
|
|
73
|
+
Headers: NodeFetchHeaders,
|
|
74
|
+
Promise,
|
|
75
|
+
Request: TestRequest,
|
|
76
|
+
Response: NodeFetchResponse,
|
|
77
|
+
self,
|
|
78
|
+
setTimeout
|
|
79
|
+
};
|
|
80
|
+
runInNewContext(workerSource, sandbox);
|
|
81
|
+
|
|
82
|
+
this.context = sandbox as typeof sandbox & AuthServiceWorkerHarness["context"];
|
|
83
|
+
const fetchListener = listeners.get("fetch");
|
|
84
|
+
if (fetchListener === undefined) {
|
|
85
|
+
throw new Error("Auth service worker did not register a fetch listener.");
|
|
86
|
+
}
|
|
87
|
+
this.fetchListener = fetchListener;
|
|
88
|
+
const messageListener = listeners.get("message");
|
|
89
|
+
if (messageListener === undefined) {
|
|
90
|
+
throw new Error("Auth service worker did not register a message listener.");
|
|
91
|
+
}
|
|
92
|
+
this.messageListener = messageListener;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async setConfig(config: AuthSwConfigEntryDto[], rules?: UrlRewriteRule[]): Promise<void> {
|
|
96
|
+
await this.context.setConfig(config, rules);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async dispatchMessage(message: AuthSwSetConfigDto): Promise<void> {
|
|
100
|
+
let persistence: Promise<void> | undefined;
|
|
101
|
+
this.messageListener({
|
|
102
|
+
data: message,
|
|
103
|
+
waitUntil: (promise: Promise<void>): void => {
|
|
104
|
+
persistence = promise;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
expect(persistence).toBeDefined();
|
|
108
|
+
await persistence;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
restart(workerSource = this.workerSource): AuthServiceWorkerHarness {
|
|
112
|
+
return new AuthServiceWorkerHarness(this.upstreamResponse, workerSource, this.cacheEntries);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getRewrittenUrl(url: string, rules: UrlRewriteRule[]): string | undefined {
|
|
116
|
+
return this.context.getRewrittenUrl(url, rules);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setWindowClient(clientId: string, url: string): void {
|
|
120
|
+
this.clientsById.set(clientId, { type: "window", url });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setPersistentConfig(config: AuthSwConfigEntryDto[], cacheReadDelayMilliseconds: number): void {
|
|
124
|
+
this.cacheEntries.set("config", new NodeFetchResponse(JSON.stringify(config)));
|
|
125
|
+
this.cacheReadDelayMilliseconds = cacheReadDelayMilliseconds;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async dispatchFetch(url: string, options: FetchOptions = {}): Promise<FetchResult> {
|
|
129
|
+
this.fetchMock.mockClear();
|
|
130
|
+
const request = new TestRequest(url, {
|
|
131
|
+
headers: options.headers as NodeFetchRequestInit["headers"],
|
|
132
|
+
method: options.method
|
|
133
|
+
});
|
|
134
|
+
if (options.navigation === true) {
|
|
135
|
+
Object.defineProperty(request, "mode", { configurable: true, value: "navigate" });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let responsePromise: Promise<NodeFetchResponse> | undefined;
|
|
139
|
+
this.fetchListener({
|
|
140
|
+
clientId: options.clientId ?? "",
|
|
141
|
+
request,
|
|
142
|
+
respondWith: (response: NodeFetchResponse | Promise<NodeFetchResponse>): void => {
|
|
143
|
+
responsePromise = Promise.resolve(response);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const response = await responsePromise;
|
|
148
|
+
expect(this.fetchMock).toHaveBeenCalledTimes(responsePromise === undefined ? 0 : 1);
|
|
149
|
+
return {
|
|
150
|
+
outboundRequest: this.fetchMock.mock.calls.at(-1)?.[0],
|
|
151
|
+
responded: responsePromise !== undefined,
|
|
152
|
+
response
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
class TestRequest extends NodeFetchRequest {
|
|
158
|
+
readonly mode: RequestMode;
|
|
159
|
+
|
|
160
|
+
constructor(input: string | NodeFetchRequest, init: NodeFetchRequestInit & { mode?: RequestMode } = {}) {
|
|
161
|
+
super(input, init);
|
|
162
|
+
this.mode = init.mode ?? (input instanceof TestRequest ? input.mode : "cors");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function authConfig(accountPath: string, token: string): AuthSwConfigEntryDto {
|
|
167
|
+
return authConfigForUrl(`https://upcdn.io/${accountPath}`, token);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function authConfigForUrl(urlPrefix: string, token: string): AuthSwConfigEntryDto {
|
|
171
|
+
return {
|
|
172
|
+
expires: undefined,
|
|
173
|
+
headers: [{ key: "Authorization", value: `Bearer ${token}` }],
|
|
174
|
+
urlPrefix
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function rewriteRule(fromPath: string, toPath: string): UrlRewriteRule {
|
|
179
|
+
return {
|
|
180
|
+
fromUrlPrefix: `https://dashboard.example.com/${fromPath}`,
|
|
181
|
+
toUrlPrefix: `https://upcdn.io/${toPath}`
|
|
182
|
+
};
|
|
183
|
+
}
|