@bitblit/ratchet-node-only 6.0.146-alpha → 6.0.147-alpha

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 (42) hide show
  1. package/package.json +4 -3
  2. package/src/build/ratchet-node-only-info.ts +19 -0
  3. package/src/ci/apply-ci-env-variables-to-files.spec.ts +30 -0
  4. package/src/ci/apply-ci-env-variables-to-files.ts +98 -0
  5. package/src/ci/ci-run-information-util.ts +48 -0
  6. package/src/ci/ci-run-information.ts +9 -0
  7. package/src/cli/abstract-ratchet-cli-handler.ts +33 -0
  8. package/src/cli/cli-ratchet.ts +34 -0
  9. package/src/cli/ratchet-cli-handler.ts +24 -0
  10. package/src/csv/csv-ratchet.spec.ts +59 -0
  11. package/src/csv/csv-ratchet.ts +211 -0
  12. package/src/export-builder/export-map-builder-config.ts +10 -0
  13. package/src/export-builder/export-map-builder-target-config.ts +5 -0
  14. package/src/export-builder/export-map-builder.spec.ts +22 -0
  15. package/src/export-builder/export-map-builder.ts +157 -0
  16. package/src/files/files-to-static-class.spec.ts +26 -0
  17. package/src/files/files-to-static-class.ts +101 -0
  18. package/src/files/unique-file-rename.ts +80 -0
  19. package/src/http/local-file-server.ts +129 -0
  20. package/src/http/local-server-cert.ts +72 -0
  21. package/src/jwt/jwt-ratchet-config.ts +18 -0
  22. package/src/jwt/jwt-ratchet-like.ts +19 -0
  23. package/src/jwt/jwt-ratchet.spec.ts +85 -0
  24. package/src/jwt/jwt-ratchet.ts +204 -0
  25. package/src/stream/buffer-writable.ts +16 -0
  26. package/src/stream/multi-stream.ts +15 -0
  27. package/src/stream/node-stream-ratchet.spec.ts +19 -0
  28. package/src/stream/node-stream-ratchet.ts +70 -0
  29. package/src/stream/string-writable.spec.ts +16 -0
  30. package/src/stream/string-writable.ts +14 -0
  31. package/src/third-party/angular/angular-aot-rollup-plugin.ts +18 -0
  32. package/src/third-party/common-crawl/common-crawl-service.ts +220 -0
  33. package/src/third-party/common-crawl/model/common-crawl-fetch-options.ts +12 -0
  34. package/src/third-party/common-crawl/model/common-crawl-scan.ts +11 -0
  35. package/src/third-party/common-crawl/model/domain-index-entry-raw.ts +14 -0
  36. package/src/third-party/common-crawl/model/index-entry-raw.ts +8 -0
  37. package/src/third-party/common-crawl/model/warc-entry-raw.ts +5 -0
  38. package/src/third-party/common-crawl/model/warc-entry.ts +5 -0
  39. package/src/third-party/git/git-ratchet.spec.ts +11 -0
  40. package/src/third-party/git/git-ratchet.ts +107 -0
  41. package/src/third-party/slack/publish-ci-release-to-slack.spec.ts +28 -0
  42. package/src/third-party/slack/publish-ci-release-to-slack.ts +84 -0
@@ -0,0 +1,157 @@
1
+ import fs, { Stats } from 'fs';
2
+ import path from 'path';
3
+ import { CliRatchet } from '../cli/cli-ratchet.js';
4
+ import { ExportMapBuilderConfig } from './export-map-builder-config.js';
5
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
6
+ import { EsmRatchet } from '@bitblit/ratchet-common/lang/esm-ratchet';
7
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
8
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
9
+
10
+ export class ExportMapBuilder {
11
+ public static process(cfg: ExportMapBuilderConfig): Record<string, any> {
12
+ Logger.info('Building export map : %j', cfg);
13
+
14
+ cfg.targetPackageJsonFile = cfg.targetPackageJsonFile ?? path.join(EsmRatchet.fetchDirName(import.meta.url), 'package.json');
15
+ cfg.includes = cfg.includes ?? [new RegExp('.*')];
16
+ cfg.excludes = cfg.excludes ?? [];
17
+
18
+ if (!fs.statSync(cfg.targetPackageJsonFile).isFile()) {
19
+ throw ErrorRatchet.fErr('targetPackageJsonFile: %s does not exist or is not a file', cfg.targetPackageJsonFile);
20
+ }
21
+
22
+ cfg.sourceRoot = cfg.sourceRoot ?? path.join(path.dirname(cfg.targetPackageJsonFile), 'src');
23
+ if (!fs.statSync(cfg.sourceRoot).isDirectory()) {
24
+ throw ErrorRatchet.fErr('sourceRoot: %s does not exist or is not a folder', cfg.sourceRoot);
25
+ }
26
+ cfg.targets = cfg.targets ?? [
27
+ {
28
+ type: 'import',
29
+ prefix: './lib',
30
+ suffix: '.js',
31
+ },
32
+ {
33
+ type: 'types',
34
+ prefix: './lib',
35
+ suffix: '.d.ts',
36
+ },
37
+ ];
38
+
39
+ Logger.info('Using sourceRoot %s and targets %j', cfg.sourceRoot, cfg.targets);
40
+ const exports: Record<string, any> = {};
41
+ // TODO: This cant be right can it? need to actually use this
42
+ for (const _f of cfg.includes) {
43
+ ExportMapBuilder.processSingleFile(cfg.sourceRoot, cfg, exports);
44
+ }
45
+
46
+ // Parse package.json
47
+ const parsedPackage: any = JSON.parse(fs.readFileSync(cfg.targetPackageJsonFile).toString());
48
+ parsedPackage['exports'] = exports;
49
+
50
+ if (cfg.dryRun) {
51
+ Logger.info('DryRun : Would have updated package json to : \n%j', parsedPackage);
52
+ } else {
53
+ // Update here
54
+ fs.writeFileSync(cfg.targetPackageJsonFile, JSON.stringify(parsedPackage, null, 2));
55
+ }
56
+
57
+ return exports;
58
+ }
59
+
60
+ private static pathMatchesOneOfRegExp(fileName: string, reg: RegExp[]): boolean {
61
+ let rval: boolean = false;
62
+ if (StringRatchet.trimToNull(fileName) && reg.length > 0) {
63
+ rval = true;
64
+ }
65
+ return rval;
66
+ }
67
+
68
+ private static findExports(fileName: string): string[] {
69
+ const text: string = StringRatchet.trimToEmpty(fs.readFileSync(fileName).toString());
70
+ const words: string[] = text.split(/[ \t\n]+/);
71
+ const exports: string[] = [];
72
+ for (let i = 0; i < words.length - 2; i++) {
73
+ if (words[i] === 'export') {
74
+ if (words[i + 1] === 'class' || words[i + 1] === 'interface') {
75
+ let next: string = words[i + 2];
76
+ if (next.endsWith('{')) {
77
+ next = next.substring(0, next.length - 1); // String trailing
78
+ }
79
+ if (next.indexOf('<') !== -1) {
80
+ // Strip generics
81
+ next = next.substring(0, next.indexOf('<'));
82
+ }
83
+ exports.push(next);
84
+ } else if (words[i + 1] === 'default') {
85
+ // TODO: handle default
86
+ }
87
+ }
88
+ }
89
+
90
+ return exports;
91
+ }
92
+
93
+ private static processSingleFile(fileName: string, cfg: ExportMapBuilderConfig, inRecord: Record<string, any>): Record<string, any> {
94
+ const rval: Record<string, any> = Object.assign({}, inRecord ?? {});
95
+ if (fs.existsSync(fileName)) {
96
+ if (ExportMapBuilder.pathMatchesOneOfRegExp(fileName, cfg.includes)) {
97
+ if (!ExportMapBuilder.pathMatchesOneOfRegExp(fileName, cfg.excludes)) {
98
+ const stats: Stats = fs.statSync(fileName);
99
+ if (stats.isDirectory()) {
100
+ const contFiles: string[] = fs.readdirSync(fileName);
101
+ Logger.info('Found %d files in %s to process', contFiles.length, fileName);
102
+ contFiles.forEach((f) => ExportMapBuilder.processSingleFile(path.join(fileName, f), cfg, inRecord));
103
+ } else if (stats.isFile()) {
104
+ const exports: string[] = ExportMapBuilder.findExports(fileName);
105
+ exports.forEach((s) => {
106
+ if (inRecord[s]) {
107
+ throw ErrorRatchet.fErr('Collision on name %s : %s vs %s', fileName, inRecord[s], s);
108
+ } else {
109
+ let subPath: string = fileName.substring(cfg.sourceRoot.length);
110
+ subPath = subPath.split('\\').join('/'); // Package.json uses unix separators even on windows
111
+ if (subPath.endsWith('.ts')) {
112
+ subPath = subPath.substring(0, subPath.length - 3);
113
+ }
114
+ cfg.targets.forEach((tgt) => {
115
+ inRecord[s] = inRecord[s] || {};
116
+ const targetFileName: string = StringRatchet.trimToEmpty(tgt.prefix) + subPath + StringRatchet.trimToEmpty(tgt.suffix);
117
+ inRecord[s][tgt.type] = targetFileName;
118
+ });
119
+ }
120
+ });
121
+ } else {
122
+ Logger.error('Skipping - neither file nor directory : %s', fileName);
123
+ }
124
+ } else {
125
+ Logger.error('Skipping - fails exclude check : %s', fileName);
126
+ }
127
+ } else {
128
+ Logger.error('Skipping - fails include check : %s', fileName);
129
+ }
130
+ } else {
131
+ Logger.warn('Could not find file %s', fileName);
132
+ }
133
+ return rval;
134
+ }
135
+
136
+ /**
137
+ And, in case you are running this command line...
138
+ TODO: should use switches to allow setting the various non-filename params
139
+ **/
140
+ public static async runFromCliArgs(args: string[]): Promise<Record<string, any>> {
141
+ if (args.length < 3) {
142
+ Logger.infoP('Usage: ratchet-export-builder'); // {packageJsonFile}');
143
+ return null;
144
+ } else {
145
+ const _idx: number = CliRatchet.indexOfCommandArgument('export-builder');
146
+ //const jsonFile: string = process.argv[idx + 1];
147
+
148
+ const cfg: ExportMapBuilderConfig = {
149
+ //targetPackageJsonFile: jsonFile
150
+ };
151
+
152
+ Logger.info('Running ExportMapBuilder from command line arguments');
153
+
154
+ return ExportMapBuilder.process(cfg);
155
+ }
156
+ }
157
+ }
@@ -0,0 +1,26 @@
1
+ import path from 'path';
2
+ import { FilesToStaticClass } from './files-to-static-class.js';
3
+ //import { fileURLToPath, URL } from 'url';
4
+ //import { Logger } from '../../common/logger';
5
+ import { describe, expect, test } from 'vitest';
6
+ import { EsmRatchet } from '@bitblit/ratchet-common/lang/esm-ratchet';
7
+
8
+ const testDirname: string = EsmRatchet.fetchDirName(import.meta.url);
9
+
10
+ describe('#filesToStaticClass', function () {
11
+ test('should convert files to a static class', async () => {
12
+ const out: string = await FilesToStaticClass.process(
13
+ [
14
+ path.join(testDirname, 'files-to-static-class.ts'),
15
+ path.join(testDirname, '../cli/cli-ratchet.ts'),
16
+ path.join(testDirname, '../third-party/git'),
17
+ ],
18
+ 'Test',
19
+ );
20
+ //Logger.info('xx: %s', out);
21
+
22
+ expect(out).not.toBeNull();
23
+ expect(out.length).toBeGreaterThan(0);
24
+ console.info('\n\n' + out);
25
+ });
26
+ });
@@ -0,0 +1,101 @@
1
+ /*
2
+ Takes in a list of static files, and a class name, and generates a static
3
+ class containing each of the files in a map. This is to allow static
4
+ content to be passed through webpack safely
5
+ */
6
+
7
+ import fs, { Stats } from 'fs';
8
+ import path from 'path';
9
+ import { CliRatchet } from '../cli/cli-ratchet.js';
10
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
11
+ import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
12
+
13
+ export class FilesToStaticClass {
14
+ public static async process(inFileNames: string[], outClassName: string, outFileName: string = null): Promise<string> {
15
+ if (!inFileNames) {
16
+ throw new Error('fileNames must be defined');
17
+ }
18
+ if (!outClassName) {
19
+ throw new Error('outClassName must be defined');
20
+ }
21
+ if (inFileNames.length === 0) {
22
+ Logger.warn('Warning - no files supplied to process');
23
+ }
24
+
25
+ // Preprocess to remove non-existent files and convert folders to files
26
+ const fileNames: string[] = [];
27
+ inFileNames.forEach((f) => FilesToStaticClass.processFilename(f, fileNames));
28
+
29
+ Logger.info('Generating class %s from files %j (output file: %s)', outClassName, fileNames, outFileName);
30
+
31
+ let rval = '/** \n';
32
+ rval += '* Holder for the constants to be used by consumers \n';
33
+ rval += '* Moves it into code so that it can survive a trip through WebPack \n';
34
+ rval += '*/ \n\n';
35
+ rval += 'export class ' + outClassName + ' { \n';
36
+ rval += ' public static readonly VALUES:Record<string, string> = { \n';
37
+
38
+ // If we reached here we can assume all the files exist and are files
39
+ for (let i = 0; i < fileNames.length; i++) {
40
+ // Remove both forward and back slashes...
41
+ let trimmed: string = fileNames[i].substring(fileNames[i].lastIndexOf('/') + 1);
42
+ trimmed = trimmed.substring(trimmed.lastIndexOf('\\') + 1);
43
+ const contents: string = fs.readFileSync(fileNames[i]).toString();
44
+ rval += i > 0 ? ',' : '';
45
+ rval += '"' + trimmed + '":' + JSON.stringify(contents) + '\n';
46
+ }
47
+
48
+ rval += '}; \n';
49
+ rval += '}';
50
+
51
+ if (outFileName) {
52
+ Logger.info('Writing to %s', outFileName);
53
+ fs.writeFileSync(outFileName, rval);
54
+ }
55
+
56
+ return rval;
57
+ }
58
+
59
+ private static processFilename(fileName: string, targetArrayInPlace: string[]): void {
60
+ RequireRatchet.notNullOrUndefined(targetArrayInPlace, 'targetArrayInPlace');
61
+ if (fs.existsSync(fileName)) {
62
+ const stats: Stats = fs.statSync(fileName);
63
+ if (stats.isDirectory()) {
64
+ const contFiles: string[] = fs.readdirSync(fileName);
65
+ Logger.info('Found %d files in %s to process', contFiles.length, fileName);
66
+ contFiles.forEach((f) => FilesToStaticClass.processFilename(path.join(fileName, f), targetArrayInPlace));
67
+ } else if (stats.isFile()) {
68
+ targetArrayInPlace.push(fileName);
69
+ } else {
70
+ Logger.error('Skipping - neither file nor directory : %s', fileName);
71
+ }
72
+ } else {
73
+ Logger.warn('Could not find file %s', fileName);
74
+ }
75
+ }
76
+
77
+ /**
78
+ And, in case you are running this command line...
79
+ TODO: should use switches to allow setting the various non-filename params
80
+ **/
81
+ public static async runFromCliArgs(args: string[]): Promise<string> {
82
+ if (args.length < 3) {
83
+ Logger.infoP('Usage: ratchet-files-to-static-class {outFileName} {outClassName} {file0} ... {fileN}');
84
+ return null;
85
+ } else {
86
+ const idx: number = CliRatchet.indexOfCommandArgument('files-to-static-class');
87
+ const outFileName: string = process.argv[idx + 1];
88
+ const outClassName: string = process.argv[idx + 2];
89
+ const files: string[] = process.argv.slice(idx + 3);
90
+
91
+ Logger.info(
92
+ 'Running FilesToStaticClass from command line arguments Target: %s TargetClass: %s InFiles: %j',
93
+ outFileName,
94
+ outClassName,
95
+ files,
96
+ );
97
+
98
+ return FilesToStaticClass.process(files, outClassName, outFileName);
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,80 @@
1
+ /*
2
+ Given a folder name, it renames all the files within it to a roughly
3
+ globally unique name (basically just timestamps) while retaining the
4
+ extensions. Mainly used to merge multiple folders that have files with the
5
+ same names
6
+ */
7
+
8
+ import fs, { Stats } from 'fs';
9
+ import path from 'path';
10
+ import { CliRatchet } from '../cli/cli-ratchet.js';
11
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
12
+ import { BooleanRatchet } from '@bitblit/ratchet-common/lang/boolean-ratchet';
13
+ import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
14
+
15
+ export class UniqueFileRename {
16
+ public static async process(inFolder: string, recursive: boolean = false, dryRun: boolean = false): Promise<number> {
17
+ let rval: number = 0;
18
+ if (!inFolder || inFolder.trim().length === 0) {
19
+ throw new Error('folder must be defined');
20
+ }
21
+ if (!fs.existsSync(inFolder)) {
22
+ throw new Error(inFolder + ' does not exist');
23
+ }
24
+ const stats: Stats = fs.statSync(inFolder);
25
+ if (!stats.isDirectory()) {
26
+ throw new Error(inFolder + ' is not a folder');
27
+ }
28
+
29
+ const contFiles: string[] = fs.readdirSync(inFolder);
30
+ for (const cFile of contFiles) {
31
+ //for (let i = 0; i < contFiles.length; i++) {
32
+ const full: string = path.join(inFolder, cFile);
33
+ const s2: Stats = fs.statSync(full);
34
+ if (s2.isFile()) {
35
+ const newNamePart: string = Date.now() + '_' + rval;
36
+ const idx: number = cFile.lastIndexOf('.');
37
+ const newName: string = idx > -1 ? newNamePart + cFile.substring(idx) : newNamePart;
38
+ const newFull: string = path.join(inFolder, newName);
39
+ if (dryRun) {
40
+ Logger.info('Would have renamed %s to %s', full, newFull);
41
+ } else {
42
+ Logger.info('Renaming %s to %s', full, newFull);
43
+ fs.renameSync(full, newFull);
44
+ }
45
+ rval++;
46
+ } else if (s2.isDirectory()) {
47
+ if (recursive) {
48
+ rval += await UniqueFileRename.process(full, recursive, dryRun);
49
+ } else {
50
+ Logger.info('Ignoring %s - folder, and recursive not specified', full);
51
+ }
52
+ } else {
53
+ Logger.info('Ignoring %s - neither file nor folder', full);
54
+ }
55
+ }
56
+
57
+ return rval;
58
+ }
59
+
60
+ /**
61
+ And, in case you are running this command line...
62
+ TODO: should use switches to allow setting the various non-filename params
63
+ **/
64
+ public static async runFromCliArgs(args: string[]): Promise<string> {
65
+ if (args.length !== 3) {
66
+ Logger.infoP('Usage: ratchet-unique-file-rename {folder} {recursive} {dryrun}');
67
+ Logger.infoP(args.length);
68
+ return null;
69
+ } else {
70
+ const idx: number = CliRatchet.indexOfCommandArgument('unique-file-rename');
71
+ const folder: string = process.argv[idx + 1];
72
+ const recursive: boolean = BooleanRatchet.parseBool(process.argv[idx + 2]);
73
+ const dryrun: boolean = BooleanRatchet.parseBool(process.argv[idx + 3]);
74
+
75
+ Logger.info('Running UniqueFileName from command line arguments Folder: %s Recursive: %s DryRun: %s', folder, recursive, dryrun);
76
+
77
+ return StringRatchet.safeString(UniqueFileRename.process(folder, recursive, dryrun));
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,129 @@
1
+ import { Logger } from '@bitblit/ratchet-common/logger/logger';
2
+ import { LoggerLevelName } from '@bitblit/ratchet-common/logger/logger-level-name';
3
+ import http, { IncomingMessage, Server, ServerResponse } from 'http';
4
+ import https from 'https';
5
+ import { LocalServerCert } from './local-server-cert.js';
6
+ import { EsmRatchet } from '@bitblit/ratchet-common/lang/esm-ratchet';
7
+ import path from 'path';
8
+ import fs from 'fs';
9
+ import mime from 'mime-types';
10
+ import { SimpleArgRatchet } from '@bitblit/ratchet-common/lang/simple-arg-ratchet';
11
+ import { ErrorRatchet } from '@bitblit/ratchet-common/lang/error-ratchet';
12
+ import { NumberRatchet } from '@bitblit/ratchet-common/lang/number-ratchet';
13
+ import { BooleanRatchet } from '@bitblit/ratchet-common/lang/boolean-ratchet';
14
+
15
+ /**
16
+ * Very simple file server like 'serve', but can run https for things that
17
+ * need that (like most apis these days)
18
+ */
19
+ export class LocalFileServer {
20
+ private server: Server;
21
+ private urlRoot: string;
22
+
23
+ constructor(
24
+ private port: number = 8888,
25
+ private https: boolean = false,
26
+ private fileRoot: string = EsmRatchet.fetchDirName(import.meta.url),
27
+ ) {
28
+ if (fs.existsSync(fileRoot)) {
29
+ if (!fs.statSync(fileRoot).isDirectory()) {
30
+ throw ErrorRatchet.fErr('Cannot start with %s - it is not a directory', fileRoot);
31
+ }
32
+ } else {
33
+ throw ErrorRatchet.fErr('Cannot start with %s - it does not exist', fileRoot);
34
+ }
35
+ }
36
+
37
+ async runServer(): Promise<boolean> {
38
+ return new Promise<boolean>((res, rej) => {
39
+ try {
40
+ Logger.info('Starting file server on port %d at root %s', this.port, this.fileRoot);
41
+
42
+ if (this.https) {
43
+ const options = {
44
+ key: LocalServerCert.CLIENT_KEY_PEM,
45
+ cert: LocalServerCert.CLIENT_CERT_PEM,
46
+ };
47
+ Logger.info(
48
+ 'Starting https server - THIS SERVER IS NOT SECURE! The KEYS are in the code! Testing Server Only - Use at your own risk!',
49
+ );
50
+ this.server = https.createServer(options, this.requestHandler.bind(this)).listen(this.port);
51
+ this.urlRoot = 'https://localhost:' + this.port;
52
+ } else {
53
+ this.server = http.createServer(this.requestHandler.bind(this)).listen(this.port);
54
+ this.urlRoot = 'http://localhost:' + this.port;
55
+ }
56
+ Logger.info('File server is listening');
57
+
58
+ // Also listen for SIGINT
59
+ process.on('SIGINT', () => {
60
+ Logger.info('Caught SIGINT - shutting down test server...');
61
+ this.server.close();
62
+ res(true);
63
+ });
64
+ } catch (err) {
65
+ Logger.error('Local server failed : %s', err, err);
66
+ rej(err);
67
+ }
68
+ });
69
+ }
70
+
71
+ async requestHandler(request: IncomingMessage, response: ServerResponse): Promise<any> {
72
+ const reqPath: string = request.url.includes('?') ? request.url.substring(0, request.url.indexOf('?')) : request.url;
73
+ const filePath: string = path.join(this.fileRoot, reqPath);
74
+ if (fs.existsSync(filePath)) {
75
+ const stats: fs.Stats = fs.statSync(filePath);
76
+ if (stats.isFile()) {
77
+ let mimetype: string = mime.contentType(filePath);
78
+ if (mimetype === 'video/mp2t') {
79
+ // Not very likely for me!
80
+ mimetype = 'text/x-typescript';
81
+ }
82
+ const buf: Buffer = fs.readFileSync(filePath);
83
+ response.setHeader('Content-Type', mimetype);
84
+ response.statusCode = 200;
85
+ response.end(buf);
86
+ } else if (stats.isDirectory()) {
87
+ this.writeFolderListToResponse(reqPath, filePath, response);
88
+ }
89
+ } else {
90
+ response.statusCode = 404;
91
+ response.setHeader('Content-Type', 'text/html');
92
+ response.end(`<html><body>No such file: ${reqPath}</body></html>`);
93
+ }
94
+ }
95
+
96
+ public writeFolderListToResponse(rootpth: string, pth: string, response: ServerResponse): void {
97
+ response.statusCode = 200;
98
+ response.setHeader('Content-Type', 'text/html');
99
+
100
+ let body: string = '<html><body><h1>Files in ' + pth + '</h1><ul>';
101
+
102
+ if (rootpth !== '/') {
103
+ let sub: string = rootpth.substring(0, rootpth.lastIndexOf('/'));
104
+ sub = sub.length === 0 ? '/' : sub;
105
+ body += '<li><a href="' + sub + '">..</a></li>';
106
+ }
107
+
108
+ fs.readdirSync(pth).forEach((file) => {
109
+ let fullUrl: string = this.urlRoot + rootpth;
110
+ fullUrl += fullUrl.endsWith('/') ? file : '/' + file;
111
+ body += `<li><a href="${fullUrl}">${file}</a></li>`;
112
+ });
113
+
114
+ body += '</ul></body></html>';
115
+
116
+ response.end(body);
117
+ }
118
+
119
+ public static async runLocalFileServerFromCliArgs(args: string[]): Promise<void> {
120
+ Logger.setLevel(LoggerLevelName.debug);
121
+ const pArgs: Record<string, string> = SimpleArgRatchet.parseSingleArgs(args, ['root', 'port', 'https']);
122
+ const port: number = pArgs['port'] ? NumberRatchet.safeNumber(pArgs['port']) : 8888;
123
+ const https: boolean = pArgs['https'] ? BooleanRatchet.parseBool(pArgs['https']) : false;
124
+ const root: string = pArgs['root'] ?? EsmRatchet.fetchDirName(import.meta.url);
125
+ const testServer: LocalFileServer = new LocalFileServer(port, https, root);
126
+ const res: boolean = await testServer.runServer();
127
+ Logger.info('Res was : %s', res);
128
+ }
129
+ }
@@ -0,0 +1,72 @@
1
+ export class LocalServerCert {
2
+ public static readonly CLIENT_CSR: string =
3
+ '-----BEGIN CERTIFICATE REQUEST-----\n' +
4
+ 'MIICwjCCAaoCAQAwfTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5WMRIwEAYDVQQH\n' +
5
+ 'DAlMYXMgVmVnYXMxDjAMBgNVBAoMBVBsdW1hMRkwFwYDVQQDDBB3d3cuaGV5cGx1\n' +
6
+ 'bWEuY29tMSIwIAYJKoZIhvcNAQkBFhNjd2Vpc3NAaGV5cGx1bWEuY29tMIIBIjAN\n' +
7
+ 'BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Gh1SD1VciosMgh3MHF7IyKgNTu2\n' +
8
+ 'DjGMoORJV4VNKzrjp7UOdKnD3so0Xx3A6bUTYAL8Vtc+EG+8LZjUpBIOwuKzzbVo\n' +
9
+ 't16+yt3YXBxzTZPU9g7sFsXx42RoNispIFq0enRtJrq/zq8izZxUZdGn9XD2DidL\n' +
10
+ 'AedtRGjb8cmRuP6wmMRPBOZjct4ZjsSiTyhPa6kpt8V7Pa7vm0HGxcHiDAGp6zoy\n' +
11
+ 'GHfBdsqeBdGbkGT1ZPfs9kpbtXPm82Sckd0p3oY3fJn0rZqpTAb8qcdGcnheYtqX\n' +
12
+ 'FSjX7EoGsIXAK+oj25MvtfoZFMk4rjQ7FkHbgGk0iMHLN0kNjJzN0ysN/wIDAQAB\n' +
13
+ 'oAAwDQYJKoZIhvcNAQELBQADggEBAMbpCdoqmY9crolsh5y9YtYDLRIwisTjTjU1\n' +
14
+ 'Xzp1MurSzGIdHLokU+fdVWTIzn3uOu24yTQouTUUoYWHT4YgN4wELdDydfNxWvyl\n' +
15
+ 'r34QV5B0FZbRV2sNz/3C1UX/Uor4af1Yv+QYlGHspgj+WIAEkNQ3xQIo9+I/miR+\n' +
16
+ '2VSlydtyGvmzipgv6CAwOsrQsIw7DkpVmnqIjgjPSXlGCgeKM9S1D/CwNwZnVA/e\n' +
17
+ 'DF1SzDkJKl60/n+xZGYl/OtkH9vB8T6fHqk0iMxXuVUxI137fwEJwIQB5L6hFyJa\n' +
18
+ 'L4hbjq7Cull4qOhXDby+fNJT9Ic7VCosJBXBHxHPsEnY2+TZAJo=\n' +
19
+ '-----END CERTIFICATE REQUEST-----\n';
20
+
21
+ public static readonly CLIENT_CERT_PEM: string =
22
+ '-----BEGIN CERTIFICATE-----\n' +
23
+ 'MIIDgTCCAmkCFDKASki0c6HD75dCdIZZ3vXq4eQeMA0GCSqGSIb3DQEBCwUAMH0x\n' +
24
+ 'CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOVjESMBAGA1UEBwwJTGFzIFZlZ2FzMQ4w\n' +
25
+ 'DAYDVQQKDAVQbHVtYTEZMBcGA1UEAwwQd3d3LmhleXBsdW1hLmNvbTEiMCAGCSqG\n' +
26
+ 'SIb3DQEJARYTY3dlaXNzQGhleXBsdW1hLmNvbTAeFw0yMzAxMjMxMDU2MDlaFw0y\n' +
27
+ 'MzAyMjIxMDU2MDlaMH0xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOVjESMBAGA1UE\n' +
28
+ 'BwwJTGFzIFZlZ2FzMQ4wDAYDVQQKDAVQbHVtYTEZMBcGA1UEAwwQd3d3LmhleXBs\n' +
29
+ 'dW1hLmNvbTEiMCAGCSqGSIb3DQEJARYTY3dlaXNzQGhleXBsdW1hLmNvbTCCASIw\n' +
30
+ 'DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANhodUg9VXIqLDIIdzBxeyMioDU7\n' +
31
+ 'tg4xjKDkSVeFTSs646e1DnSpw97KNF8dwOm1E2AC/FbXPhBvvC2Y1KQSDsLis821\n' +
32
+ 'aLdevsrd2Fwcc02T1PYO7BbF8eNkaDYrKSBatHp0bSa6v86vIs2cVGXRp/Vw9g4n\n' +
33
+ 'SwHnbURo2/HJkbj+sJjETwTmY3LeGY7Eok8oT2upKbfFez2u75tBxsXB4gwBqes6\n' +
34
+ 'Mhh3wXbKngXRm5Bk9WT37PZKW7Vz5vNknJHdKd6GN3yZ9K2aqUwG/KnHRnJ4XmLa\n' +
35
+ 'lxUo1+xKBrCFwCvqI9uTL7X6GRTJOK40OxZB24BpNIjByzdJDYyczdMrDf8CAwEA\n' +
36
+ 'ATANBgkqhkiG9w0BAQsFAAOCAQEAWQG2tvWY+cyeeumD/7WKTBNaBjg4EAe+1mnZ\n' +
37
+ 'KQsg0gGUL0kWsqCkg4xEqIojkKMjs62uS6ballEyWawygYd91OaJLFopNu+Dxk4N\n' +
38
+ '5GWKpriPr02vI6rMUZNtCmsooukEShr5ufFWb4WLnk4NXQlBCXTHbmIf7Z82UOMw\n' +
39
+ 'ONZdZyKLqlA0Z6SWYBp2gO32puww6dUU0DAKkIIx1SN8i8UKvowRAy13bugPtyau\n' +
40
+ 'NknlE3J1+Gab1hHCMRdKFZPKy8nc7LWUNZhgKdY82IC/k5FSW32Wibfog1TwWRJR\n' +
41
+ 'ceTW4EN4P7ZmdHGMYkIplc7Qcx0mraY2HRqmjA33j3cNcY5UsQ==\n' +
42
+ '-----END CERTIFICATE-----\n';
43
+
44
+ public static readonly CLIENT_KEY_PEM: string =
45
+ '-----BEGIN RSA PRIVATE KEY-----\n' +
46
+ 'MIIEogIBAAKCAQEA2Gh1SD1VciosMgh3MHF7IyKgNTu2DjGMoORJV4VNKzrjp7UO\n' +
47
+ 'dKnD3so0Xx3A6bUTYAL8Vtc+EG+8LZjUpBIOwuKzzbVot16+yt3YXBxzTZPU9g7s\n' +
48
+ 'FsXx42RoNispIFq0enRtJrq/zq8izZxUZdGn9XD2DidLAedtRGjb8cmRuP6wmMRP\n' +
49
+ 'BOZjct4ZjsSiTyhPa6kpt8V7Pa7vm0HGxcHiDAGp6zoyGHfBdsqeBdGbkGT1ZPfs\n' +
50
+ '9kpbtXPm82Sckd0p3oY3fJn0rZqpTAb8qcdGcnheYtqXFSjX7EoGsIXAK+oj25Mv\n' +
51
+ 'tfoZFMk4rjQ7FkHbgGk0iMHLN0kNjJzN0ysN/wIDAQABAoIBAHzZ9yAQUqWk8w6C\n' +
52
+ 'l9EZB4PDzE4p/uS9bXa9fhrCSz0vonv1FzvzXY/BdOmTTuMGlwEDd/XaBHKTJCvi\n' +
53
+ 'SnvF90I0bKu3h4yTWtvLlbG+sD8HlQvInCifVuhr2zu1Nur1qb4kQXzgrRxfKmMZ\n' +
54
+ 'WA/OH2qZGzwbK0kT7ZRUMuCR/EKPjYw9KP6pMF8nxXUjSm+g3YwgIB8kiPeDCV6C\n' +
55
+ '0/Ecpv5qMqcoYTg9f9KyBNmY3U5ZgbYrdwTDSMrrTwZKSHhTktdF0SEfYrVGLLEU\n' +
56
+ 'vfQQlQmfcc5Z3+cz99BH1BeTNaCPtEaXjgvQYwkWlSxnY6QUE0p1qq9Jy0xaVEx9\n' +
57
+ '8LReuHECgYEA/hhJ6b2XV1WLtszFO8MRMzlyMuvBc3Ot0dsuJ7r1W3WOF5X7poSU\n' +
58
+ 'xG0xe+n6Kubmi3tGhzS7BN4TEO9/SSE2gIQTk9zwAMuf+mZJQcG0Qz1iftVp5nnM\n' +
59
+ 'zi205vBLLq2Pmk7wbhTIBO8J190Dli1/fuvk/cmJrA60Ys8v3y4e820CgYEA2gfV\n' +
60
+ 'Q3eHRxk3wl4On71mMovY7lMx6+S8K8hBGo26A5FIu6N6v2jxKnQ5YphwFgjtkxgs\n' +
61
+ 'LspAXmxRwFMapZP3d/nFeBhlOzNly7tqIdDOeNqEcUzEx7pwGXpWGZEHOYs1fvU+\n' +
62
+ 'gU/1N8q9DBvr0B+XGeYR3NNdljajo6pwWZ6ed5sCgYAGmkz5ZPLU0yVBR8rsRaJh\n' +
63
+ 'yWFdT2EEhgIDTQXDBImxqblahYw3hIR1Ij1B8g+NI9jj0P1BMC6X7sliDEcreFB5\n' +
64
+ 'QHVdx0T5UFFE6XmH2ue7Q5IWp6cL1ShsRyXHRoE9okb0BI8c3S9haXDBCj44ndAN\n' +
65
+ 'VUXrDlykevFXC/k7fHBTdQKBgHjIshpoEycOD1e753oS4JTL6GdO6271DlFq5LYj\n' +
66
+ 'IZNsXtCkJhH3vvJ35Hp8XEu4snQ0hfV90d79PuS+pRppOETct8pqKVp8hL4ymv8U\n' +
67
+ 'v+0vkQN7NeA3pnZW0W/kag400nP8xJ26f+xiggw9Q4vOlFSioe6loUjgCBNZDlh3\n' +
68
+ 'iO5VAoGASXkcv39B9/8FuWQ7rwhXHEubxOOwZZzSBU1wtLj2qHyPGoeU9ZpsmdrL\n' +
69
+ 'XS9w1Jy0e49qmbBjzTqAEBw2nn/JVMFHyI/s//JVF7Q9GfBZAF5XF6mJ24yzTKgF\n' +
70
+ 'kSoO5T+7s8NXi0eAIBe4CkWCBX7kWEZtu46GuVhsrC3oEazuLOs=\n' +
71
+ '-----END RSA PRIVATE KEY-----\n';
72
+ }
@@ -0,0 +1,18 @@
1
+ import { LoggerLevelName } from '@bitblit/ratchet-common/logger/logger-level-name';
2
+
3
+ /**
4
+ * Functions to help with creating and decoding JWTs
5
+ *
6
+ * JWTRatchet accepts promises for its inputs for the simple reason that best practice dictates that the keys
7
+ * should never be in the code, which means it is likely somewhere else. That MIGHT be somewhere synchronous
8
+ * like an environmental variable, but it could very likely be someplace remote like a secure key store. By
9
+ * accepting promises here, we make it easy to do JwtRatchet construction in a place (like an IOT container)
10
+ * that itself must be synchronous
11
+ */
12
+ export interface JwtRatchetConfig {
13
+ encryptionKeyPromise: Promise<string | string[]>;
14
+ decryptKeysPromise?: Promise<string[]>;
15
+ jtiGenerator?: () => string;
16
+ decryptOnlyKeyUseLogLevel?: LoggerLevelName;
17
+ parseFailureLogLevel?: LoggerLevelName;
18
+ }
@@ -0,0 +1,19 @@
1
+ import { LoggerLevelName } from '@bitblit/ratchet-common/logger/logger-level-name';
2
+ import { JwtTokenBase } from '@bitblit/ratchet-common/jwt/jwt-token-base';
3
+ import { ExpiredJwtHandling } from '@bitblit/ratchet-common/jwt/expired-jwt-handling';
4
+
5
+ /**
6
+ * Classes implementing this interface have Functions to help with creating and decoding JWTs
7
+ */
8
+ export interface JwtRatchetLike {
9
+ get encryptionKeyPromise(): Promise<string | string[]>;
10
+ get decryptKeysPromise(): Promise<string[]>;
11
+ get jtiGenerator(): () => string;
12
+ get decryptOnlyKeyUseLogLevel(): LoggerLevelName;
13
+ get parseFailureLogLevel(): LoggerLevelName;
14
+ decodeToken<T extends JwtTokenBase>(payloadString: string, expiredHandling?: ExpiredJwtHandling): Promise<T>;
15
+ encryptionKeyArray(): Promise<string[]>;
16
+ selectRandomEncryptionKey(): Promise<string>;
17
+ createTokenString(payload: any, expirationSeconds?: number, overrideEncryptionKey?: string): Promise<string>;
18
+ refreshJWTString(tokenString: string, allowExpired?: boolean, expirationSeconds?: number): Promise<string>;
19
+ }