@openstax/ts-utils 1.7.0 → 1.8.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.
Files changed (55) hide show
  1. package/dist/cjs/{fetch.d.ts → fetch/index.d.ts} +1 -1
  2. package/dist/{esm/middleware.d.ts → cjs/middleware/index.d.ts} +2 -2
  3. package/dist/cjs/{pagination.d.ts → pagination/index.d.ts} +1 -1
  4. package/dist/cjs/{pagination.js → pagination/index.js} +3 -3
  5. package/dist/cjs/routing/index.d.ts +0 -3
  6. package/dist/cjs/routing/index.js +7 -9
  7. package/dist/cjs/services/authProvider/decryption.js +5 -5
  8. package/dist/cjs/services/authProvider/index.d.ts +0 -2
  9. package/dist/cjs/services/authProvider/subrequest.js +6 -6
  10. package/dist/cjs/services/exercisesGateway/index.d.ts +3 -6
  11. package/dist/cjs/services/exercisesGateway/index.js +7 -7
  12. package/dist/cjs/services/versionedDocumentStore/dynamodb.d.ts +1 -4
  13. package/dist/cjs/services/versionedDocumentStore/dynamodb.js +13 -13
  14. package/dist/cjs/services/versionedDocumentStore/file-system.d.ts +1 -4
  15. package/dist/cjs/services/versionedDocumentStore/file-system.js +13 -13
  16. package/dist/cjs/tsconfig.without-specs.cjs.tsbuildinfo +1 -1
  17. package/dist/cjs/types.d.ts +4 -0
  18. package/dist/esm/{fetch.d.ts → fetch/index.d.ts} +1 -1
  19. package/dist/{cjs/middleware.d.ts → esm/middleware/index.d.ts} +2 -2
  20. package/dist/esm/{pagination.d.ts → pagination/index.d.ts} +1 -1
  21. package/dist/esm/{pagination.js → pagination/index.js} +3 -3
  22. package/dist/esm/routing/index.d.ts +0 -3
  23. package/dist/esm/routing/index.js +7 -9
  24. package/dist/esm/services/authProvider/decryption.js +5 -5
  25. package/dist/esm/services/authProvider/index.d.ts +0 -2
  26. package/dist/esm/services/authProvider/subrequest.js +6 -6
  27. package/dist/esm/services/exercisesGateway/index.d.ts +3 -6
  28. package/dist/esm/services/exercisesGateway/index.js +7 -7
  29. package/dist/esm/services/versionedDocumentStore/dynamodb.d.ts +1 -4
  30. package/dist/esm/services/versionedDocumentStore/dynamodb.js +13 -13
  31. package/dist/esm/services/versionedDocumentStore/file-system.d.ts +1 -4
  32. package/dist/esm/services/versionedDocumentStore/file-system.js +13 -13
  33. package/dist/esm/tsconfig.without-specs.esm.tsbuildinfo +1 -1
  34. package/dist/esm/types.d.ts +4 -0
  35. package/package.json +108 -13
  36. package/dist/cjs/profile.d.ts +0 -61
  37. package/dist/cjs/profile.js +0 -199
  38. package/dist/esm/profile.d.ts +0 -61
  39. package/dist/esm/profile.js +0 -191
  40. /package/dist/cjs/{assertions.d.ts → assertions/index.d.ts} +0 -0
  41. /package/dist/cjs/{assertions.js → assertions/index.js} +0 -0
  42. /package/dist/cjs/{errors.d.ts → errors/index.d.ts} +0 -0
  43. /package/dist/cjs/{errors.js → errors/index.js} +0 -0
  44. /package/dist/cjs/{fetch.js → fetch/index.js} +0 -0
  45. /package/dist/cjs/{guards.d.ts → guards/index.d.ts} +0 -0
  46. /package/dist/cjs/{guards.js → guards/index.js} +0 -0
  47. /package/dist/cjs/{middleware.js → middleware/index.js} +0 -0
  48. /package/dist/esm/{assertions.d.ts → assertions/index.d.ts} +0 -0
  49. /package/dist/esm/{assertions.js → assertions/index.js} +0 -0
  50. /package/dist/esm/{errors.d.ts → errors/index.d.ts} +0 -0
  51. /package/dist/esm/{errors.js → errors/index.js} +0 -0
  52. /package/dist/esm/{fetch.js → fetch/index.js} +0 -0
  53. /package/dist/esm/{guards.d.ts → guards/index.d.ts} +0 -0
  54. /package/dist/esm/{guards.js → guards/index.js} +0 -0
  55. /package/dist/esm/{middleware.js → middleware/index.js} +0 -0
