@warp-drive/holodeck 0.0.1 → 0.0.3-beta.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.
- package/README.md +29 -14
- package/declarations/index.d.ts +52 -0
- package/declarations/mock.d.ts +65 -0
- package/dist/index.js +184 -24
- package/dist/mock.js +89 -7
- package/logos/README.md +2 -2
- package/logos/logo-yellow-slab.svg +1 -0
- package/logos/word-mark-black.svg +1 -0
- package/logos/word-mark-white.svg +1 -0
- package/package.json +32 -18
- package/server/bun-worker.js +3 -0
- package/server/bun.js +393 -0
- package/server/compat-shim.js +95 -0
- package/server/ensure-cert.js +4 -0
- package/server/index.js +24 -421
- package/server/node-compat-start.js +12 -0
- package/server/node-worker.js +3 -0
- package/server/node.js +401 -0
- package/server/utils.js +128 -0
- package/dist/index.js.map +0 -1
- package/dist/mock.js.map +0 -1
- package/logos/NCC-1701-a-blue.svg +0 -4
- package/logos/NCC-1701-a-gold.svg +0 -4
- package/logos/NCC-1701-a-gold_100.svg +0 -1
- package/logos/NCC-1701-a-gold_base-64.txt +0 -1
- package/logos/NCC-1701-a.svg +0 -4
- package/logos/docs-badge.svg +0 -2
- package/logos/ember-data-logo-dark.svg +0 -12
- package/logos/ember-data-logo-light.svg +0 -12
- package/logos/social1.png +0 -0
- package/logos/social2.png +0 -0
- package/logos/warp-drive-logo-dark.svg +0 -4
- package/logos/warp-drive-logo-gold.svg +0 -4
- package/server/start-node.js +0 -3
- package/server/worker.js +0 -3
package/README.md
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<img
|
|
3
3
|
class="project-logo"
|
|
4
|
-
src="./logos/
|
|
4
|
+
src="./logos/logo-yellow-slab.svg"
|
|
5
5
|
alt="WarpDrive"
|
|
6
|
-
width="
|
|
7
|
-
title="WarpDrive"
|
|
8
|
-
|
|
9
|
-
class="project-logo"
|
|
10
|
-
src="./logos/NCC-1701-a.svg#gh-dark-mode-only"
|
|
11
|
-
alt="WarpDrive"
|
|
12
|
-
width="120px"
|
|
13
|
-
title="WarpDrive" />
|
|
6
|
+
width="180px"
|
|
7
|
+
title="WarpDrive"
|
|
8
|
+
/>
|
|
14
9
|
</p>
|
|
15
10
|
|
|
11
|
+

|
|
12
|
+

|
|
13
|
+

