diff --git a/bdd/data/sequences/bin-out-seq/dist/index.js b/bdd/data/sequences/bin-out-seq/dist/index.js new file mode 100644 index 000000000..f693951b0 --- /dev/null +++ b/bdd/data/sequences/bin-out-seq/dist/index.js @@ -0,0 +1,73 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +exports.__esModule = true; +var fs_1 = require("fs"); +var stream_1 = require("stream"); +var crypto = require("crypto"); +var mod = [ + function (_stream, filePath) { + if (filePath === void 0) { filePath = "".concat(__dirname, "/random.bin"); } + return __awaiter(this, void 0, void 0, function () { + var ps, stream, hash_1; + var _this = this; + return __generator(this, function (_a) { + this.logger.info("Sequence started"); + try { + ps = new stream_1.PassThrough({ encoding: "binary" }); + stream = (0, fs_1.createReadStream)(filePath); + hash_1 = crypto.createHash("sha256"); + stream.pipe(ps); + stream.on("data", function (data) { + hash_1.update(data); + }); + stream.on("end", function () { + var checksum = hash_1.digest("hex"); + console.log(checksum); + _this.logger.info("".concat(filePath, " checksum written to stdout: ").concat(checksum)); + }); + return [2 /*return*/, stream]; + } + catch (e) { + this.logger.error(e); + } + return [2 /*return*/]; + }); + }); + } +]; +exports["default"] = mod; diff --git a/bdd/data/sequences/bin-out-seq/dist/node_modules/.package-lock.json b/bdd/data/sequences/bin-out-seq/dist/node_modules/.package-lock.json new file mode 100644 index 000000000..b59aeb399 --- /dev/null +++ b/bdd/data/sequences/bin-out-seq/dist/node_modules/.package-lock.json @@ -0,0 +1,7 @@ +{ + "name": "@scramjet/bin-out-seq", + "version": "0.23.2", + "lockfileVersion": 2, + "requires": true, + "packages": {} +} diff --git a/bdd/data/sequences/bin-out-seq/dist/package-lock.json b/bdd/data/sequences/bin-out-seq/dist/package-lock.json new file mode 100644 index 000000000..5f6ae8441 --- /dev/null +++ b/bdd/data/sequences/bin-out-seq/dist/package-lock.json @@ -0,0 +1,75 @@ +{ + "name": "@scramjet/bin-out-seq", + "version": "0.23.2", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@scramjet/bin-out-seq", + "version": "0.23.2", + "license": "ISC", + "devDependencies": { + "@scramjet/types": "^0.38.0", + "@types/node": "15.12.5" + } + }, + "node_modules/@scramjet/symbols": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@scramjet/symbols/-/symbols-0.38.0.tgz", + "integrity": "sha512-Wjg0JUFybp++ngDUovkoBb3IrfVAjo3OeK6d7JodCOVlgrPsOs2jvmINhGhTfbB/WZx0/sRHeG6Yr9zmpDY3Cg==", + "dev": true + }, + "node_modules/@scramjet/types": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@scramjet/types/-/types-0.38.0.tgz", + "integrity": "sha512-6PlWPgdP5ot65Zr17d01rf9m6rgL9xgsliFw2yhikEPflLV8c2ohSGSsptR+faM+x+pNyN/O9TnxbSzwwWBNVA==", + "dev": true, + "dependencies": { + "@scramjet/symbols": "^0.38.0", + "http-status-codes": "^2.2.0" + } + }, + "node_modules/@types/node": { + "version": "15.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.5.tgz", + "integrity": "sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==", + "dev": true + }, + "node_modules/http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "dev": true + } + }, + "dependencies": { + "@scramjet/symbols": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@scramjet/symbols/-/symbols-0.38.0.tgz", + "integrity": "sha512-Wjg0JUFybp++ngDUovkoBb3IrfVAjo3OeK6d7JodCOVlgrPsOs2jvmINhGhTfbB/WZx0/sRHeG6Yr9zmpDY3Cg==", + "dev": true + }, + "@scramjet/types": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@scramjet/types/-/types-0.38.0.tgz", + "integrity": "sha512-6PlWPgdP5ot65Zr17d01rf9m6rgL9xgsliFw2yhikEPflLV8c2ohSGSsptR+faM+x+pNyN/O9TnxbSzwwWBNVA==", + "dev": true, + "requires": { + "@scramjet/symbols": "^0.38.0", + "http-status-codes": "^2.2.0" + } + }, + "@types/node": { + "version": "15.12.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.5.tgz", + "integrity": "sha512-se3yX7UHv5Bscf8f1ERKvQOD6sTyycH3hdaoozvaLxgUiY5lIGEeH37AD0G0Qi9kPqihPn0HOfd2yaIEN9VwEg==", + "dev": true + }, + "http-status-codes": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", + "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", + "dev": true + } + } +} diff --git a/bdd/data/sequences/bin-out-seq/dist/package.json b/bdd/data/sequences/bin-out-seq/dist/package.json new file mode 100644 index 000000000..aac67fd2f --- /dev/null +++ b/bdd/data/sequences/bin-out-seq/dist/package.json @@ -0,0 +1,23 @@ +{ + "name": "@scramjet/bin-out-seq", + "version": "0.23.2", + "description": "A Sequence that reads binary file and writes data to output.", + "main": "index.js", + "scripts": { + "build": "tsc -p tsconfig.json", + "postbuild": "cp -r package.json random.bin dist/ && (cd dist && npm i --only=production)" + }, + "author": "Scramjet ", + "license": "ISC", + "devDependencies": { + "@scramjet/types": "^0.38.0", + "@types/node": "15.12.5" + }, + "repository": { + "type": "git", + "url": "https://github.com/scramjetorg/transform-hub.git" + }, + "assets": [ + "random.bin" + ] +} diff --git a/bdd/data/sequences/bin-out-seq/dist/random.bin b/bdd/data/sequences/bin-out-seq/dist/random.bin new file mode 100644 index 000000000..1266ffd0d Binary files /dev/null and b/bdd/data/sequences/bin-out-seq/dist/random.bin differ diff --git a/bdd/data/sequences/bin-out-seq/index.ts b/bdd/data/sequences/bin-out-seq/index.ts new file mode 100644 index 000000000..6ff0a6df6 --- /dev/null +++ b/bdd/data/sequences/bin-out-seq/index.ts @@ -0,0 +1,37 @@ +/* eslint-disable consistent-return */ +/* eslint-disable no-console */ +import { AppConfig, AppContext } from "@scramjet/types"; +import { createReadStream } from "fs"; +import { PassThrough } from "stream"; +import * as crypto from "crypto"; + +const mod = [ + async function(this: AppContext, _stream, filePath: string = `${__dirname}/random.bin`) { + this.logger.info("Sequence started"); + + try { + const ps = new PassThrough({ encoding: "binary" }); + const stream = createReadStream(filePath); + const hash = crypto.createHash("sha256"); + + stream.pipe(ps); + + stream.on("data", (data) => { + hash.update(data); + }); + + stream.on("end", () => { + const checksum = hash.digest("hex"); + + console.log(checksum); + this.logger.info(`${filePath} checksum written to stdout: ${checksum}`); + }); + + return stream; + } catch (e) { + this.logger.error(e); + } + } +]; + +export default mod; diff --git a/bdd/data/sequences/bin-out-seq/package.json b/bdd/data/sequences/bin-out-seq/package.json new file mode 100644 index 000000000..aac67fd2f --- /dev/null +++ b/bdd/data/sequences/bin-out-seq/package.json @@ -0,0 +1,23 @@ +{ + "name": "@scramjet/bin-out-seq", + "version": "0.23.2", + "description": "A Sequence that reads binary file and writes data to output.", + "main": "index.js", + "scripts": { + "build": "tsc -p tsconfig.json", + "postbuild": "cp -r package.json random.bin dist/ && (cd dist && npm i --only=production)" + }, + "author": "Scramjet ", + "license": "ISC", + "devDependencies": { + "@scramjet/types": "^0.38.0", + "@types/node": "15.12.5" + }, + "repository": { + "type": "git", + "url": "https://github.com/scramjetorg/transform-hub.git" + }, + "assets": [ + "random.bin" + ] +} diff --git a/bdd/data/sequences/bin-out-seq/random.bin b/bdd/data/sequences/bin-out-seq/random.bin new file mode 100644 index 000000000..1266ffd0d Binary files /dev/null and b/bdd/data/sequences/bin-out-seq/random.bin differ diff --git a/bdd/data/sequences/bin-out-seq/tsconfig.json b/bdd/data/sequences/bin-out-seq/tsconfig.json new file mode 100644 index 000000000..a92b56a8d --- /dev/null +++ b/bdd/data/sequences/bin-out-seq/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "outDir": "./dist" + }, + "include": [ + "./index.ts" + ] +} diff --git a/bdd/features/performance-tests/PT-004-checksum.feature b/bdd/features/performance-tests/PT-004-checksum.feature index dabb6edea..6cfb2dcff 100644 --- a/bdd/features/performance-tests/PT-004-checksum.feature +++ b/bdd/features/performance-tests/PT-004-checksum.feature @@ -12,3 +12,16 @@ Feature: Verify the checksums of payloads are correct When runner has ended execution Then host is still running + @ci-performance + Scenario: PT-004 TC-002 Checksum of binary payload + Given file in the location "data/sequences/bin-out-seq/random.bin" exists on hard drive + And host is running + When I execute CLI with "seq pack data/sequences/bin-out-seq/dist -o data/sequences/bin-out-seq.tar.gz" + When sequence "data/sequences/bin-out-seq.tar.gz" loaded + And instance started + When wait for instance healthy is "true" + And get runner PID + And confirm file checksum match output checksum + When runner has ended execution + Then host is still running + diff --git a/bdd/step-definitions/e2e/host-steps.ts b/bdd/step-definitions/e2e/host-steps.ts index 5fc996f6b..a0c050ef6 100644 --- a/bdd/step-definitions/e2e/host-steps.ts +++ b/bdd/step-definitions/e2e/host-steps.ts @@ -20,7 +20,7 @@ import fs, { createReadStream, existsSync, ReadStream } from "fs"; import { HostClient, InstanceOutputStream } from "@scramjet/api-client"; import { HostUtils } from "../../lib/host-utils"; import { PassThrough, Readable, Stream, Writable } from "stream"; -import crypto from "crypto"; +import crypto, { BinaryLike } from "crypto"; import { promisify } from "util"; import Dockerode from "dockerode"; import { CustomWorld } from "../world"; @@ -67,6 +67,25 @@ const streamToString = async (stream: Stream): Promise => { return chunks.join(""); }; +const streamToBinary = async (stream: Readable): Promise => { + const chunks: Uint8Array[] = []; + + return new Promise((resolve, reject) => { + stream.on("data", (chunk: Buffer | Uint8Array) => { + chunks.push(chunk instanceof Buffer ? chunk : Uint8Array.from(chunk)); + }); + + stream.on("end", () => { + const binaryData = Buffer.concat(chunks); + + resolve(binaryData); + }); + + stream.on("error", (error: Error) => { + reject(error); + }); + }); +}; const waitForContainerToClose = async () => { if (!containerId) assert.fail(); @@ -505,6 +524,23 @@ When("compare checksums of content sent from file {string}", async function(this await this.resources.instance?.sendInput("null"); }); +When("confirm file checksum match output checksum", async function(this: CustomWorld) { + // the random.bin hex is written to instance stdout + const stdout = await this.resources.instance!.getStream("stdout"); + const fileHexFromStdout = await streamToString(stdout); + const output = await this.resources.instance?.getStream("output"); + + if (!output || !stdout) assert.fail("No output or stdout, or both."); + + const dataFromOutput = await streamToBinary(output); + const outputHex: string = crypto + .createHash("sha256") + .update(dataFromOutput) + .digest("hex"); + + assert.strictEqual(outputHex, fileHexFromStdout.trim()); +}); + When( "send stop message to instance with arguments timeout {int} and canCallKeepAlive {string}", async function(this: CustomWorld, timeout: number, canCallKeepalive: string) { diff --git a/packages/api-server/src/handlers/stream.ts b/packages/api-server/src/handlers/stream.ts index 967dbb518..bd3e6a73f 100644 --- a/packages/api-server/src/handlers/stream.ts +++ b/packages/api-server/src/handlers/stream.ts @@ -67,7 +67,7 @@ export function createStreamHandlers(router: SequentialCeroRouter) { ? `${type}; charset=${encoding}` : type; - out.setEncoding(encoding); + logger.debug("encoding, cType, readableEncoding", encoding, cType, data.readableEncoding); res.setHeader("content-type", cType); res.setHeader("transfer-encoding", "chunked"); diff --git a/packages/client-utils/src/types/index.ts b/packages/client-utils/src/types/index.ts index 9312ec121..3927e9b32 100644 --- a/packages/client-utils/src/types/index.ts +++ b/packages/client-utils/src/types/index.ts @@ -15,6 +15,7 @@ export type SendStreamOptions = Partial<{ */ export type GetStreamOptions = Partial<{ type: string; + encoding?: BufferEncoding; }>; /** diff --git a/packages/host/src/lib/csi-controller.ts b/packages/host/src/lib/csi-controller.ts index 7bff15a3d..9924262f6 100644 --- a/packages/host/src/lib/csi-controller.ts +++ b/packages/host/src/lib/csi-controller.ts @@ -103,8 +103,10 @@ export class CSIController extends TypedEmitter { info: CSIControllerInfo = {}; status: InstanceStatus; terminated?: { exitcode: number; reason: string; }; + provides?: string; requires?: string; + outputEncoding: BufferEncoding = "utf-8"; initResolver?: { res: Function; rej: Function }; heartBeatResolver?: { res: Function; rej: Function }; @@ -440,6 +442,9 @@ export class CSIController extends TypedEmitter { this.apiInputEnabled = false; } + this.outputEncoding = pangData.outputEncoding || "utf-8"; + //this.upStreams[CC.OUT].setDefaultEncoding(pangData.outputEncoding || "utf-8"); + this.emit("pang", { provides: this.provides, requires: this.requires, @@ -595,7 +600,8 @@ export class CSIController extends TypedEmitter { if (development()) { this.router.upstream("/monitoring", this.upStreams[CC.MONITORING]); } - this.router.upstream("/output", this.upStreams[CC.OUT]); + + this.router.upstream("/output", this.upStreams[CC.OUT], { encoding: this.outputEncoding }); this.router.downstream("/input", (req) => { if (this.apiInputEnabled) { diff --git a/packages/host/src/lib/host.ts b/packages/host/src/lib/host.ts index 48288fdcb..bb6c7e7b8 100644 --- a/packages/host/src/lib/host.ts +++ b/packages/host/src/lib/host.ts @@ -733,19 +733,27 @@ export class Host implements IComponent { spaceMiddleware(req: ParsedMessage, res: ServerResponse) { const url = req.url!.replace(`${this.apiBase}/cpm/api/v1/`, ""); - this.logger.info("SPACE REQUEST", req.url, url, this.apiBase, req.body); + this.logger.info("SPACE REQUEST", req.url, url, this.apiBase); const clientRequest = this.cpmConnector?.makeHttpRequestToCpm(req.method!, url, req.headers); if (clientRequest) { - clientRequest.on("response", (response: IncomingMessage) => { - response.pipe(res); - }).on("error", (error) => { - this.logger.error("Error requesting CPM", error); - }); + clientRequest.on("socket", (socket) => { + clientRequest.on("response", (response: IncomingMessage) => { + response.on("end", () => { + this.logger.info("Space response ended", url, response.statusCode); + }); + + res.writeHead(response.statusCode!, response.statusMessage || "", response.headers); - clientRequest.flushHeaders(); - req.pipe(clientRequest); + socket.pipe(res.socket!); + }).on("error", (error) => { + this.logger.error("Error requesting CPM", error); + }); + + clientRequest.flushHeaders(); + req.socket.pipe(socket); + }); } else { res.statusCode = 404; res.end(); diff --git a/packages/host/src/lib/socket-server.ts b/packages/host/src/lib/socket-server.ts index 400d5d1c9..e3ed0aff6 100644 --- a/packages/host/src/lib/socket-server.ts +++ b/packages/host/src/lib/socket-server.ts @@ -4,6 +4,7 @@ import net from "net"; import { isDefined, TypedEmitter } from "@scramjet/utility"; import { ObjLogger } from "@scramjet/obj-logger"; +import { CommunicationChannel } from "@scramjet/symbols"; type MaybeSocket = net.Socket | null type RunnerConnectionsInProgress = [ @@ -64,7 +65,9 @@ export class SocketServer extends TypedEmitter implements IComponent { connection .on("error", (err) => this.logger.error("Error on Instance in stream", id, channel, err)) - .on("end", () => this.logger.debug(`Channel [${id}:${channel}] ended`)); + .on("end", () => this.logger.debug( + `Channel [${id}:${channel}] ended. tx/rx: ${connection.bytesWritten}/${connection.bytesRead}` + )); try { await this.handleConnection(id, channel, connection); @@ -97,8 +100,10 @@ export class SocketServer extends TypedEmitter implements IComponent { } else { throw new Error(`Runner(${id}) wanted to connect on already initialized channel ${channel}`); } + if (runner.every(isDefined)) { this.runnerConnectionsInProgress.delete(id); + runner[CommunicationChannel.OUT]?.setDefaultEncoding("binary"); this.emit("connect", id, runner as RunnerOpenConnections); } } diff --git a/packages/runner/src/host-client.ts b/packages/runner/src/host-client.ts index 93aeb2104..75f44ab23 100644 --- a/packages/runner/src/host-client.ts +++ b/packages/runner/src/host-client.ts @@ -79,6 +79,9 @@ class HostClient implements IHostClient { }); this._streams = openConnections as HostOpenConnections; + this._streams[CC.OUT].on("end", () => { + this.logger.info("Total data written to instance output", (this.streams[CC.OUT] as net.Socket).bytesWritten); + }); const input = this._streams[CC.IN]; diff --git a/packages/runner/src/runner.ts b/packages/runner/src/runner.ts index 589f8cf9b..e05c5d05f 100644 --- a/packages/runner/src/runner.ts +++ b/packages/runner/src/runner.ts @@ -772,6 +772,13 @@ export class Runner implements IComponent { this.instanceOutput instanceof StringStream || this.instanceOutput instanceof BufferStream ); + if (!this.shouldSerialize && this.instanceOutput.readableEncoding) { + this.hostClient.outputStream.setDefaultEncoding(this.instanceOutput.readableEncoding); + } + + this.logger.info("Will Output be serialized?", this.shouldSerialize); + this.logger.info("Stream encoding is", this.instanceOutput.readableEncoding); + this.instanceOutput .once("end", () => { this.logger.debug("Sequence stream ended"); @@ -786,6 +793,14 @@ export class Runner implements IComponent { this.providesContentType = intermediate.contentType || ""; this.sendPang({ provides: this.provides, contentType: this.providesContentType }); + MessageUtils.writeMessageOnStream( + [RunnerMessageCode.PANG, { + provides: intermediate.topic || "", + contentType: intermediate.contentType || "", + outputEncoding: this.instanceOutput.readableEncoding + }], + this.hostClient.monitorStream, + ); } else { // TODO: this should push a PANG message with the sequence description this.logger.debug("Sequence did not output a stream"); diff --git a/packages/types/src/messages/handshake.ts b/packages/types/src/messages/handshake.ts index 338d9c4af..627df8377 100644 --- a/packages/types/src/messages/handshake.ts +++ b/packages/types/src/messages/handshake.ts @@ -28,4 +28,5 @@ export type PangMessageData = { requires?: string; contentType?: string; provides?: string; -}; + outputEncoding?: BufferEncoding | null; +} diff --git a/packages/verser/src/lib/verser-client.ts b/packages/verser/src/lib/verser-client.ts index 568007edd..78f170259 100644 --- a/packages/verser/src/lib/verser-client.ts +++ b/packages/verser/src/lib/verser-client.ts @@ -121,6 +121,7 @@ export class VerserClient extends TypedEmitter { resolve({ res, socket }); }); + connectRequest.socket?.setNoDelay(true); connectRequest.flushHeaders(); }); } diff --git a/packages/verser/src/lib/verser.ts b/packages/verser/src/lib/verser.ts index bf694d137..28f51371d 100644 --- a/packages/verser/src/lib/verser.ts +++ b/packages/verser/src/lib/verser.ts @@ -25,6 +25,7 @@ export class Verser extends TypedEmitter { this.server = server; this.server.on("connect", (req, socket: Socket) => { + socket.setNoDelay(true); this.logger.info("New connection:", req.url); const connection = new VerserConnection(req, socket); diff --git a/yarn.lock b/yarn.lock index 0aa55e74e..161b8be6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1291,80 +1291,6 @@ resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== -"@scramjet/api-client@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/api-client/-/api-client-0.38.0.tgz#0c93d6ebe9c522dc67d073dfe6794bcdb0e6fbba" - integrity sha512-YxUHtQDPPIlmRoRsFEDviKQehSTl0Gb3l8drzUcZ8vpAZzijKqab7DTgDHwf8P1sW6AKjhUKMF0Di/Ve2J14iQ== - dependencies: - "@scramjet/client-utils" "^0.38.0" - "@scramjet/sth-config" "^0.38.0" - "@scramjet/symbols" "^0.38.0" - n-readlines "^1.0.1" - scramjet "^4.36.9" - -"@scramjet/client-utils@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/client-utils/-/client-utils-0.38.0.tgz#dfebd035617aaa192cea79a9ca93844cab143e85" - integrity sha512-DzxwgcSCEtIxdG+GsIReL1Zhkh9zKnPDIq3zbVt9Svvsq/UuP94KGg25KVelvr0OTM/yEEovJw9/yOMDWLiXbQ== - dependencies: - "@scramjet/model" "^0.38.0" - "@scramjet/obj-logger" "^0.38.0" - "@scramjet/sth-config" "^0.38.0" - "@scramjet/symbols" "^0.38.0" - "@scramjet/utility" "^0.38.0" - "@types/node-fetch" "^2.6.4" - abort-controller "^3.0.0" - n-readlines "^1.0.1" - node-fetch "^2.6.7" - normalize-url "4" - scramjet "^4.36.9" - -"@scramjet/model@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/model/-/model-0.38.0.tgz#c5bc04d346dbeba6b90ac7ec182c39877477c25d" - integrity sha512-KaIqZi6tbNnBZcc7TmvJhki+oO3k+EKUuH7h044LGatVcS5tbMrIki3w5BX41OCi5neigDCoOla/nJraod6IcQ== - dependencies: - "@scramjet/obj-logger" "^0.38.0" - "@scramjet/symbols" "^0.38.0" - scramjet "^4.36.9" - uuid "^8.3.2" - -"@scramjet/obj-logger@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/obj-logger/-/obj-logger-0.38.0.tgz#681c1aa13505dbcd0ce2bcd7141a6872e80c2367" - integrity sha512-2RLYiGnVbt1rGN74yvhqocz3PDxjYdO2Rb0GKlxYZJ2JQB/hIVAJSE47wUjKmPkG39anH6UrfGV8T73paH9qgg== - dependencies: - "@scramjet/utility" "^0.38.0" - scramjet "^4.36.9" - -"@scramjet/sth-config@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/sth-config/-/sth-config-0.38.0.tgz#b3523823b2550ba19e7d61daeda6f600e2f1601c" - integrity sha512-Glm/lXMK3UsdAwnWnINlxGm7UFaJfGCnDgjwtI8DVHOL3+52SujFbO0fNb/ye0htR77vNg6GTydbK8sEFdTbkw== - dependencies: - "@scramjet/utility" "^0.38.0" - -"@scramjet/symbols@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/symbols/-/symbols-0.38.0.tgz#0ae5f48085eff32dab8c3ed621e3e6560949bc67" - integrity sha512-Wjg0JUFybp++ngDUovkoBb3IrfVAjo3OeK6d7JodCOVlgrPsOs2jvmINhGhTfbB/WZx0/sRHeG6Yr9zmpDY3Cg== - -"@scramjet/types@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/types/-/types-0.38.0.tgz#2543bcb15c47b6e91f1d87d835c40398b30a5203" - integrity sha512-6PlWPgdP5ot65Zr17d01rf9m6rgL9xgsliFw2yhikEPflLV8c2ohSGSsptR+faM+x+pNyN/O9TnxbSzwwWBNVA== - dependencies: - "@scramjet/symbols" "^0.38.0" - http-status-codes "^2.2.0" - -"@scramjet/utility@^0.38.0": - version "0.38.0" - resolved "https://registry.yarnpkg.com/@scramjet/utility/-/utility-0.38.0.tgz#336876075396b6959dbb8c4730889718b3a8d334" - integrity sha512-jSxeqm1BJ/ZczZAlcXP8tiU4k0cRTF4oVFsrczSw07sxliHbKSh7FaYsRfDKrO53xOlyD5FGLER/3IJckFlPpQ== - dependencies: - normalize-url "4" - yaml "^2.2.2" - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz"