ng-talkback 21.0.16 → 21.0.18

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 (59) hide show
  1. package/browser/package.json +1 -1
  2. package/browser-prod/package.json +1 -1
  3. package/lib/build-info._auto-generated_.d.ts +1 -1
  4. package/lib/build-info._auto-generated_.js +1 -1
  5. package/lib/package.json +1 -1
  6. package/lib-prod/{build-info._auto-generated_.ts → build-info._auto-generated_.js} +1 -2
  7. package/lib-prod/env/{env.angular-node-app.ts → env.angular-node-app.js} +1 -1
  8. package/lib-prod/env/{env.docs-webapp.ts → env.docs-webapp.js} +1 -1
  9. package/lib-prod/env/{env.electron-app.ts → env.electron-app.js} +1 -1
  10. package/lib-prod/env/{env.mobile-app.ts → env.mobile-app.js} +1 -1
  11. package/lib-prod/env/{env.npm-lib-and-cli-tool.ts → env.npm-lib-and-cli-tool.js} +1 -1
  12. package/lib-prod/env/{env.vscode-plugin.ts → env.vscode-plugin.js} +1 -1
  13. package/lib-prod/es6.backend.js +2 -0
  14. package/lib-prod/features/error-rate.backend.js +21 -0
  15. package/lib-prod/features/latency.backend.js +28 -0
  16. package/lib-prod/{index._auto-generated_.ts → index._auto-generated_.js} +1 -1
  17. package/lib-prod/index.js +19 -0
  18. package/lib-prod/logger.backend.js +21 -0
  19. package/lib-prod/migrations/index.js +2 -0
  20. package/lib-prod/migrations/{migrations_index._auto-generated_.ts → migrations_index._auto-generated_.js} +0 -2
  21. package/lib-prod/options.backend.js +85 -0
  22. package/lib-prod/package.json +1 -1
  23. package/lib-prod/request-handler.backend.js +137 -0
  24. package/lib-prod/server.backend.js +79 -0
  25. package/lib-prod/summary.backend.js +19 -0
  26. package/lib-prod/talkback-factory.backend.js +16 -0
  27. package/lib-prod/tape-matcher.backend.js +91 -0
  28. package/lib-prod/tape-renderer.backend.js +94 -0
  29. package/lib-prod/tape-store.backend.js +92 -0
  30. package/lib-prod/tape.backend.js +69 -0
  31. package/lib-prod/types.backend.js +1 -0
  32. package/lib-prod/utils/content-encoding.backend.js +36 -0
  33. package/lib-prod/utils/headers.backend.js +14 -0
  34. package/lib-prod/utils/media-type.backend.js +49 -0
  35. package/package.json +1 -1
  36. package/websql/package.json +1 -1
  37. package/websql-prod/package.json +1 -1
  38. package/lib-prod/es6.backend.ts +0 -3
  39. package/lib-prod/features/error-rate.backend.ts +0 -31
  40. package/lib-prod/features/latency.backend.ts +0 -38
  41. package/lib-prod/index.ts +0 -27
  42. package/lib-prod/lib-info.md +0 -8
  43. package/lib-prod/logger.backend.ts +0 -26
  44. package/lib-prod/migrations/index.ts +0 -2
  45. package/lib-prod/migrations/migrations-info.md +0 -6
  46. package/lib-prod/options.backend.ts +0 -140
  47. package/lib-prod/request-handler.backend.ts +0 -165
  48. package/lib-prod/server.backend.ts +0 -100
  49. package/lib-prod/summary.backend.ts +0 -26
  50. package/lib-prod/talkback-factory.backend.ts +0 -18
  51. package/lib-prod/tape-matcher.backend.ts +0 -113
  52. package/lib-prod/tape-renderer.backend.ts +0 -118
  53. package/lib-prod/tape-store.backend.ts +0 -111
  54. package/lib-prod/tape.backend.ts +0 -93
  55. package/lib-prod/types.backend.ts +0 -55
  56. package/lib-prod/utils/content-encoding.backend.ts +0 -53
  57. package/lib-prod/utils/headers.backend.ts +0 -14
  58. package/lib-prod/utils/media-type.backend.ts +0 -62
  59. /package/lib-prod/env/{index.ts → index.js} +0 -0
@@ -1,165 +0,0 @@
1
- import TapeStore from './tape-store.backend';
2
- import { v4 as uuidv4 } from 'uuid';
3
-
4
- import axios, { AxiosResponse } from 'axios';
5
-
6
- import Tape from './tape.backend';
7
- import OptionsFactory, { RecordMode, FallbackMode, Options } from './options.backend';
8
- import ErrorRate from './features/error-rate.backend';
9
- import Latency from './features/latency.backend';
10
- import { HttpRequest, HttpResponse, MatchingContext } from './types.backend';
11
-
12
- export default class RequestHandler {
13
- private readonly tapeStore: TapeStore
14
- private readonly options: Options
15
- private readonly errorRate: ErrorRate
16
- private readonly latency: Latency
17
-
18
- constructor(tapeStore: TapeStore, options: Options) {
19
- this.tapeStore = tapeStore
20
- this.options = options
21
- this.errorRate = new ErrorRate(this.options)
22
- this.latency = new Latency(this.options)
23
- }
24
-
25
- async handle(req: HttpRequest): Promise<HttpResponse> {
26
- const matchingContext: MatchingContext = {
27
- id: uuidv4()
28
- }
29
-
30
- const recordMode = typeof (this.options.record) === "string" ? this.options.record : this.options.record(req)
31
-
32
- OptionsFactory.validateRecord(recordMode)
33
-
34
- if (this.options.requestDecorator) {
35
- req = this.options.requestDecorator(req, matchingContext)
36
- if (!req) {
37
- throw new Error("requestDecorator didn't return a req object")
38
- }
39
- }
40
-
41
- let newTape = new Tape(req, this.options)
42
- let matchingTape = this.tapeStore.find(newTape)
43
- let resObj, responseTape
44
-
45
- if (recordMode !== RecordMode.OVERWRITE && matchingTape) {
46
- responseTape = matchingTape
47
-
48
- if (this.errorRate.shouldSimulate(req, matchingTape)) {
49
- return this.errorRate.simulate(req)
50
- }
51
-
52
- await this.latency.simulate(req, matchingTape)
53
- } else {
54
- if (matchingTape) {
55
- responseTape = matchingTape
56
- } else {
57
- responseTape = newTape
58
- }
59
-
60
- if (recordMode === RecordMode.NEW || recordMode === RecordMode.OVERWRITE) {
61
- resObj = await this.makeRealRequest(req)
62
- responseTape.res = { ...resObj }
63
- if (this.options.tapeDecorator) {
64
- responseTape = this.options.tapeDecorator(responseTape, matchingContext)
65
- if (!responseTape) {
66
- throw new Error("tapeDecorator didn't return a tape object")
67
- }
68
- }
69
- await this.tapeStore.save(responseTape)
70
- } else {
71
- resObj = await this.onNoRecord(req)
72
- responseTape.res = { ...resObj }
73
- }
74
- }
75
-
76
- resObj = responseTape.res;
77
-
78
- if (this.options.responseDecorator) {
79
- const clonedTape = await responseTape.clone()
80
- const resTape = this.options.responseDecorator(clonedTape, req, matchingContext)
81
- if (!resTape) {
82
- throw new Error("responseDecorator didn't return a tape object")
83
- }
84
-
85
- if (resTape.res.headers["content-length"]) {
86
- resTape.res.headers["content-length"] = resTape.res.body.length
87
- }
88
- resObj = resTape.res
89
- }
90
-
91
- return resObj
92
- }
93
-
94
- private async onNoRecord(req: HttpRequest) {
95
- const fallbackMode = typeof (this.options.fallbackMode) === "string" ? this.options.fallbackMode : this.options.fallbackMode(req)
96
-
97
- OptionsFactory.validateFallbackMode(fallbackMode)
98
-
99
- this.options.logger.log(`Tape for ${req.url} not found and recording is disabled (fallbackMode: ${fallbackMode})`)
100
- this.options.logger.log({
101
- url: req.url,
102
- headers: req.headers
103
- })
104
-
105
- if (fallbackMode === FallbackMode.PROXY) {
106
- if (this.errorRate.shouldSimulate(req, undefined)) {
107
- return this.errorRate.simulate(req)
108
- }
109
-
110
- await this.latency.simulate(req, undefined)
111
- return await this.makeRealRequest(req)
112
- }
113
-
114
- return {
115
- status: 404,
116
- headers: { "content-type": ["text/plain"] },
117
- body: Buffer.from("talkback - tape not found")
118
- } as HttpResponse
119
- }
120
-
121
- private async makeRealRequest(req: HttpRequest) {
122
- // let fetchBody: Buffer | null
123
- let { method, url, body } = req
124
- // fetchBody = body
125
- const headers = { ...req.headers }
126
- delete headers.host
127
-
128
- const host = this.options.host
129
- // this.options.logger.log(`Making real request to ${host}${url}`)
130
-
131
- // if (method === "GET" || method === "HEAD") {
132
- // fetchBody = null
133
- // }
134
- let urlToGetData = `${host}${url}`;
135
- // console.log(`host: "${urlToGetData}"` )
136
- // console.log(`url: "${url}"` )
137
- var fRes = {
138
- status: 400,
139
- headers: {},
140
- body: new Buffer('')
141
- } as any;
142
- try {
143
- // console.log(body.toString())
144
- const r = await axios({
145
- url: urlToGetData,
146
- method,
147
- headers,
148
- data: body,
149
- responseType: 'arraybuffer',
150
- }) as any;
151
- fRes = {
152
- status: r.status,
153
- headers: r.headers,
154
- body: r.data
155
- } as HttpResponse;
156
- } catch (err) {
157
- fRes = {
158
- status: err?.response?.status,
159
- headers: err?.response?.headers,
160
- body: err?.response?.data
161
- } as any;
162
- }
163
- return fRes;
164
- }
165
- }
@@ -1,100 +0,0 @@
1
- import RequestHandler from './request-handler.backend';
2
- import Summary from './summary.backend';
3
- import TapeStore from './tape-store.backend';
4
- import * as http from 'http';
5
- import { https, fse } from 'tnp-core/lib-prod';
6
- import { Options } from './options.backend';
7
- import { Req } from './types.backend';
8
-
9
- export default class TalkbackServer {
10
- private readonly options: Options
11
- readonly tapeStore: TapeStore
12
- private requestHandler: RequestHandler
13
- private readonly closeSignalHandler?: (...args: any[]) => void
14
- private server?: http.Server
15
- private closed: boolean = false
16
-
17
- constructor(options: Options) {
18
- this.options = options
19
- this.tapeStore = new TapeStore(this.options)
20
- this.requestHandler = new RequestHandler(this.tapeStore, this.options)
21
-
22
- this.closeSignalHandler = this.close.bind(this)
23
- }
24
-
25
- handleRequest(rawReq: http.IncomingMessage, res: http.ServerResponse) {
26
- // console.log(`rawReq: ${rawReq.url}`)
27
- let reqBody = [] as Uint8Array[]
28
- rawReq.on("data", (chunk) => {
29
- reqBody.push(chunk)
30
- }).on("end", async () => {
31
- try {
32
- const req: Req = {
33
- headers: rawReq.headers,
34
- url: rawReq.url,
35
- method: rawReq.method,
36
- body: Buffer.concat(reqBody)
37
- }
38
- const fRes = await this.requestHandler.handle(req)
39
-
40
- res.writeHead(fRes.status, fRes.headers)
41
- res.end(fRes.body)
42
- } catch (ex) {
43
- console.error("Error handling request", ex)
44
- res.statusCode = 500
45
- res.end()
46
- }
47
- })
48
- }
49
-
50
- async start(callback?: () => void) {
51
- await this.tapeStore.load()
52
- const handleRequest = this.handleRequest.bind(this)
53
-
54
- const serverFactory = this.options.https.enabled ? () => {
55
- const httpsOpts = {
56
- key: fse.readFileSync(this.options.https.keyPath!),
57
- cert: fse.readFileSync(this.options.https.certPath!)
58
- }
59
- return https.createServer(httpsOpts, handleRequest)
60
- } : () => http.createServer(handleRequest)
61
-
62
- this.server = serverFactory()
63
- console.log(`Starting talkback on ${this.options.port}`)
64
- this.server.listen(this.options.port, callback)
65
-
66
- process.on("exit", this.closeSignalHandler as any)
67
- process.on("SIGINT", this.closeSignalHandler as any)
68
- process.on("SIGTERM", this.closeSignalHandler as any)
69
-
70
- return this.server
71
- }
72
-
73
- hasTapeBeenUsed(tapeName: string) {
74
- return this.tapeStore.hasTapeBeenUsed(tapeName)
75
- }
76
-
77
- resetTapeUsage() {
78
- this.tapeStore.resetTapeUsage()
79
- }
80
-
81
- close(callback?: () => void) {
82
- if (this.closed) {
83
- return
84
- }
85
- this.closed = true
86
- this.server!.close(callback)
87
-
88
- // @ts-ignore
89
- process.removeListener("exit", this.closeSignalHandler as any)
90
- // @ts-ignore
91
- process.removeListener("SIGINT", this.closeSignalHandler as any)
92
- // @ts-ignore
93
- process.removeListener("SIGTERM", this.closeSignalHandler as any)
94
-
95
- if (this.options.summary) {
96
- const summary = new Summary(this.tapeStore.tapes, this.options)
97
- summary.print()
98
- }
99
- }
100
- }
@@ -1,26 +0,0 @@
1
- import Tape from './tape.backend';
2
- import {Options} from './options.backend';
3
-
4
- export default class Summary {
5
- private tapes: Tape[]
6
- private opts: Options
7
-
8
- constructor(tapes: Tape[], opts: Options) {
9
- this.tapes = tapes
10
- this.opts = opts
11
- }
12
-
13
- print() {
14
- console.log(`===== SUMMARY (${this.opts.name}) =====`)
15
- const newTapes = this.tapes.filter(t => t.new)
16
- if (newTapes.length > 0) {
17
- console.log("New tapes:")
18
- newTapes.forEach(t => console.log(`- ${t.path}`))
19
- }
20
- const unusedTapes = this.tapes.filter(t => !t.used)
21
- if (unusedTapes.length > 0) {
22
- console.log("Unused tapes:")
23
- unusedTapes.forEach(t => console.log(`- ${t.path}`))
24
- }
25
- }
26
- }
@@ -1,18 +0,0 @@
1
- import Options from './options.backend';
2
- import TapeStore from './tape-store.backend';
3
- import TalkbackServer from './server.backend';
4
- import RequestHandler from './request-handler.backend';
5
-
6
- export default class TalkbackFactory {
7
- static server(options: Partial<Options>) {
8
- const fullOptions = Options.prepare(options)
9
- return new TalkbackServer(fullOptions)
10
- }
11
-
12
- static async requestHandler(options: Partial<Options>) {
13
- const fullOptions = Options.prepare(options)
14
- const tapeStore = new TapeStore(fullOptions)
15
- await tapeStore.load()
16
- return new RequestHandler(tapeStore, fullOptions)
17
- }
18
- }
@@ -1,113 +0,0 @@
1
- import ContentEncoding from './utils/content-encoding.backend';
2
- import MediaType from './utils/media-type.backend';
3
- import { Options } from './options.backend';
4
- import Tape from './tape.backend';
5
- import { Req } from './types.backend';
6
- import { ___NS__add, ___NS__after, ___NS__ary, ___NS__assign, ___NS__assignIn, ___NS__assignInWith, ___NS__assignWith, ___NS__at, ___NS__attempt, ___NS__before, ___NS__bind, ___NS__bindAll, ___NS__bindKey, ___NS__camelCase, ___NS__capitalize, ___NS__castArray, ___NS__ceil, ___NS__chain, ___NS__chunk, ___NS__clamp, ___NS__clone, ___NS__cloneDeep, ___NS__cloneDeepWith, ___NS__cloneWith, ___NS__compact, ___NS__concat, ___NS__cond, ___NS__conforms, ___NS__conformsTo, ___NS__constant, ___NS__countBy, ___NS__create, ___NS__curry, ___NS__curryRight, ___NS__debounce, ___NS__deburr, ___NS__defaults, ___NS__defaultsDeep, ___NS__defaultTo, ___NS__defer, ___NS__delay, ___NS__difference, ___NS__differenceBy, ___NS__differenceWith, ___NS__divide, ___NS__drop, ___NS__dropRight, ___NS__dropRightWhile, ___NS__dropWhile, ___NS__each, ___NS__eachRight, ___NS__endsWith, ___NS__entries, ___NS__entriesIn, ___NS__eq, ___NS__escape, ___NS__escapeRegExp, ___NS__every, ___NS__extend, ___NS__extendWith, ___NS__fill, ___NS__filter, ___NS__find, ___NS__findIndex, ___NS__findKey, ___NS__findLast, ___NS__findLastIndex, ___NS__findLastKey, ___NS__first, ___NS__flatMap, ___NS__flatMapDeep, ___NS__flatMapDepth, ___NS__flatten, ___NS__flattenDeep, ___NS__flattenDepth, ___NS__flip, ___NS__floor, ___NS__flow, ___NS__flowRight, ___NS__forEach, ___NS__forEachRight, ___NS__forIn, ___NS__forInRight, ___NS__forOwn, ___NS__forOwnRight, ___NS__fromPairs, ___NS__functions, ___NS__functionsIn, ___NS__get, ___NS__groupBy, ___NS__gt, ___NS__gte, ___NS__has, ___NS__hasIn, ___NS__head, ___NS__identity, ___NS__includes, ___NS__indexOf, ___NS__initial, ___NS__inRange, ___NS__intersection, ___NS__intersectionBy, ___NS__intersectionWith, ___NS__invert, ___NS__invertBy, ___NS__invoke, ___NS__invokeMap, ___NS__isArguments, ___NS__isArray, ___NS__isArrayBuffer, ___NS__isArrayLike, ___NS__isArrayLikeObject, ___NS__isBoolean, ___NS__isBuffer, ___NS__isDate, ___NS__isElement, ___NS__isEmpty, ___NS__isEqual, ___NS__isEqualWith, ___NS__isError, ___NS__isFinite, ___NS__isFunction, ___NS__isInteger, ___NS__isLength, ___NS__isMap, ___NS__isMatch, ___NS__isMatchWith, ___NS__isNaN, ___NS__isNative, ___NS__isNil, ___NS__isNull, ___NS__isNumber, ___NS__isObject, ___NS__isObjectLike, ___NS__isPlainObject, ___NS__isRegExp, ___NS__isSafeInteger, ___NS__isSet, ___NS__isString, ___NS__isSymbol, ___NS__isTypedArray, ___NS__isUndefined, ___NS__isWeakMap, ___NS__isWeakSet, ___NS__iteratee, ___NS__join, ___NS__kebabCase, ___NS__keyBy, ___NS__keys, ___NS__keysIn, ___NS__last, ___NS__lastIndexOf, ___NS__lowerCase, ___NS__lowerFirst, ___NS__lt, ___NS__lte, ___NS__map, ___NS__mapKeys, ___NS__mapValues, ___NS__matches, ___NS__matchesProperty, ___NS__max, ___NS__maxBy, ___NS__mean, ___NS__meanBy, ___NS__memoize, ___NS__merge, ___NS__mergeWith, ___NS__method, ___NS__methodOf, ___NS__min, ___NS__minBy, ___NS__mixin, ___NS__multiply, ___NS__negate, ___NS__noop, ___NS__now, ___NS__nth, ___NS__nthArg, ___NS__omit, ___NS__omitBy, ___NS__once, ___NS__orderBy, ___NS__over, ___NS__overArgs, ___NS__overEvery, ___NS__overSome, ___NS__pad, ___NS__padEnd, ___NS__padStart, ___NS__parseInt, ___NS__partial, ___NS__partialRight, ___NS__partition, ___NS__pick, ___NS__pickBy, ___NS__property, ___NS__propertyOf, ___NS__pull, ___NS__pullAll, ___NS__pullAllBy, ___NS__pullAllWith, ___NS__pullAt, ___NS__random, ___NS__range, ___NS__rangeRight, ___NS__rearg, ___NS__reduce, ___NS__reduceRight, ___NS__reject, ___NS__remove, ___NS__repeat, ___NS__replace, ___NS__rest, ___NS__result, ___NS__reverse, ___NS__round, ___NS__sample, ___NS__sampleSize, ___NS__set, ___NS__setWith, ___NS__shuffle, ___NS__size, ___NS__slice, ___NS__snakeCase, ___NS__some, ___NS__sortBy, ___NS__sortedIndex, ___NS__sortedIndexBy, ___NS__sortedIndexOf, ___NS__sortedLastIndex, ___NS__sortedLastIndexBy, ___NS__sortedLastIndexOf, ___NS__sortedUniq, ___NS__sortedUniqBy, ___NS__split, ___NS__spread, ___NS__startCase, ___NS__startsWith, ___NS__stubArray, ___NS__stubFalse, ___NS__stubObject, ___NS__stubString, ___NS__stubTrue, ___NS__subtract, ___NS__sum, ___NS__sumBy, ___NS__tail, ___NS__take, ___NS__takeRight, ___NS__takeRightWhile, ___NS__takeWhile, ___NS__tap, ___NS__template, ___NS__templateSettings, ___NS__throttle, ___NS__thru, ___NS__times, ___NS__toArray, ___NS__toFinite, ___NS__toInteger, ___NS__toLength, ___NS__toLower, ___NS__toNumber, ___NS__toPairs, ___NS__toPairsIn, ___NS__toPath, ___NS__toPlainObject, ___NS__toSafeInteger, ___NS__toString, ___NS__toUpper, ___NS__transform, ___NS__trim, ___NS__trimEnd, ___NS__trimStart, ___NS__truncate, ___NS__unary, ___NS__unescape, ___NS__union, ___NS__unionBy, ___NS__unionWith, ___NS__uniq, ___NS__uniqBy, ___NS__uniqueId, ___NS__uniqWith, ___NS__unset, ___NS__unzip, ___NS__unzipWith, ___NS__update, ___NS__updateWith, ___NS__upperCase, ___NS__upperFirst, ___NS__values, ___NS__valuesIn, ___NS__without, ___NS__words, ___NS__wrap, ___NS__xor, ___NS__xorBy, ___NS__xorWith, ___NS__zip, ___NS__zipObject, ___NS__zipObjectDeep, ___NS__zipWith } from 'tnp-core/lib-prod';
7
-
8
- export default class TapeMatcher {
9
- private readonly tape: Tape
10
- private readonly options: Options
11
-
12
- constructor(tape: Tape, options: Options) {
13
- this.tape = tape
14
- this.options = options
15
- }
16
-
17
- sameAs(otherTape: Tape) {
18
- const otherReq = otherTape.req
19
- const req = this.tape.req
20
- if (!this.isSameUrl(req, otherReq)) {
21
- return false
22
- }
23
-
24
- if (!this.isSameMethod(req, otherReq)) {
25
- return false
26
- }
27
-
28
- if (!this.isSameHeaders(req, otherReq)) {
29
- return false
30
- }
31
-
32
- return this.options.ignoreBody || this.isSameBody(req, otherReq);
33
-
34
- }
35
-
36
- private isSameBody(req: Req, otherReq: Req) {
37
- const mediaType = new MediaType(req)
38
- const contentEncoding = new ContentEncoding(req)
39
-
40
-
41
- let sameBody: boolean
42
- if (contentEncoding.isUncompressed() && mediaType.isJSON() && req.body.length > 0 && otherReq.body.length > 0) {
43
- const parsedReqBody = JSON.parse(req.body.toString())
44
- const parsedOtherReqBody = JSON.parse(otherReq.body.toString())
45
- sameBody = ___NS__isEqual(parsedReqBody, parsedOtherReqBody)
46
- } else {
47
- sameBody = req.body.equals(otherReq.body)
48
- }
49
-
50
- if (!sameBody) {
51
- if (!this.options.bodyMatcher) {
52
- this.options.logger.debug(`Not same BODY ${req.body} vs ${otherReq.body}`)
53
- return false
54
- }
55
-
56
- const bodyMatches = this.options.bodyMatcher(this.tape, otherReq)
57
- if (!bodyMatches) {
58
- this.options.logger.debug(`Not same bodyMatcher ${req.body} vs ${otherReq.body}`)
59
- return false
60
- }
61
- }
62
- return true
63
- }
64
-
65
- private isSameHeaders(req: Req, otherReq: Req) {
66
- const currentHeadersLength = Object.keys(req.headers).length
67
- const otherHeadersLength = Object.keys(otherReq.headers).length
68
- const sameNumberOfHeaders = currentHeadersLength === otherHeadersLength
69
- if (!sameNumberOfHeaders) {
70
- this.options.logger.debug(`Not same #HEADERS ${JSON.stringify(req.headers)} vs ${JSON.stringify(otherReq.headers)}`)
71
- return false
72
- }
73
-
74
- let headersSame = true
75
- Object.keys(req.headers).forEach(k => {
76
- const entryHeader = req.headers[k]
77
- const header = otherReq.headers[k]
78
-
79
- headersSame = headersSame && entryHeader === header
80
- })
81
- if (!headersSame) {
82
- this.options.logger.debug(`Not same HEADERS values ${JSON.stringify(req.headers)} vs ${JSON.stringify(otherReq.headers)}`)
83
- return false
84
- }
85
- return true
86
- }
87
-
88
- private isSameMethod(req: Req, otherReq: Req) {
89
- const sameMethod = req.method === otherReq.method
90
- if (!sameMethod) {
91
- this.options.logger.debug(`Not same METHOD ${req.method} vs ${otherReq.method}`)
92
- return false
93
- }
94
- return true
95
- }
96
-
97
- private isSameUrl(req: Req, otherReq: Req) {
98
- const sameURL = req.url === otherReq.url
99
- if (!sameURL) {
100
- if (!this.options.urlMatcher) {
101
- this.options.logger.debug(`Not same URL ${req.url} vs ${otherReq.url}`)
102
- return false
103
- }
104
-
105
- const urlMatches = this.options.urlMatcher(this.tape, otherReq)
106
- if (!urlMatches) {
107
- this.options.logger.debug(`Not same urlMatcher ${req.url} vs ${otherReq.url}`)
108
- return false
109
- }
110
- }
111
- return true
112
- }
113
- }
@@ -1,118 +0,0 @@
1
- import Headers from './utils/headers.backend';
2
- import MediaType from './utils/media-type.backend';
3
- import Tape from './tape.backend';
4
- import ContentEncoding from './utils/content-encoding.backend';
5
- import { Options } from './options.backend';
6
- import { ReqRes } from './types.backend';
7
- import * as _ from 'lodash';
8
- import * as bufferShim from 'buffer-shims';
9
- // const bufferShim = require('buffer-shims')/
10
-
11
- export default class TapeRenderer {
12
- private tape: Tape
13
-
14
- constructor(tape: Tape) {
15
- this.tape = tape
16
- }
17
-
18
- static async fromStore(raw: any, options: Options) {
19
-
20
- const req = { ...raw.req }
21
-
22
- req.body = await this.prepareBody(raw, req, req.body, "req")
23
-
24
- const tape = new Tape(req, options)
25
- tape.meta = { ...raw.meta }
26
- const baseRes = { ...raw.res }
27
- const resBody = await this.prepareBody(tape, baseRes, baseRes.body, "res")
28
-
29
- tape.res = {
30
- ...baseRes,
31
- body: resBody
32
- }
33
-
34
- return tape
35
- }
36
-
37
- static async prepareBody(tape: Tape, reqResObj: ReqRes, rawBody: string, metaPrefix: "res" | "req") {
38
- const contentEncoding = new ContentEncoding(reqResObj)
39
- const isTapeUncompressed = (tape.meta as any)[metaPrefix + "Uncompressed"]
40
- const isTapeHumanReadable = (tape.meta as any)[metaPrefix + "HumanReadable"]
41
- const isTapeInPlainText = isTapeUncompressed || contentEncoding.isUncompressed()
42
-
43
- if (isTapeHumanReadable && isTapeInPlainText) {
44
- const mediaType = new MediaType(reqResObj)
45
- let bufferContent = rawBody
46
- const isResAnObject = typeof (bufferContent) === "object"
47
-
48
- if (isResAnObject && mediaType.isJSON()) {
49
- bufferContent = JSON.stringify(bufferContent, null, 2)
50
- }
51
-
52
- if (Headers.read(reqResObj.headers, "content-length")) {
53
- Headers.write(reqResObj.headers, "content-length", Buffer.byteLength(bufferContent).toString(), metaPrefix)
54
- }
55
-
56
- if (isTapeUncompressed) {
57
- return await contentEncoding.compressedBody(bufferContent)
58
- }
59
-
60
- return bufferShim.from(bufferContent)
61
- } else {
62
- return bufferShim.from(rawBody, "base64")
63
- }
64
- }
65
-
66
- async render() {
67
- const reqBody = await this.bodyFor(this.tape.req, "req")
68
- const resBody = await this.bodyFor(this.tape.res!, "res")
69
- return {
70
- meta: this.tape.meta,
71
- req: {
72
- ...this.tape.req,
73
- body: reqBody
74
- },
75
- res: {
76
- ...this.tape.res,
77
- body: resBody
78
- }
79
- }
80
- }
81
-
82
- async bodyFor(reqResObj: ReqRes, metaPrefix: "req" | "res") {
83
- const mediaType = new MediaType(reqResObj)
84
- const contentEncoding = new ContentEncoding(reqResObj)
85
- const bodyLength = reqResObj.body.length
86
-
87
-
88
- const isUncompressed = contentEncoding.isUncompressed()
89
- const contentEncodingSupported = isUncompressed || contentEncoding.supportedAlgorithm()
90
-
91
- if (mediaType.isHumanReadable() && contentEncodingSupported && bodyLength > 0) {
92
- (this.tape.meta as any)[metaPrefix + "HumanReadable"] = true
93
-
94
- let body = reqResObj.body
95
-
96
- if (!isUncompressed) {
97
- (this.tape.meta as any)[metaPrefix + "Uncompressed"] = true
98
- body = await contentEncoding.uncompressedBody(body)
99
- }
100
-
101
- const rawBody = body.toString("utf8")
102
-
103
- if (mediaType.isJSON()) {
104
- try { // TODO handle this better in future not based on mediaType.isJSON
105
- const parsed = JSON.parse(rawBody);
106
- return parsed;
107
- } catch (error) {
108
- return rawBody;
109
- }
110
- } else {
111
- return rawBody;
112
- }
113
- } else {
114
- return reqResObj.body.toString("base64")
115
- }
116
- }
117
-
118
- }
@@ -1,111 +0,0 @@
1
- import { Options } from './options.backend';
2
- import { fse, path, json5, mkdirp } from 'tnp-core/lib-prod';
3
-
4
- import Tape from './tape.backend';
5
- import TapeMatcher from './tape-matcher.backend';
6
- import TapeRenderer from './tape-renderer.backend';
7
-
8
- export default class TapeStore {
9
- private readonly path: string
10
- private readonly options: Options
11
- tapes: Tape[]
12
-
13
- constructor(options: Options) {
14
- this.path = path.normalize(options.path + "/")
15
- this.options = options
16
- this.tapes = []
17
- }
18
-
19
- async load() {
20
- mkdirp.sync(this.path)
21
-
22
- await this.loadTapesAtDir(this.path)
23
- console.log(`Loaded ${this.tapes.length} tapes`)
24
- }
25
-
26
- async loadTapesAtDir(directory: string) {
27
- const items = fse.readdirSync(directory) as string[]
28
- for (let i = 0; i < items.length; i++) {
29
- const filename = items[i]
30
- const fullPath = `${directory}${filename}`
31
- const stat = fse.statSync(fullPath)
32
- if (!stat.isDirectory()) {
33
- try {
34
- const data = fse.readFileSync(fullPath, "utf8")
35
- const raw = json5.parse(data)
36
- const tape = await Tape.fromStore(raw, this.options)
37
- tape.path = filename
38
- this.tapes.push(tape)
39
- } catch (e) {
40
- console.log(`Error reading tape ${fullPath}`, e.message)
41
- }
42
- } else {
43
- this.loadTapesAtDir(fullPath + "/")
44
- }
45
- }
46
- }
47
-
48
- find(newTape: Tape) {
49
- const foundTape = this.tapes.find(t => {
50
- this.options.logger.debug(`Comparing against tape ${t.path}`)
51
- return new TapeMatcher(t, this.options).sameAs(newTape)
52
- })
53
-
54
- if (foundTape) {
55
- foundTape.used = true
56
- this.options.logger.log(`Found matching tape for ${newTape.req.url} at ${foundTape.path}`)
57
- return foundTape
58
- }
59
- }
60
-
61
- async save(tape: Tape) {
62
- tape.new = true
63
- tape.used = true
64
-
65
- const tapePath = tape.path
66
- let fullFilename
67
-
68
- if (tapePath) {
69
- fullFilename = path.join(this.path, tapePath)
70
- } else {
71
- // If the tape doesn't have a path then it's new
72
- this.tapes.push(tape)
73
-
74
- fullFilename = this.createTapePath(tape)
75
- tape.path = path.relative(this.path, fullFilename)
76
- }
77
- this.options.logger.log(`Saving request ${tape.req.url} at ${tape.path}`)
78
-
79
- const tapeRenderer = new TapeRenderer(tape)
80
- const toSave = await tapeRenderer.render()
81
- fse.writeFileSync(fullFilename, json5.stringify(toSave, null, 4))
82
- }
83
-
84
- currentTapeId() {
85
- return this.tapes.length
86
- }
87
-
88
- hasTapeBeenUsed(tapeName: string) {
89
- return this.tapes.some(t => t.used && t.path === tapeName)
90
- }
91
-
92
- resetTapeUsage() {
93
- return this.tapes.forEach(t => t.used = false)
94
- }
95
-
96
- createTapePath(tape: Tape) {
97
- const currentTapeId = this.currentTapeId()
98
- let tapePath = `unnamed-${currentTapeId}.json5`
99
- if (this.options.tapeNameGenerator) {
100
- tapePath = this.options.tapeNameGenerator(currentTapeId, tape)
101
- }
102
- let result = path.normalize(path.join(this.options.path, tapePath))
103
- if (!result.endsWith(".json5")) {
104
- result = `${result}.json5`
105
- }
106
- const dir = path.dirname(result)
107
- mkdirp.sync(dir)
108
-
109
- return result
110
- }
111
- }