@@ -7,6 +7,10 @@
7
7
  * There may be a better way to do this; `T1 extends T2` doesn't work
8
8
  */
9
9
  export declare type TupleExtends<T1, T2> = T1 extends [infer T1Head, ...infer T1Tail] ? T2 extends [infer T2Head, ...infer T2Tail] ? T1Head extends T2Head ? TupleExtends<T1Tail, T2Tail> extends 'yes' ? 'yes' : 'no' : 'no' : T2 extends [] ? 'yes' : 'no' : T1 extends [] ? T2 extends [] ? 'yes' : 'no' : 'no';
10
+ /**
11
+ * unions each member of the tuple
12
+ */
13
+ export declare type TupleZip<T1, T2> = T1 extends [infer T1Head, ...infer T1Tail] ? T2 extends [infer T2Head, ...infer T2Tail] ? [T1Head & T2Head, ...TupleZip<T1Tail, T2Tail>] : T2 extends [] ? T1 : never : T1 extends [] ? T2 extends any[] ? T2 : never : never;
10
14
  /**
11
15
  * If `R` is `Promise<I>`, returns `I`, otherwise returns `R`
12
16
  * @deprecated use TypeScript builtin Awaited instead:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openstax/ts-utils",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "bin": {
5
5
  "ts-utils": "./script/bin-entry.bash"
6
6
  },
@@ -50,20 +50,115 @@
50
50
  "import": "./dist/esm/index.js",
51
51
  "require": "./dist/cjs/index.js"
52
52
  },
53
- "./": {
54
- "browser": "./dist/cjs/",
55
- "import": "./dist/esm/",
56
- "require": "./dist/cjs/"
53
+ "./*": {
54
+ "browser": "./dist/cjs/*.js",
55
+ "import": "./dist/esm/*.js",
56
+ "require": "./dist/cjs/*.js"
57
57
  },
58
- "./**": {
59
- "browser": "./dist/cjs/**.js",
60
- "import": "./dist/esm/**.js",
61
- "require": "./dist/cjs/**.js"
58
+ "./assertions": {
59
+ "browser": "./dist/cjs/assertions/index.js",
60
+ "import": "./dist/esm/assertions/index.js",
61
+ "require": "./dist/cjs/assertions/index.js"
62
62
  },
63
- "./**/": {
64
- "browser": "./dist/cjs/**/index.js",
65
- "import": "./dist/esm/**/index.js",
66
- "require": "./dist/cjs/**/index.js"
63
+ "./aws": {
64
+ "browser": "./dist/cjs/aws/index.js",
65
+ "import": "./dist/esm/aws/index.js",
66
+ "require": "./dist/cjs/aws/index.js"
67
+ },
68
+ "./config": {
69
+ "browser": "./dist/cjs/config/index.js",
70
+ "import": "./dist/esm/config/index.js",
71
+ "require": "./dist/cjs/config/index.js"
72
+ },
73
+ "./errors": {
74
+ "browser": "./dist/cjs/errors/index.js",
75
+ "import": "./dist/esm/errors/index.js",
76
+ "require": "./dist/cjs/errors/index.js"
77
+ },
78
+ "./fetch": {
79
+ "browser": "./dist/cjs/fetch/index.js",
80
+ "import": "./dist/esm/fetch/index.js",
81
+ "require": "./dist/cjs/fetch/index.js"
82
+ },
83
+ "./guards": {
84
+ "browser": "./dist/cjs/guards/index.js",
85
+ "import": "./dist/esm/guards/index.js",
86
+ "require": "./dist/cjs/guards/index.js"
87
+ },
88
+ "./middleware": {
89
+ "browser": "./dist/cjs/middleware/index.js",
90
+ "import": "./dist/esm/middleware/index.js",
91
+ "require": "./dist/cjs/middleware/index.js"
92
+ },
93
+ "./misc": {
94
+ "browser": "./dist/cjs/misc/index.js",
95
+ "import": "./dist/esm/misc/index.js",
96
+ "require": "./dist/cjs/misc/index.js"
97
+ },
98
+ "./pagination": {
99
+ "browser": "./dist/cjs/pagination/index.js",
100
+ "import": "./dist/esm/pagination/index.js",
101
+ "require": "./dist/cjs/pagination/index.js"
102
+ },
103
+ "./routing": {
104
+ "browser": "./dist/cjs/routing/index.js",
105
+ "import": "./dist/esm/routing/index.js",
106
+ "require": "./dist/cjs/routing/index.js"
107
+ },
108
+ "./services": {
109
+ "browser": "./dist/cjs/services/index.js",
110
+ "import": "./dist/esm/services/index.js",
111
+ "require": "./dist/cjs/services/index.js"
112
+ },
113
+ "./services/launchParams": {
114
+ "browser": "./dist/cjs/services/launchParams/index.js",
115
+ "import": "./dist/esm/services/launchParams/index.js",
116
+ "require": "./dist/cjs/services/launchParams/index.js"
117
+ },
118
+ "./services/exercisesGateway": {
119
+ "browser": "./dist/cjs/services/exercisesGateway/index.js",
120
+ "import": "./dist/esm/services/exercisesGateway/index.js",
121
+ "require": "./dist/cjs/services/exercisesGateway/index.js"
122
+ },
123
+ "./services/accountsGateway": {
124
+ "browser": "./dist/cjs/services/accountsGateway/index.js",
125
+ "import": "./dist/esm/services/accountsGateway/index.js",
126
+ "require": "./dist/cjs/services/accountsGateway/index.js"
127
+ },
128
+ "./services/logger": {
129
+ "browser": "./dist/cjs/services/logger/index.js",
130
+ "import": "./dist/esm/services/logger/index.js",
131
+ "require": "./dist/cjs/services/logger/index.js"
132
+ },
133
+ "./services/searchProvider": {
134
+ "browser": "./dist/cjs/services/searchProvider/index.js",
135
+ "import": "./dist/esm/services/searchProvider/index.js",
136
+ "require": "./dist/cjs/services/searchProvider/index.js"
137
+ },
138
+ "./services/versionedDocumentStore": {
139
+ "browser": "./dist/cjs/services/versionedDocumentStore/index.js",
140
+ "import": "./dist/esm/services/versionedDocumentStore/index.js",
141
+ "require": "./dist/cjs/services/versionedDocumentStore/index.js"
142
+ },
143
+ "./services/lrsGateway": {
144
+ "browser": "./dist/cjs/services/lrsGateway/index.js",
145
+ "import": "./dist/esm/services/lrsGateway/index.js",
146
+ "require": "./dist/cjs/services/lrsGateway/index.js"
147
+ },
148
+ "./services/authProvider": {
149
+ "browser": "./dist/cjs/services/authProvider/index.js",
150
+ "import": "./dist/esm/services/authProvider/index.js",
151
+ "require": "./dist/cjs/services/authProvider/index.js"
152
+ },
153
+ "./services/authProvider/utils": {
154
+ "browser": "./dist/cjs/services/authProvider/utils/index.js",
155
+ "import": "./dist/esm/services/authProvider/utils/index.js",
156
+ "require": "./dist/cjs/services/authProvider/utils/index.js"
157
+ },
158
+ "./services/apiGateway": {
159
+ "browser": "./dist/cjs/services/apiGateway/index.js",
160
+ "import": "./dist/esm/services/apiGateway/index.js",
161
+ "require": "./dist/cjs/services/apiGateway/index.js"
67
162
  }