|
|
14
|
+
[](https://discord.gg/zT3asNS
|
|
15
|
+
)
|
|
16
|
+
[](https://discord.gg/PHBbnWJx5S
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<br>
|
|
21
|
+
<a href="https://warp-drive.io">WarpDrive</a> is the lightweight data library for web apps —
|
|
22
|
+
<br>
|
|
23
|
+
universal, typed, reactive, and ready to scale.
|
|
24
|
+
<br/><br/>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
# @warp-drive/holodeck
|
|
30
|
+
|
|
16
31
|
<h3 align="center">⚡️ Simple, Fast HTTP Mocking</h3>
|
|
17
32
|
<p align="center">Ideal for Test Suites</p>
|
|
18
33
|
|
|
@@ -163,10 +178,10 @@ const MockHost = `https://${window.location.hostname}:${Number(window.location.p
|
|
|
163
178
|
setConfig({ host: MockHost });
|
|
164
179
|
|
|
165
180
|
QUnit.hooks.beforeEach(function (assert) {
|
|
166
|
-
setTestId(assert.test.testId);
|
|
181
|
+
setTestId(this, assert.test.testId);
|
|
167
182
|
});
|
|
168
183
|
QUnit.hooks.afterEach(function (assert) {
|
|
169
|
-
setTestId(null);
|
|
184
|
+
setTestId(this, null);
|
|
170
185
|
});
|
|
171
186
|
```
|
|
172
187
|
|
|
@@ -225,7 +240,7 @@ holodeck can be launched and cleaned up using the lifecycle hooks in the launch
|
|
|
225
240
|
for diagnostic in `diagnostic.js`:
|
|
226
241
|
|
|
227
242
|
```ts
|
|
228
|
-
import launch from '@warp-drive/diagnostic/server
|
|
243
|
+
import { launch } from '@warp-drive/diagnostic/server';
|
|
229
244
|
import holodeck from '@warp-drive/holodeck';
|
|
230
245
|
|
|
231
246
|
await launch({
|
|
@@ -249,7 +264,7 @@ await launch({
|
|
|
249
264
|
img.project-logo {
|
|
250
265
|
padding: 0 5em 1em 5em;
|
|
251
266
|
width: 100px;
|
|
252
|
-
border-bottom: 2px solid #
|
|
267
|
+
border-bottom: 2px solid #bbb;
|
|
253
268
|
margin: 0 auto;
|
|
254
269
|
display: block;
|
|
255
270
|
}
|
|
@@ -265,7 +280,7 @@ await launch({
|
|
|
265
280
|
display: inline-block;
|
|
266
281
|
padding: .2rem 0;
|
|
267
282
|
color: #000;
|
|
268
|
-
border-bottom: 3px solid #
|
|
283
|
+
border-bottom: 3px solid #bbb;
|
|
269
284
|
}
|
|
270
285
|
|
|
271
286
|
details > details {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Handler, NextFn } from "@warp-drive/core/request";
|
|
2
|
+
import type { RequestContext, StructuredDataDocument } from "@warp-drive/core/types/request";
|
|
3
|
+
import type { Store } from "@warp-drive/legacy/store";
|
|
4
|
+
import type { ScaffoldGenerator } from "./mock.js";
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export declare function setConfig({ host }: {
|
|
9
|
+
host: string;
|
|
10
|
+
}): void;
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export declare function setTestId(context: object, str: string | null): void;
|
|
15
|
+
/**
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
export declare function setIsRecording(value: boolean): void;
|
|
19
|
+
/**
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
export declare function getIsRecording(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* A request handler that intercepts requests and routes them through
|
|
25
|
+
* the Holodeck mock server.
|
|
26
|
+
*
|
|
27
|
+
* This handler modifies the request URL to include test identifiers
|
|
28
|
+
* and manages request counts for accurate mocking.
|
|
29
|
+
*
|
|
30
|
+
* Requires that the test context be configured with a testId using `setTestId`.
|
|
31
|
+
*
|
|
32
|
+
* @param owner - the test context object used to retrieve the test ID.
|
|
33
|
+
*/
|
|
34
|
+
export declare class MockServerHandler implements Handler {
|
|
35
|
+
owner: object;
|
|
36
|
+
constructor(owner: object);
|
|
37
|
+
request<T>(context: RequestContext, next: NextFn<T>): Promise<StructuredDataDocument<T>>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Creates an adapterFor function that wraps the provided adapterFor function
|
|
41
|
+
* to override the adapter's _fetchRequest method to route requests through
|
|
42
|
+
* the Holodeck mock server.
|
|
43
|
+
*
|
|
44
|
+
* @param owner - The test context object used to retrieve the test ID.
|
|
45
|
+
*/
|
|
46
|
+
export declare function installAdapterFor(owner: object, store: Store): void;
|
|
47
|
+
/**
|
|
48
|
+
* Mock a request by sending the scaffold to the mock server.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
export declare function mock(owner: object, generate: ScaffoldGenerator, isRecording?: boolean): Promise<void>;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @public
|
|
3
|
+
*/
|
|
4
|
+
export interface Scaffold {
|
|
5
|
+
status: number;
|
|
6
|
+
statusText?: string;
|
|
7
|
+
headers: Record<string, string>;
|
|
8
|
+
body: Record<string, string> | string | null;
|
|
9
|
+
method: string;
|
|
10
|
+
url: string;
|
|
11
|
+
response: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export type ScaffoldGenerator = () => Scaffold;
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type ResponseGenerator = () => Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Sets up Mocking for a GET request on the mock server
|
|
23
|
+
* for the supplied url.
|
|
24
|
+
*
|
|
25
|
+
* The response body is generated by the supplied response function.
|
|
26
|
+
*
|
|
27
|
+
* Available options:
|
|
28
|
+
* - status: the status code to return (default: 200)
|
|
29
|
+
* - headers: the headers to return (default: {})
|
|
30
|
+
* - body: the body to match against for the request (default: null)
|
|
31
|
+
* - RECORD: whether to record the request (default: false)
|
|
32
|
+
*
|
|
33
|
+
* @param url the url to mock, relative to the mock server host (e.g. `users/1`)
|
|
34
|
+
* @param response a function which generates the response to return
|
|
35
|
+
* @param options status, headers for the response, body to match against for the request, and whether to record the request
|
|
36
|
+
* @return
|
|
37
|
+
*/
|
|
38
|
+
export declare function GET(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
39
|
+
RECORD?: boolean;
|
|
40
|
+
}): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Mock a POST request
|
|
43
|
+
*/
|
|
44
|
+
export declare function POST(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
45
|
+
RECORD?: boolean;
|
|
46
|
+
}): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* mock a PUT request
|
|
49
|
+
*/
|
|
50
|
+
export declare function PUT(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
51
|
+
RECORD?: boolean;
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* mock a PATCH request
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
57
|
+
export declare function PATCH(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
58
|
+
RECORD?: boolean;
|
|
59
|
+
}): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* mock a DELETE request
|
|
62
|
+
*/
|
|
63
|
+
export declare function DELETE(owner: object, url: string, response: ResponseGenerator, options?: Partial<Omit<Scaffold, "response" | "url" | "method">> & {
|
|
64
|
+
RECORD?: boolean;
|
|
65
|
+
}): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
|
+
import { SHOULD_RECORD } from '@warp-drive/core/build-config/env';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module
|
|
5
|
+
* @mergeModuleWith <project>
|
|
6
|
+
*/
|
|
1
7
|
const TEST_IDS = new WeakMap();
|
|
2
|
-
let HOST = '
|
|
8
|
+
let HOST = '/';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
|
|
3
14
|
function setConfig({
|
|
4
15
|
host
|
|
5
16
|
}) {
|
|
6
17
|
HOST = host.endsWith('/') ? host : `${host}/`;
|
|
7
18
|
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @public
|
|
22
|
+
*/
|
|
23
|
+
|
|
8
24
|
function setTestId(context, str) {
|
|
9
25
|
if (str && TEST_IDS.has(context)) {
|
|
10
26
|
throw new Error(`MockServerHandler is already configured with a testId.`);
|
|
@@ -12,37 +28,72 @@ function setTestId(context, str) {
|
|
|
12
28
|
if (str) {
|
|
13
29
|
TEST_IDS.set(context, {
|
|
14
30
|
id: str,
|
|
15
|
-
|
|
16
|
-
|
|
31
|
+
mock: {
|
|
32
|
+
GET: {},
|
|
33
|
+
PUT: {},
|
|
34
|
+
PATCH: {},
|
|
35
|
+
DELETE: {},
|
|
36
|
+
POST: {},
|
|
37
|
+
QUERY: {},
|
|
38
|
+
OPTIONS: {},
|
|
39
|
+
HEAD: {},
|
|
40
|
+
CONNECT: {},
|
|
41
|
+
TRACE: {}
|
|
42
|
+
},
|
|
43
|
+
request: {
|
|
44
|
+
GET: {},
|
|
45
|
+
PUT: {},
|
|
46
|
+
PATCH: {},
|
|
47
|
+
DELETE: {},
|
|
48
|
+
POST: {},
|
|
49
|
+
QUERY: {},
|
|
50
|
+
OPTIONS: {},
|
|
51
|
+
HEAD: {},
|
|
52
|
+
CONNECT: {},
|
|
53
|
+
TRACE: {}
|
|
54
|
+
}
|
|
17
55
|
});
|
|
18
56
|
} else {
|
|
19
57
|
TEST_IDS.delete(context);
|
|
20
58
|
}
|
|
21
59
|
}
|
|
22
|
-
|
|
60
|
+
const shouldRecord = SHOULD_RECORD ? true : false;
|
|
61
|
+
let IS_RECORDING = null;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
23
66
|
function setIsRecording(value) {
|
|
24
|
-
IS_RECORDING = Boolean(value);
|
|
67
|
+
IS_RECORDING = value === null ? value : Boolean(value);
|
|
25
68
|
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
26
73
|
function getIsRecording() {
|
|
27
|
-
return IS_RECORDING;
|
|
74
|
+
return IS_RECORDING === null ? shouldRecord : IS_RECORDING;
|
|
28
75
|
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A request handler that intercepts requests and routes them through
|
|
79
|
+
* the Holodeck mock server.
|
|
80
|
+
*
|
|
81
|
+
* This handler modifies the request URL to include test identifiers
|
|
82
|
+
* and manages request counts for accurate mocking.
|
|
83
|
+
*
|
|
84
|
+
* Requires that the test context be configured with a testId using `setTestId`.
|
|
85
|
+
*
|
|
86
|
+
* @param owner - the test context object used to retrieve the test ID.
|
|
87
|
+
*/
|
|
29
88
|
class MockServerHandler {
|
|
30
89
|
constructor(owner) {
|
|
31
90
|
this.owner = owner;
|
|
32
91
|
}
|
|
33
92
|
async request(context, next) {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
const request = Object.assign({}, context.request);
|
|
39
|
-
const isRecording = request.url.endsWith('/__record');
|
|
40
|
-
const firstChar = request.url.includes('?') ? '&' : '?';
|
|
41
|
-
const queryForTest = `${firstChar}__xTestId=${test.id}&__xTestRequestNumber=${isRecording ? test.mock++ : test.request++}`;
|
|
42
|
-
request.url = request.url + queryForTest;
|
|
43
|
-
request.mode = 'cors';
|
|
44
|
-
request.credentials = 'omit';
|
|
45
|
-
request.referrerPolicy = '';
|
|
93
|
+
const {
|
|
94
|
+
request,
|
|
95
|
+
queryForTest
|
|
96
|
+
} = setupHolodeckFetch(this.owner, Object.assign({}, context.request));
|
|
46
97
|
try {
|
|
47
98
|
const future = next(request);
|
|
48
99
|
context.setStream(future.getStream());
|
|
@@ -55,17 +106,127 @@ class MockServerHandler {
|
|
|
55
106
|
}
|
|
56
107
|
}
|
|
57
108
|
}
|
|
58
|
-
|
|
109
|
+
function setupHolodeckFetch(owner, request) {
|
|
59
110
|
const test = TEST_IDS.get(owner);
|
|
60
111
|
if (!test) {
|
|
61
|
-
throw new Error(`
|
|
112
|
+
throw new Error(`MockServerHandler is not configured with a testId. Use setTestId to set the testId for each test`);
|
|
113
|
+
}
|
|
114
|
+
const url = request.url;
|
|
115
|
+
const firstChar = url.includes('?') ? '&' : '?';
|
|
116
|
+
const method = request.method?.toUpperCase() ?? 'GET';
|
|
117
|
+
|
|
118
|
+
// enable custom methods
|
|
119
|
+
if (!test.request[method]) {
|
|
120
|
+
// eslint-disable-next-line no-console
|
|
121
|
+
console.log(`⚠️ Using custom HTTP method ${method} for response to request ${url}`);
|
|
122
|
+
test.request[method] = {};
|
|
123
|
+
}
|
|
124
|
+
if (!(url in test.request[method])) {
|
|
125
|
+
test.request[method][url] = 0;
|
|
126
|
+
}
|
|
127
|
+
const queryForTest = `${firstChar}__xTestId=${test.id}&__xTestRequestNumber=${test.request[method][url]++}`;
|
|
128
|
+
request.url = url + queryForTest;
|
|
129
|
+
request.method = method;
|
|
130
|
+
request.mode = 'cors';
|
|
131
|
+
request.credentials = 'omit';
|
|
132
|
+
request.referrerPolicy = '';
|
|
133
|
+
|
|
134
|
+
// since holodeck currently runs on a separate port
|
|
135
|
+
// and we don't want to trigger cors pre-flight
|
|
136
|
+
// we convert PUT to POST to keep the request in the
|
|
137
|
+
// "simple" cors category.
|
|
138
|
+
// if (request.method === 'PUT') {
|
|
139
|
+
// request.method = 'POST';
|
|
140
|
+
// }
|
|
141
|
+
|
|
142
|
+
const headers = new Headers(request.headers);
|
|
143
|
+
if (headers.has('Content-Type')) {
|
|
144
|
+
// under the rules of simple-cors, content-type can only be
|
|
145
|
+
// one of three things, none of which are what folks typically
|
|
146
|
+
// set this to. Since holodeck always expects body to be JSON
|
|
147
|
+
// this "just works".
|
|
148
|
+
headers.set('Content-Type', 'text/plain');
|
|
149
|
+
request.headers = headers;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
request,
|
|
153
|
+
queryForTest
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function upgradeStore(store) {
|
|
157
|
+
if (typeof store.adapterFor !== 'function') {
|
|
158
|
+
throw new Error('Store is not compatible with Holodeck. Missing adapterFor method.');
|
|
62
159
|
}
|
|
63
|
-
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Creates an adapterFor function that wraps the provided adapterFor function
|
|
164
|
+
* to override the adapter's _fetchRequest method to route requests through
|
|
165
|
+
* the Holodeck mock server.
|
|
166
|
+
*
|
|
167
|
+
* @param owner - The test context object used to retrieve the test ID.
|
|
168
|
+
*/
|
|
169
|
+
function installAdapterFor(owner, store) {
|
|
170
|
+
upgradeStore(store);
|
|
171
|
+
const fn = store.adapterFor;
|
|
172
|
+
function holodeckAdapterFor(modelName, _allowMissing) {
|
|
173
|
+
const adapter = fn.call(this, modelName, _allowMissing);
|
|
174
|
+
if (adapter) {
|
|
175
|
+
if (!adapter.hasOverriddenFetch) {
|
|
176
|
+
adapter.hasOverriddenFetch = true;
|
|
177
|
+
adapter.useFetch = true;
|
|
178
|
+
const originalFetch = adapter._fetchRequest?.bind(adapter);
|
|
179
|
+
adapter._fetchRequest = function (options) {
|
|
180
|
+
if (!originalFetch) {
|
|
181
|
+
throw new Error(`Adapter ${String(modelName)} does not implement _fetchRequest`);
|
|
182
|
+
}
|
|
183
|
+
const {
|
|
184
|
+
request
|
|
185
|
+
} = setupHolodeckFetch(owner, options);
|
|
186
|
+
return originalFetch(request);
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return adapter;
|
|
191
|
+
}
|
|
192
|
+
store.adapterFor = holodeckAdapterFor;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Mock a request by sending the scaffold to the mock server.
|
|
197
|
+
*
|
|
198
|
+
* @public
|
|
199
|
+
*/
|
|
200
|
+
async function mock(owner, generate, isRecording) {
|
|
64
201
|
if (getIsRecording() || isRecording) {
|
|
202
|
+
const test = TEST_IDS.get(owner);
|
|
203
|
+
if (!test) {
|
|
204
|
+
throw new Error(`Cannot call "mock" before configuring a testId. Use setTestId to set the testId for each test`);
|
|
205
|
+
}
|
|
206
|
+
const requestToMock = generate();
|
|
207
|
+
const {
|
|
208
|
+
url: mockUrl,
|
|
209
|
+
method
|
|
210
|
+
} = requestToMock;
|
|
211
|
+
if (!mockUrl || !method) {
|
|
212
|
+
throw new Error(`MockError: Cannot mock a request without providing a URL and Method`);
|
|
213
|
+
}
|
|
214
|
+
const mockMethod = method?.toUpperCase() ?? 'GET';
|
|
215
|
+
|
|
216
|
+
// enable custom methods
|
|
217
|
+
if (!test.mock[mockMethod]) {
|
|
218
|
+
// eslint-disable-next-line no-console
|
|
219
|
+
console.log(`⚠️ Using custom HTTP method ${mockMethod} for response to request ${mockUrl}`);
|
|
220
|
+
test.mock[mockMethod] = {};
|
|
221
|
+
}
|
|
222
|
+
if (!(mockUrl in test.mock[mockMethod])) {
|
|
223
|
+
test.mock[mockMethod][mockUrl] = 0;
|
|
224
|
+
}
|
|
225
|
+
const testMockNum = test.mock[mockMethod][mockUrl]++;
|
|
65
226
|
const url = `${HOST}__record?__xTestId=${test.id}&__xTestRequestNumber=${testMockNum}`;
|
|
66
227
|
await fetch(url, {
|
|
67
228
|
method: 'POST',
|
|
68
|
-
body: JSON.stringify(
|
|
229
|
+
body: JSON.stringify(requestToMock),
|
|
69
230
|
mode: 'cors',
|
|
70
231
|
credentials: 'omit',
|
|
71
232
|
referrerPolicy: ''
|
|
@@ -73,5 +234,4 @@ async function mock(owner, generate, isRecording) {
|
|
|
73
234
|
}
|
|
74
235
|
}
|
|
75
236
|
|
|
76
|
-
export { MockServerHandler, getIsRecording, mock, setConfig, setIsRecording, setTestId };
|
|
77
|
-
//# sourceMappingURL=index.js.map
|
|
237
|
+
export { MockServerHandler, getIsRecording, installAdapterFor, mock, setConfig, setIsRecording, setTestId };
|
package/dist/mock.js
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { mock, getIsRecording } from './index.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
|
|
3
15
|
/**
|
|
4
16
|
* Sets up Mocking for a GET request on the mock server
|
|
5
17
|
* for the supplied url.
|
|
@@ -28,11 +40,81 @@ function GET(owner, url, response, options) {
|
|
|
28
40
|
response: response()
|
|
29
41
|
}), getIsRecording() || (options?.RECORD ?? false));
|
|
30
42
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
const STATUS_TEXT_FOR = new Map([[200, 'OK'], [201, 'Created'], [202, 'Accepted'], [203, 'Non-Authoritative Information'], [204, 'No Content'], [205, 'Reset Content'], [206, 'Partial Content'], [207, 'Multi-Status'], [208, 'Already Reported'], [226, 'IM Used'], [300, 'Multiple Choices'], [301, 'Moved Permanently'], [302, 'Found'], [303, 'See Other'], [304, 'Not Modified'], [307, 'Temporary Redirect'], [308, 'Permanent Redirect'], [400, 'Bad Request'], [401, 'Unauthorized'], [402, 'Payment Required'], [403, 'Forbidden'], [404, 'Not Found'], [405, 'Method Not Allowed'], [406, 'Not Acceptable'], [407, 'Proxy Authentication Required'], [408, 'Request Timeout'], [409, 'Conflict'], [410, 'Gone'], [411, 'Length Required'], [412, 'Precondition Failed'], [413, 'Payload Too Large'], [414, 'URI Too Long'], [415, 'Unsupported Media Type'], [416, 'Range Not Satisfiable'], [417, 'Expectation Failed'], [419, 'Page Expired'], [420, 'Enhance Your Calm'], [421, 'Misdirected Request'], [422, 'Unprocessable Entity'], [423, 'Locked'], [424, 'Failed Dependency'], [425, 'Too Early'], [426, 'Upgrade Required'], [428, 'Precondition Required'], [429, 'Too Many Requests'], [430, 'Request Header Fields Too Large'], [431, 'Request Header Fields Too Large'], [450, 'Blocked By Windows Parental Controls'], [451, 'Unavailable For Legal Reasons'], [500, 'Internal Server Error'], [501, 'Not Implemented'], [502, 'Bad Gateway'], [503, 'Service Unavailable'], [504, 'Gateway Timeout'], [505, 'HTTP Version Not Supported'], [506, 'Variant Also Negotiates'], [507, 'Insufficient Storage'], [508, 'Loop Detected'], [509, 'Bandwidth Limit Exceeded'], [510, 'Not Extended'], [511, 'Network Authentication Required']]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Mock a POST request
|
|
47
|
+
*/
|
|
48
|
+
function POST(owner, url, response, options) {
|
|
49
|
+
return mock(owner, () => {
|
|
50
|
+
const body = response();
|
|
51
|
+
const status = options?.status ?? (body ? 201 : 204);
|
|
52
|
+
return {
|
|
53
|
+
status: status,
|
|
54
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
55
|
+
headers: options?.headers ?? {},
|
|
56
|
+
body: options?.body ?? null,
|
|
57
|
+
method: 'POST',
|
|
58
|
+
url,
|
|
59
|
+
response: body
|
|
60
|
+
};
|
|
61
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* mock a PUT request
|
|
66
|
+
*/
|
|
67
|
+
function PUT(owner, url, response, options) {
|
|
68
|
+
return mock(owner, () => {
|
|
69
|
+
const body = response();
|
|
70
|
+
const status = options?.status ?? (body ? 200 : 204);
|
|
71
|
+
return {
|
|
72
|
+
status: status,
|
|
73
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
74
|
+
headers: options?.headers ?? {},
|
|
75
|
+
body: options?.body ?? null,
|
|
76
|
+
method: 'PUT',
|
|
77
|
+
url,
|
|
78
|
+
response: body
|
|
79
|
+
};
|
|
80
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* mock a PATCH request
|
|
84
|
+
*
|
|
85
|
+
*/
|
|
86
|
+
function PATCH(owner, url, response, options) {
|
|
87
|
+
return mock(owner, () => {
|
|
88
|
+
const body = response();
|
|
89
|
+
const status = options?.status ?? (body ? 200 : 204);
|
|
90
|
+
return {
|
|
91
|
+
status: status,
|
|
92
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
93
|
+
headers: options?.headers ?? {},
|
|
94
|
+
body: options?.body ?? null,
|
|
95
|
+
method: 'PATCH',
|
|
96
|
+
url,
|
|
97
|
+
response: body
|
|
98
|
+
};
|
|
99
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* mock a DELETE request
|
|
103
|
+
*/
|
|
104
|
+
function DELETE(owner, url, response, options) {
|
|
105
|
+
return mock(owner, () => {
|
|
106
|
+
const body = response();
|
|
107
|
+
const status = options?.status ?? (body ? 200 : 204);
|
|
108
|
+
return {
|
|
109
|
+
status: status,
|
|
110
|
+
statusText: options?.statusText ?? STATUS_TEXT_FOR.get(status) ?? '',
|
|
111
|
+
headers: options?.headers ?? {},
|
|
112
|
+
body: options?.body ?? null,
|
|
113
|
+
method: 'DELETE',
|
|
114
|
+
url,
|
|
115
|
+
response: body
|
|
116
|
+
};
|
|
117
|
+
}, getIsRecording() || (options?.RECORD ?? false));
|
|
118
|
+
}
|
|
36
119
|
|
|
37
|
-
export { DELETE, GET, PATCH, POST, PUT
|
|
38
|
-
//# sourceMappingURL=mock.js.map
|
|
120
|
+
export { DELETE, GET, PATCH, POST, PUT };
|
package/logos/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
# Autogeneration Notice
|
|
2
2
|
|
|
3
|
-
This directory is maintained in the root of the monorepo and sync'd
|
|
4
|
-
repositories by running `bun sync-logos`
|
|
3
|
+
This directory is maintained in the root of the monorepo in `/logos/synced` and sync'd
|
|
4
|
+
to public repositories by running `bun sync-logos`
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1498" height="1047" fill="none" viewBox="0 0 1498 1047"><g filter="url(#a)"><path fill="url(#b)" d="M132 175.075C132 149.076 153.138 128 179.213 128H1318.23c26.08 0 47.22 21.076 47.22 47.075v688.85c0 25.999-21.14 47.075-47.22 47.075H179.213C153.138 911 132 889.924 132 863.925z"/><path fill="#201328" d="M1043.4 587.312c0-39.962 30.84-72.357 68.87-72.357h53.09c38.04 0 68.88-32.395 68.88-72.357v-92.651c0-39.962-30.84-72.358-68.88-72.358h-53.09c-38.01 0-68.82 32.351-68.87 72.275v92.734c0 39.962-30.83 72.357-68.869 72.357h-53.089c-38.036 0-68.871-32.395-68.871-72.357v-87.409c0-39.962-30.835-72.358-68.872-72.358h-51.654c-38.037 0-68.872 32.396-68.872 72.358v87.409c0 39.962-30.835 72.357-68.872 72.357h-53.088c-38.037 0-68.872-32.395-68.872-72.357v-92.652c0-39.961-30.835-72.357-68.872-72.357h-53.088c-38.037 0-68.872 32.396-68.872 72.357v92.652c0 39.962 30.835 72.357 68.872 72.357h53.088c38.037 0 68.872 32.395 68.872 72.357v101.741c0 39.962 30.835 72.358 68.872 72.358h53.088c38.037 0 68.872-32.396 68.872-72.358V587.312c0-39.962 30.835-72.357 68.872-72.357h51.654c38.037 0 68.872 32.395 68.872 72.357v101.741c0 39.962 30.835 72.358 68.871 72.358h53.089c38.039 0 68.869-32.396 68.869-72.358z"/></g><defs><linearGradient id="b" x1="748.724" x2="1167.5" y1="128" y2="1070.23" gradientUnits="userSpaceOnUse"><stop stop-color="#ffc474"/><stop offset="1" stop-color="#ff9809"/></linearGradient><filter id="a" width="1497.45" height="1047" x="0" y="0" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="66"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/><feBlend in2="BackgroundImageFix" result="effect1_dropShadow_757_188"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="18"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="effect1_dropShadow_757_188" result="effect2_dropShadow_757_188"/><feColorMatrix in="SourceAlpha" result="hardAlpha" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/><feOffset dy="4"/><feGaussianBlur stdDeviation="2"/><feComposite in2="hardAlpha" operator="out"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/><feBlend in2="effect2_dropShadow_757_188" result="effect3_dropShadow_757_188"/><feBlend in="SourceGraphic" in2="effect3_dropShadow_757_188" result="shape"/></filter></defs></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="2081" height="200" fill="none" viewBox="0 0 2081 200"><path fill="#000" d="M2064.89 185.359c-15.57 5.16-31.15 8.846-46.72 11.058q-23.22 3.456-47.55 3.456c-20.73 0-39.21-2.212-55.43-6.635q-24.18-6.773-40.77-19.49c-11.06-8.478-19.49-18.844-25.3-31.1-5.8-12.256-8.71-26.125-8.71-41.606 0-14.375 2.91-27.69 8.71-39.947 5.9-12.348 14.19-23.037 24.88-32.068 10.78-9.123 23.78-16.218 38.98-21.286C1928.19 2.58 1945.14 0 1963.85 0c17.23 0 32.99 2.35 47.27 7.05 14.38 4.607 26.68 11.472 36.91 20.595q15.48 13.684 23.91 33.727c5.71 13.361 8.57 28.75 8.57 46.167v12.716h-187.16a48.3 48.3 0 0 0 8.16 16.449c3.87 4.976 9.08 9.215 15.62 12.717 6.54 3.501 14.56 6.22 24.05 8.155 9.58 1.935 21.01 2.903 34.28 2.903 9.4 0 18.61-.553 27.64-1.659 9.04-1.198 17.47-2.719 25.3-4.562q11.745-2.902 21.15-6.358c6.36-2.304 11.47-4.607 15.34-6.911zm-35.38-102.7c-.47-4.7-1.89-9.538-4.29-14.514-2.3-5.069-5.99-9.63-11.06-13.685s-11.7-7.371-19.9-9.952q-12.3-4.008-30.69-4.008-17.28 0-29.85 4.285-12.585 4.284-21.15 10.643c-5.62 4.239-10 8.8-13.13 13.684-3.14 4.884-5.16 9.4-6.09 13.546zm-284.63 112.099h-59.71l-96.06-189.92h57.22l69.11 142.924 68.98-142.924h57.22zm-238.29 0V4.838h51.28v189.92zm-32.63-146.932c-1.38-.461-3.4-1.06-6.08-1.797q-3.87-1.245-9.12-2.35-5.25-1.244-11.61-2.073c-4.15-.553-8.43-.83-12.86-.83-9.21 0-17.83 1.152-25.84 3.456q-11.895 3.317-22.26 8.984a125.3 125.3 0 0 0-19.35 12.717c-5.9 4.7-11.33 9.537-16.31 14.514v114.311h-51.28V4.838h51.28v30.824c6.17-4.608 12.49-9.03 18.94-13.27a163 163 0 0 1 20.32-11.472c7.09-3.318 14.56-5.944 22.39-7.88 7.83-2.026 16.12-3.04 24.88-3.04 3.32 0 6.68.184 10.09.553 3.5.276 6.86.645 10.09 1.106 3.32.46 6.4.967 9.26 1.52s5.35 1.106 7.46 1.659zM1263.71 99.66c0 12.164-1.33 22.991-4 32.483-2.68 9.399-6.45 17.6-11.34 24.604-4.79 7.003-10.6 12.947-17.42 17.831q-10.08 7.325-22.53 11.749c-8.29 2.948-17.23 5.114-26.81 6.496q-14.37 1.935-30 1.935h-120.53V4.838h120.26c10.41 0 20.41.691 29.99 2.073 9.59 1.29 18.52 3.41 26.82 6.359 8.38 2.948 15.99 6.865 22.8 11.749 6.82 4.791 12.63 10.735 17.42 17.83 4.89 7.004 8.66 15.205 11.34 24.605 2.67 9.399 4 20.134 4 32.206m-51.69 0q0-13.685-3.6-23.775c-2.3-6.727-6.08-12.256-11.33-16.587-5.16-4.423-11.93-7.694-20.32-9.814-8.38-2.211-18.61-3.317-30.68-3.317h-63.73v107.262h63.73c12.07 0 22.3-1.06 30.68-3.179 8.39-2.212 15.16-5.529 20.32-9.952 5.25-4.515 9.03-10.137 11.33-16.864q3.6-10.09 3.6-23.774M986.574 63.169q0 14.237-4.699 25.157-4.562 10.92-14.514 18.383-9.952 7.464-25.71 11.335-15.62 3.732-37.735 3.732h-85.285v72.982h-17.278V4.838h102.563q22.116 0 37.735 3.87 15.758 3.732 25.71 11.058t14.514 18.246q4.7 10.92 4.699 25.157m-17.831 0q0-13.546-4.561-21.84-4.423-8.294-13.823-12.716-9.26-4.562-23.636-6.082-14.237-1.52-33.865-1.52h-74.227v84.593h74.227q7.602 0 16.172.138 8.709 0 17.14-.83 8.432-.967 16.034-3.179 7.74-2.349 13.546-7.05 5.945-4.699 9.399-12.301 3.594-7.602 3.594-19.213M766.455 19.49q-4.01-1.244-11.197-2.627-7.049-1.52-18.245-1.52-15.481 0-29.166 3.87-13.545 3.732-25.295 10.229a126.3 126.3 0 0 0-21.563 15.205q-9.951 8.57-17.969 18.245v131.866h-17.278V4.838h17.278V43.54a166 166 0 0 1 20.043-17.555q10.92-8.017 22.945-13.684a125.8 125.8 0 0 1 25.157-8.985Q724.435 0 738.533 0q4.839 0 8.708.276 3.87.14 7.05.553 3.317.277 6.22.691 2.902.416 5.944.968zm-220.33 175.268v-21.01q-9.952 5.39-22.669 9.952-12.579 4.562-26.677 7.879-13.96 3.318-28.751 5.114-14.79 1.797-29.027 1.797-18.522 0-33.312-3.317-14.652-3.318-24.881-9.814-10.228-6.635-15.757-16.31-5.529-9.814-5.529-22.669 0-12.717 6.358-22.393 6.497-9.813 18.384-17.001 11.887-7.326 28.612-12.302 16.864-5.115 37.598-8.57 20.733-3.594 44.784-5.806 24.19-2.349 50.867-3.87V61.925q0-8.846-3.318-15.62-3.317-6.772-9.261-11.749-5.943-5.115-13.96-8.431-8.017-3.456-17.416-5.53-9.262-2.074-19.628-2.902a219 219 0 0 0-20.596-.968q-13.96 0-25.433 1.383-11.473 1.381-21.425 3.87-9.952 2.35-19.075 5.529a563 563 0 0 0-18.384 6.773V14.099q19.213-5.114 40.915-8.846 21.839-3.87 46.305-3.87 20.733 0 38.703 3.593 17.969 3.456 31.238 11.196 13.27 7.74 20.872 20.043 7.603 12.164 7.603 29.718v128.825zm0-102.977q-45.2 2.489-77.268 7.05-31.929 4.561-52.248 11.196-20.32 6.634-29.857 15.481-9.399 8.846-9.399 20.042 0 9.123 4.561 16.173 4.7 7.049 13.408 11.887 8.708 4.7 21.148 7.188 12.44 2.349 27.922 2.349 9.123 0 18.798-.967a276 276 0 0 0 19.49-2.903 319 319 0 0 0 19.075-4.423q9.4-2.627 17.693-5.667t15.066-6.497q6.912-3.455 11.611-7.049zM180.245 4.838l67.73 176.098L318.193 4.838h19.213l-76.438 189.92h-25.71L168.634 23.222l-66.486 171.536h-25.71L0 4.838h19.213l70.218 176.098L157.023 4.838z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="2081" height="200" fill="none" viewBox="0 0 2081 200"><path fill="#fff" d="M2064.89 185.359c-15.57 5.16-31.15 8.846-46.72 11.058q-23.22 3.456-47.55 3.456c-20.73 0-39.21-2.212-55.43-6.635q-24.18-6.773-40.77-19.49c-11.06-8.478-19.49-18.844-25.3-31.1-5.8-12.256-8.71-26.125-8.71-41.606 0-14.375 2.91-27.69 8.71-39.947 5.9-12.348 14.19-23.037 24.88-32.068 10.78-9.123 23.78-16.218 38.98-21.286C1928.19 2.58 1945.14 0 1963.85 0c17.23 0 32.99 2.35 47.27 7.05 14.38 4.607 26.68 11.472 36.91 20.595q15.48 13.684 23.91 33.727c5.71 13.361 8.57 28.75 8.57 46.167v12.716h-187.16a48.3 48.3 0 0 0 8.16 16.449c3.87 4.976 9.08 9.215 15.62 12.717 6.54 3.501 14.56 6.22 24.05 8.155 9.58 1.935 21.01 2.903 34.28 2.903 9.4 0 18.61-.553 27.64-1.659 9.04-1.198 17.47-2.719 25.3-4.562q11.745-2.902 21.15-6.358c6.36-2.304 11.47-4.607 15.34-6.911zm-35.38-102.7c-.47-4.7-1.89-9.538-4.29-14.514-2.3-5.069-5.99-9.63-11.06-13.685s-11.7-7.371-19.9-9.952q-12.3-4.008-30.69-4.008-17.28 0-29.85 4.285-12.585 4.284-21.15 10.643c-5.62 4.239-10 8.8-13.13 13.684-3.14 4.884-5.16 9.4-6.09 13.546zm-284.63 112.099h-59.71l-96.06-189.92h57.22l69.11 142.924 68.98-142.924h57.22zm-238.29 0V4.838h51.28v189.92zm-32.63-146.932c-1.38-.461-3.4-1.06-6.08-1.797q-3.87-1.245-9.12-2.35-5.25-1.244-11.61-2.073c-4.15-.553-8.43-.83-12.86-.83-9.21 0-17.83 1.152-25.84 3.456q-11.895 3.317-22.26 8.984a125.3 125.3 0 0 0-19.35 12.717c-5.9 4.7-11.33 9.537-16.31 14.514v114.311h-51.28V4.838h51.28v30.824c6.17-4.608 12.49-9.03 18.94-13.27a163 163 0 0 1 20.32-11.472c7.09-3.318 14.56-5.944 22.39-7.88 7.83-2.026 16.12-3.04 24.88-3.04 3.32 0 6.68.184 10.09.553 3.5.276 6.86.645 10.09 1.106 3.32.46 6.4.967 9.26 1.52s5.35 1.106 7.46 1.659zM1263.71 99.66c0 12.164-1.33 22.991-4 32.483-2.68 9.399-6.45 17.6-11.34 24.604-4.79 7.003-10.6 12.947-17.42 17.831q-10.08 7.325-22.53 11.749c-8.29 2.948-17.23 5.114-26.81 6.496q-14.37 1.935-30 1.935h-120.53V4.838h120.26c10.41 0 20.41.691 29.99 2.073 9.59 1.29 18.52 3.41 26.82 6.359 8.38 2.948 15.99 6.865 22.8 11.749 6.82 4.791 12.63 10.735 17.42 17.83 4.89 7.004 8.66 15.205 11.34 24.605 2.67 9.399 4 20.134 4 32.206m-51.69 0q0-13.685-3.6-23.775c-2.3-6.727-6.08-12.256-11.33-16.587-5.16-4.423-11.93-7.694-20.32-9.814-8.38-2.211-18.61-3.317-30.68-3.317h-63.73v107.262h63.73c12.07 0 22.3-1.06 30.68-3.179 8.39-2.212 15.16-5.529 20.32-9.952 5.25-4.515 9.03-10.137 11.33-16.864q3.6-10.09 3.6-23.774M986.574 63.169q0 14.237-4.699 25.157-4.562 10.92-14.514 18.383-9.952 7.464-25.71 11.335-15.62 3.732-37.735 3.732h-85.285v72.982h-17.278V4.838h102.563q22.116 0 37.735 3.87 15.758 3.732 25.71 11.058t14.514 18.246q4.7 10.92 4.699 25.157m-17.831 0q0-13.546-4.561-21.84-4.423-8.294-13.823-12.716-9.26-4.562-23.636-6.082-14.237-1.52-33.865-1.52h-74.227v84.593h74.227q7.602 0 16.172.138 8.709 0 17.14-.83 8.432-.967 16.034-3.179 7.74-2.349 13.546-7.05 5.945-4.699 9.399-12.301 3.594-7.602 3.594-19.213M766.455 19.49q-4.01-1.244-11.197-2.627-7.049-1.52-18.245-1.52-15.481 0-29.166 3.87-13.545 3.732-25.295 10.229a126.3 126.3 0 0 0-21.563 15.205q-9.951 8.57-17.969 18.245v131.866h-17.278V4.838h17.278V43.54a166 166 0 0 1 20.043-17.555q10.92-8.017 22.945-13.684a125.8 125.8 0 0 1 25.157-8.985Q724.435 0 738.533 0q4.839 0 8.708.276 3.87.14 7.05.553 3.317.277 6.22.691 2.902.416 5.944.968zm-220.33 175.268v-21.01q-9.952 5.39-22.669 9.952-12.579 4.562-26.677 7.879-13.96 3.318-28.751 5.114-14.79 1.797-29.027 1.797-18.522 0-33.312-3.317-14.652-3.318-24.881-9.814-10.228-6.635-15.757-16.31-5.529-9.814-5.529-22.669 0-12.717 6.358-22.393 6.497-9.813 18.384-17.001 11.887-7.326 28.612-12.302 16.864-5.115 37.598-8.57 20.733-3.594 44.784-5.806 24.19-2.349 50.867-3.87V61.925q0-8.846-3.318-15.62-3.317-6.772-9.261-11.749-5.943-5.115-13.96-8.431-8.017-3.456-17.416-5.53-9.262-2.074-19.628-2.902a219 219 0 0 0-20.596-.968q-13.96 0-25.433 1.383-11.473 1.381-21.425 3.87-9.952 2.35-19.075 5.529a563 563 0 0 0-18.384 6.773V14.099q19.213-5.114 40.915-8.846 21.839-3.87 46.305-3.87 20.733 0 38.703 3.593 17.969 3.456 31.238 11.196 13.27 7.74 20.872 20.043 7.603 12.164 7.603 29.718v128.825zm0-102.977q-45.2 2.489-77.268 7.05-31.929 4.561-52.248 11.196-20.32 6.634-29.857 15.481-9.399 8.846-9.399 20.042 0 9.123 4.561 16.173 4.7 7.049 13.408 11.887 8.708 4.7 21.148 7.188 12.44 2.349 27.922 2.349 9.123 0 18.798-.967a276 276 0 0 0 19.49-2.903 319 319 0 0 0 19.075-4.423q9.4-2.627 17.693-5.667t15.066-6.497q6.912-3.455 11.611-7.049zM180.245 4.838l67.73 176.098L318.193 4.838h19.213l-76.438 189.92h-25.71L168.634 23.222l-66.486 171.536h-25.71L0 4.838h19.213l70.218 176.098L157.023 4.838z"/></svg>
|