68
163
  },
69
164
  "files": [
@@ -1,61 +0,0 @@
1
- import { GenericFetch } from './fetch';
2
- import { ApiResponse, HttpHeaders } from './routing';
3
- import { Logger } from './services/logger';
4
- export interface Call {
5
- start: number;
6
- end?: number;
7
- children: Array<Profile | ProfileData>;
8
- }
9
- export declare type ProfileFunction = <Fn extends (...args: any[]) => any>(trackName: string, fn: (p: Track) => Fn) => Fn;
10
- export interface Track {
11
- track: ProfileFunction;
12
- trackFetch: <F extends GenericFetch>(fetch: F) => F;
13
- setTracing: (tracing: boolean | Promise<boolean>) => void;
14
- getTracing: () => Promise<undefined | boolean>;
15
- data: () => Call;
16
- report: () => CallReport;
17
- adopt: (child: Profile) => void;
18
- }
19
- export interface ProfileData {
20
- name: string;
21
- calls: Call[];
22
- }
23
- export interface Profile extends ProfileData {
24
- report: () => ProfileReport;
25
- start: () => Track & {
26
- end: () => void;
27
- };
28
- }
29
- interface CallReport {
30
- time: number | string;
31
- children: ProfileReport[];
32
- coverage: 'unknown' | {
33
- accountedTime: number;
34
- unaccountedTime: number;
35
- accountedPercent: number;
36
- };
37
- }
38
- interface ProfileReport {
39
- name: string;
40
- time: 'unknown' | {
41
- total: number;
42
- min: number;
43
- max: number;
44
- avg: number;
45
- };
46
- calls: CallReport[];
47
- }
48
- export declare const createProfile: (name: string, options?: {
49
- trace?: boolean | Promise<boolean>;
50
- }) => Profile;
51
- export declare const makeProfileApiResponseMiddleware: <Ri extends {
52
- headers: HttpHeaders;
53
- }>(showReport: (request: Ri) => string | boolean) => () => (responsePromise: Promise<ApiResponse<number, any>> | undefined, { request, profile }: {
54
- request: Ri;
55
- profile: Track;
56
- logger: Logger;
57
- }) => Promise<ApiResponse<number, any>> | undefined;
58
- export declare const makeHtmlReport: (report: CallReport) => string;
59
- export declare const makeHtmlProfileReport: (report: ProfileReport) => string;
60
- export declare const makeHtmlCallReport: (report: CallReport, index: number) => string;
61
- export {};
@@ -1,199 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.makeHtmlCallReport = exports.makeHtmlProfileReport = exports.makeHtmlReport = exports.makeProfileApiResponseMiddleware = exports.createProfile = void 0;
4
- const guards_1 = require("./guards");
5
- const routing_1 = require("./routing");
6
- const _1 = require(".");
7
- const callReport = (call) => {
8
- const time = call.start && call.end
9
- ? call.end - call.start
10
- : 'unknown';
11
- const children = call.children
12
- .filter(profile => profile.calls.length > 0)
13
- .map(profileReport);
14
- const accountedTime = children.reduce((result, profile) => typeof profile.time === 'string' ? result : result + profile.time.total, 0);
15
- const coverage = typeof time === 'number' && children.length > 0 ? {
16
- accountedTime,
17
- unaccountedTime: time - accountedTime,
18
- accountedPercent: (0, _1.roundToPrecision)(accountedTime / time, -2),
19
- } : 'unknown';
20
- return { time, children, coverage };
21
- };
22
- const profileReport = (profile) => {
23
- const calls = profile.calls.map(callReport);
24
- const times = calls
25
- .map(call => call.time)
26
- .filter(guards_1.isNumber);
27
- const time = times.length > 0 ? {
28
- total: times.reduce((result, item) => result + item, 0),
29
- min: Math.min(...times),
30
- max: Math.max(...times),
31
- avg: Math.round(times.reduce((result, item) => result + item, 0) / times.length),
32
- } : 'unknown';
33
- return {
34
- name: profile.name, calls, time
35
- };
36
- };
37
- const createProfile = (name, options = {}) => {
38
- const profile = {
39
- name,
40
- calls: [],
41
- report: () => profileReport(profile),
42
- start: () => {
43
- const call = { start: Date.now(), children: [] };
44
- profile.calls.push(call);
45
- return {
46
- end: () => { call.end = Date.now(); },
47
- report: () => callReport({ end: Date.now(), ...call }),
48
- data: () => ({ end: Date.now(), ...call }),
49
- adopt: (childProfile) => { call.children.push(childProfile); },
50
- setTracing: (tracing) => { options.trace = tracing; },
51
- getTracing: async () => options.trace,
52
- trackFetch: (fetch) => {
53
- const trackProfile = (0, exports.createProfile)('fetch', options);
54
- call.children.push(trackProfile);
55
- return (async (url, config) => {
56
- const { end, ...track } = trackProfile.start();
57
- const fetchConfig = await options.trace
58
- ? (0, _1.merge)(config || {}, {
59
- headers: { 'x-application-profile': 'trace' }
60
- })
61
- : config;
62
- return fetch(url, fetchConfig).then(response => {
63
- const fetchProfileJson = response.headers.get('x-application-profile');
64
- const fetchProfile = fetchProfileJson ? JSON.parse(fetchProfileJson) : undefined;
65
- end();
66
- if (fetchProfile) {
67
- const subrequestProfile = (0, exports.createProfile)(url, options);
68
- subrequestProfile.calls.push(fetchProfile);
69
- track.adopt(subrequestProfile);
70
- }
71
- return response;
72
- });
73
- });
74
- },
75
- track: (trackName, fn) => {
76
- const trackProfile = (0, exports.createProfile)(trackName, options);
77
- call.children.push(trackProfile);
78
- return ((...args) => {
79
- const { end, ...track } = trackProfile.start();
80
- const result = fn(track)(...args);
81
- if (result instanceof Promise) {
82
- return result.finally(() => { end(); });
83
- }
84
- else {
85
- end();
86
- return result;
87
- }
88
- });
89
- }
90
- };
91
- },
92
- };
93
- return profile;
94
- };
95
- exports.createProfile = createProfile;
96
- const makeProfileApiResponseMiddleware = (showReport) => () => (responsePromise, { request, profile }) => {
97
- if (responsePromise) {
98
- return responsePromise.then(async (response) => {
99
- const reportFormat = showReport(request);
100
- const enabled = await profile.getTracing();
101
- // profiling is disabled, return original response
102
- if (!enabled) {
103
- return response;
104
- }
105
- // there is no report format, return original response but
106
- // add sub-request trace response if necessary
107
- if (reportFormat === false) {
108
- if ((0, routing_1.getHeader)(request.headers, 'x-application-profile')) {
109
- return {
110
- ...response,
111
- headers: {
112
- ...response.headers,
113
- 'x-application-profile': JSON.stringify(profile.data())
114
- }
115
- };
116
- }
117
- return response;
118
- // override response with html report
119
- }
120
- else if (reportFormat === 'html') {
121
- return (0, routing_1.apiHtmlResponse)(200, (0, exports.makeHtmlReport)(profile.report()));
122
- }
123
- // override response with json report
124
- return (0, routing_1.apiJsonResponse)(200, profile.report());
125
- });
126
- }
127
- return responsePromise;
128
- };
129
- exports.makeProfileApiResponseMiddleware = makeProfileApiResponseMiddleware;
130
- const makeHtmlReport = (report) => `
131
- <head>
132
- <style>
133
- table {
134
- border-collapse: collapse;
135
- }
136
- table td,th {
137
- border: 1px solid #ccc;
138
- padding: 10px;
139
- }
140
- summary {
141
- padding: 10px 0;
142
- }
143
- </style>
144
- </head>
145
- <body>
146
- ${(0, exports.makeHtmlCallReport)(report, 0)}
147
- </body>
148
- `;
149
- exports.makeHtmlReport = makeHtmlReport;
150
- const makeHtmlProfileReport = (report) => `
151
- <details>
152
- <summary>Profile: ${report.name}</summary>
153
- <table>
154
- <thead>
155
- <tr>
156
- <th>call #</th>
157
- <th>children</th>
158
- <th>time</th>
159
- <th>time coverage (accounted/unaccounted/percent)</th>
160
- </tr>
161
- </thead>
162
- <tbody>
163
- ${report.calls.map((call, i) => `<tr>
164
- <td>${i + 1}</td>
165
- <td>${call.children.length}</td>
166
- <td>${call.time}</td>
167
- <td>${typeof call.coverage === 'string' ? call.coverage : `${call.coverage.accountedTime}/${call.coverage.unaccountedTime}/${call.coverage.accountedPercent}`}</td>
168
- </tr>`)}
169
- </tbody>
170
- </table>
171
- <div style="padding-left: 20px; border-left: 1px solid #ccc;">
172
- ${report.calls.map(exports.makeHtmlCallReport).join('')}
173
- </div>
174
- </details>
175
- `;
176
- exports.makeHtmlProfileReport = makeHtmlProfileReport;
177
- const makeHtmlCallReport = (report, index) => `
178
- <h4>call # ${index + 1} children<h4>
179
- <table>
180
- <thead>
181
- <tr>
182
- <th>name</th>
183
- <th>calls</th>
184
- <th>time (total (min/max/avg))</th>
185
- </tr>
186
- </thead>
187
- <tbody>
188
- ${report.children.map(profile => `<tr>
189
- <td>${profile.name}</td>
190
- <td>${profile.calls.length}</td>
191
- <td>${typeof profile.time === 'string' ? profile.time : `${profile.time.total} (${profile.time.min}/${profile.time.max}/${profile.time.avg})`}</td>
192
- </tr>`).join('')}
193
- </tbody>
194
- </table>
195
- <div style="margin-left: 20px">
196
- ${report.children.map(exports.makeHtmlProfileReport).join('')}
197
- </div>
198
- `;
199
- exports.makeHtmlCallReport = makeHtmlCallReport;
@@ -1,61 +0,0 @@
1
- import { GenericFetch } from './fetch';
2
- import { ApiResponse, HttpHeaders } from './routing';
3
- import { Logger } from './services/logger';
4
- export interface Call {
5
- start: number;
6
- end?: number;
7
- children: Array<Profile | ProfileData>;
8
- }
9
- export declare type ProfileFunction = <Fn extends (...args: any[]) => any>(trackName: string, fn: (p: Track) => Fn) => Fn;
10
- export interface Track {
11
- track: ProfileFunction;
12
- trackFetch: <F extends GenericFetch>(fetch: F) => F;
13
- setTracing: (tracing: boolean | Promise<boolean>) => void;
14
- getTracing: () => Promise<undefined | boolean>;
15
- data: () => Call;
16
- report: () => CallReport;
17
- adopt: (child: Profile) => void;
18
- }
19
- export interface ProfileData {
20
- name: string;
21
- calls: Call[];
22
- }
23
- export interface Profile extends ProfileData {
24
- report: () => ProfileReport;
25
- start: () => Track & {
26
- end: () => void;
27
- };
28
- }
29
- interface CallReport {
30
- time: number | string;
31
- children: ProfileReport[];
32
- coverage: 'unknown' | {
33
- accountedTime: number;
34
- unaccountedTime: number;
35
- accountedPercent: number;
36
- };
37
- }
38
- interface ProfileReport {
39
- name: string;
40
- time: 'unknown' | {
41
- total: number;
42
- min: number;
43
- max: number;
44
- avg: number;
45
- };
46
- calls: CallReport[];
47
- }
48
- export declare const createProfile: (name: string, options?: {
49
- trace?: boolean | Promise<boolean>;
50
- }) => Profile;
51
- export declare const makeProfileApiResponseMiddleware: <Ri extends {
52
- headers: HttpHeaders;
53
- }>(showReport: (request: Ri) => string | boolean) => () => (responsePromise: Promise<ApiResponse<number, any>> | undefined, { request, profile }: {
54
- request: Ri;
55
- profile: Track;
56
- logger: Logger;
57
- }) => Promise<ApiResponse<number, any>> | undefined;
58
- export declare const makeHtmlReport: (report: CallReport) => string;
59
- export declare const makeHtmlProfileReport: (report: ProfileReport) => string;
60
- export declare const makeHtmlCallReport: (report: CallReport, index: number) => string;
61
- export {};
@@ -1,191 +0,0 @@
1
- import { isNumber } from './guards';
2
- import { apiHtmlResponse, apiJsonResponse, getHeader } from './routing';
3
- import { merge, roundToPrecision } from '.';
4
- const callReport = (call) => {
5
- const time = call.start && call.end
6
- ? call.end - call.start
7
- : 'unknown';
8
- const children = call.children
9
- .filter(profile => profile.calls.length > 0)
10
- .map(profileReport);
11
- const accountedTime = children.reduce((result, profile) => typeof profile.time === 'string' ? result : result + profile.time.total, 0);
12
- const coverage = typeof time === 'number' && children.length > 0 ? {
13
- accountedTime,
14
- unaccountedTime: time - accountedTime,
15
- accountedPercent: roundToPrecision(accountedTime / time, -2),
16
- } : 'unknown';
17
- return { time, children, coverage };
18
- };
19
- const profileReport = (profile) => {
20
- const calls = profile.calls.map(callReport);
21
- const times = calls
22
- .map(call => call.time)
23
- .filter(isNumber);
24
- const time = times.length > 0 ? {
25
- total: times.reduce((result, item) => result + item, 0),
26
- min: Math.min(...times),
27
- max: Math.max(...times),
28
- avg: Math.round(times.reduce((result, item) => result + item, 0) / times.length),
29
- } : 'unknown';
30
- return {
31
- name: profile.name, calls, time
32
- };
33
- };
34
- export const createProfile = (name, options = {}) => {
35
- const profile = {
36
- name,
37
- calls: [],
38
- report: () => profileReport(profile),
39
- start: () => {
40
- const call = { start: Date.now(), children: [] };
41
- profile.calls.push(call);
42
- return {
43
- end: () => { call.end = Date.now(); },
44
- report: () => callReport({ end: Date.now(), ...call }),
45
- data: () => ({ end: Date.now(), ...call }),
46
- adopt: (childProfile) => { call.children.push(childProfile); },
47
- setTracing: (tracing) => { options.trace = tracing; },
48
- getTracing: async () => options.trace,
49
- trackFetch: (fetch) => {
50
- const trackProfile = createProfile('fetch', options);
51
- call.children.push(trackProfile);
52
- return (async (url, config) => {
53
- const { end, ...track } = trackProfile.start();
54
- const fetchConfig = await options.trace
55
- ? merge(config || {}, {
56
- headers: { 'x-application-profile': 'trace' }
57
- })
58
- : config;
59
- return fetch(url, fetchConfig).then(response => {
60
- const fetchProfileJson = response.headers.get('x-application-profile');
61
- const fetchProfile = fetchProfileJson ? JSON.parse(fetchProfileJson) : undefined;
62
- end();
63
- if (fetchProfile) {
64
- const subrequestProfile = createProfile(url, options);
65
- subrequestProfile.calls.push(fetchProfile);
66
- track.adopt(subrequestProfile);
67
- }
68
- return response;
69
- });
70
- });
71
- },
72
- track: (trackName, fn) => {
73
- const trackProfile = createProfile(trackName, options);
74
- call.children.push(trackProfile);
75
- return ((...args) => {
76
- const { end, ...track } = trackProfile.start();
77
- const result = fn(track)(...args);
78
- if (result instanceof Promise) {
79
- return result.finally(() => { end(); });
80
- }
81
- else {
82
- end();
83
- return result;
84
- }
85
- });
86
- }
87
- };
88
- },
89
- };
90
- return profile;
91
- };
92
- export const makeProfileApiResponseMiddleware = (showReport) => () => (responsePromise, { request, profile }) => {
93
- if (responsePromise) {
94
- return responsePromise.then(async (response) => {
95
- const reportFormat = showReport(request);
96
- const enabled = await profile.getTracing();
97
- // profiling is disabled, return original response
98
- if (!enabled) {
99
- return response;
100
- }
101
- // there is no report format, return original response but
102
- // add sub-request trace response if necessary
103
- if (reportFormat === false) {
104
- if (getHeader(request.headers, 'x-application-profile')) {
105
- return {
106
- ...response,
107
- headers: {
108
- ...response.headers,
109
- 'x-application-profile': JSON.stringify(profile.data())
110
- }
111
- };
112
- }
113
- return response;
114
- // override response with html report
115
- }
116
- else if (reportFormat === 'html') {
117
- return apiHtmlResponse(200, makeHtmlReport(profile.report()));
118
- }
119
- // override response with json report
120
- return apiJsonResponse(200, profile.report());
121
- });
122
- }
123
- return responsePromise;
124
- };
125
- export const makeHtmlReport = (report) => `
126
- <head>
127
- <style>
128
- table {
129
- border-collapse: collapse;
130
- }
131
- table td,th {
132
- border: 1px solid #ccc;
133
- padding: 10px;
134
- }
135
- summary {
136
- padding: 10px 0;
137
- }
138
- </style>
139
- </head>
140
- <body>
141
- ${makeHtmlCallReport(report, 0)}
142
- </body>
143
- `;
144
- export const makeHtmlProfileReport = (report) => `
145
- <details>
146
- <summary>Profile: ${report.name}</summary>
147
- <table>
148
- <thead>
149
- <tr>
150
- <th>call #</th>
151
- <th>children</th>
152
- <th>time</th>
153
- <th>time coverage (accounted/unaccounted/percent)</th>
154
- </tr>
155
- </thead>
156
- <tbody>
157
- ${report.calls.map((call, i) => `<tr>
158
- <td>${i + 1}</td>
159
- <td>${call.children.length}</td>
160
- <td>${call.time}</td>
161
- <td>${typeof call.coverage === 'string' ? call.coverage : `${call.coverage.accountedTime}/${call.coverage.unaccountedTime}/${call.coverage.accountedPercent}`}</td>
162
- </tr>`)}
163
- </tbody>
164
- </table>
165
- <div style="padding-left: 20px; border-left: 1px solid #ccc;">
166
- ${report.calls.map(makeHtmlCallReport).join('')}
167
- </div>
168
- </details>
169
- `;
170
- export const makeHtmlCallReport = (report, index) => `
171
- <h4>call # ${index + 1} children<h4>
172
- <table>
173
- <thead>
174
- <tr>
175
- <th>name</th>
176
- <th>calls</th>
177
- <th>time (total (min/max/avg))</th>
178
- </tr>
179
- </thead>
180
- <tbody>
181
- ${report.children.map(profile => `<tr>
182
- <td>${profile.name}</td>
183
- <td>${profile.calls.length}</td>
184
- <td>${typeof profile.time === 'string' ? profile.time : `${profile.time.total} (${profile.time.min}/${profile.time.max}/${profile.time.avg})`}</td>
185
- </tr>`).join('')}
186
- </tbody>
187
- </table>
188
- <div style="margin-left: 20px">
189
- ${report.children.map(makeHtmlProfileReport).join('')}
190
- </div>
191
- `;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes