@openapi-typescript-infra/service 6.10.1 → 6.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -2
- package/.github/workflows/codeql-analysis.yml +0 -77
- package/.github/workflows/nodejs.yml +0 -62
- package/.trunk/configs/.markdownlint.yaml +0 -10
- package/.trunk/configs/.yamllint.yaml +0 -10
- package/.trunk/trunk.yaml +0 -35
- package/.yarn/patches/confit-npm-3.0.0-eade8c7ce1.patch +0 -52
- package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +0 -541
- package/.yarn/releases/yarn-3.2.3.cjs +0 -783
- package/.yarnrc.yml +0 -7
- package/CHANGELOG.md +0 -525
- package/SECURITY.md +0 -12
- package/__tests__/config.test.ts +0 -53
- package/__tests__/fake-serv/api/fake-serv.yaml +0 -48
- package/__tests__/fake-serv/config/config.json +0 -13
- package/__tests__/fake-serv/src/handlers/hello.ts +0 -17
- package/__tests__/fake-serv/src/index.ts +0 -36
- package/__tests__/fake-serv/src/routes/error.ts +0 -16
- package/__tests__/fake-serv/src/routes/index.ts +0 -19
- package/__tests__/fake-serv/src/routes/other/world.ts +0 -7
- package/__tests__/fake-serv.test.ts +0 -119
- package/__tests__/vitest.test-setup.ts +0 -15
- package/src/bin/start-service.ts +0 -32
- package/src/bootstrap.ts +0 -160
- package/src/config/index.ts +0 -124
- package/src/config/schema.ts +0 -70
- package/src/config/shortstops.ts +0 -155
- package/src/config/validation.ts +0 -23
- package/src/development/port-finder.ts +0 -67
- package/src/development/repl.ts +0 -131
- package/src/env.ts +0 -29
- package/src/error.ts +0 -47
- package/src/express-app/app.ts +0 -438
- package/src/express-app/index.ts +0 -3
- package/src/express-app/internal-server.ts +0 -43
- package/src/express-app/modules.ts +0 -10
- package/src/express-app/route-loader.ts +0 -40
- package/src/express-app/types.ts +0 -32
- package/src/hook.ts +0 -36
- package/src/index.ts +0 -9
- package/src/openapi.ts +0 -184
- package/src/telemetry/DummyExporter.ts +0 -17
- package/src/telemetry/hook-modules.ts +0 -8
- package/src/telemetry/index.ts +0 -168
- package/src/telemetry/instrumentations.ts +0 -103
- package/src/telemetry/requestLogger.ts +0 -267
- package/src/tsx.d.ts +0 -1
- package/src/types.ts +0 -223
package/src/config/shortstops.ts
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import os from 'os';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
base64Handler,
|
|
6
|
-
envHandler,
|
|
7
|
-
fileHandler,
|
|
8
|
-
pathHandler,
|
|
9
|
-
requireHandler,
|
|
10
|
-
yamlHandler,
|
|
11
|
-
type ShortstopHandler,
|
|
12
|
-
} from '@sesamecare-oss/confit';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Default shortstop handlers for GasBuddy service configuration
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* A require: shortstop that will dig and find a named function
|
|
20
|
-
* with a url-like hash pattern
|
|
21
|
-
*/
|
|
22
|
-
function betterRequire(basepath: string) {
|
|
23
|
-
const baseRequire = requireHandler(basepath);
|
|
24
|
-
return function hashRequire(v: string) {
|
|
25
|
-
const [moduleName, func] = v.split('#');
|
|
26
|
-
const module = baseRequire(moduleName);
|
|
27
|
-
if (func) {
|
|
28
|
-
if (module[func]) {
|
|
29
|
-
return module[func];
|
|
30
|
-
}
|
|
31
|
-
return baseRequire(v);
|
|
32
|
-
}
|
|
33
|
-
return module;
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Just like path, but resolve ~/ to the home directory
|
|
39
|
-
*/
|
|
40
|
-
function betterPath(basepath: string) {
|
|
41
|
-
const basePath = pathHandler(basepath);
|
|
42
|
-
return function pathWithHomeDir(v: string) {
|
|
43
|
-
if (v.startsWith('~/')) {
|
|
44
|
-
return basePath(path.join(os.homedir(), v.slice(2)));
|
|
45
|
-
}
|
|
46
|
-
return basePath(v);
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Just like file, but resolve ~/ to the home directory
|
|
52
|
-
*/
|
|
53
|
-
function betterFile(basepath: string) {
|
|
54
|
-
const baseFile = fileHandler(basepath);
|
|
55
|
-
return function fileWithHomeDir(v: string) {
|
|
56
|
-
if (v.startsWith('~/')) {
|
|
57
|
-
return baseFile(path.join(os.homedir(), v.slice(2)));
|
|
58
|
-
}
|
|
59
|
-
return baseFile(v);
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function canonicalizeServiceSuffix(suffix?: string) {
|
|
64
|
-
if (!suffix) {
|
|
65
|
-
return 'internal';
|
|
66
|
-
}
|
|
67
|
-
return { serv: 'internal' }[suffix] || suffix;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Our convention is that service names end with:
|
|
72
|
-
* -serv or -internal - a back end service not callable by the outside world and where no authorization occurs (canonicalized to internal)
|
|
73
|
-
* -api - a non-UI front end service that exposes swagger and sometimes non-swagger APIs
|
|
74
|
-
* -web - a UI front end service
|
|
75
|
-
* -worker - a scheduled job or queue processor
|
|
76
|
-
*
|
|
77
|
-
* This shortstop will take a CSV of service types and tell you if this service is
|
|
78
|
-
* of that type, or if the first character after serviceType: is an exclamation point,
|
|
79
|
-
* whether it's NOT of any of the specified types
|
|
80
|
-
*/
|
|
81
|
-
function serviceTypeFactory(name: string) {
|
|
82
|
-
const type = canonicalizeServiceSuffix(name.split('-').pop());
|
|
83
|
-
|
|
84
|
-
return function serviceType(v: string) {
|
|
85
|
-
let checkValue = v;
|
|
86
|
-
let matchIsGood = true;
|
|
87
|
-
if (checkValue.startsWith('!')) {
|
|
88
|
-
matchIsGood = false;
|
|
89
|
-
checkValue = checkValue.substring(1);
|
|
90
|
-
}
|
|
91
|
-
const values = checkValue.split(',').map(canonicalizeServiceSuffix);
|
|
92
|
-
// Welp, there's no XOR so here we are.
|
|
93
|
-
return type && values.includes(type) ? matchIsGood : !matchIsGood;
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const osMethods = {
|
|
98
|
-
hostname: os.hostname,
|
|
99
|
-
platform: os.platform,
|
|
100
|
-
type: os.type,
|
|
101
|
-
version: os.version,
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
export function shortstops(service: { name: string }, sourcedir: string) {
|
|
105
|
-
/**
|
|
106
|
-
* Since we use transpiled sources a lot,
|
|
107
|
-
* basedir and sourcedir are meaningfully different reference points.
|
|
108
|
-
*/
|
|
109
|
-
const basedir = path.join(sourcedir, '..');
|
|
110
|
-
|
|
111
|
-
const env = envHandler();
|
|
112
|
-
|
|
113
|
-
return {
|
|
114
|
-
env,
|
|
115
|
-
// A version of env that can default to false
|
|
116
|
-
env_switch(v: string) {
|
|
117
|
-
if (v && v.startsWith('!')) {
|
|
118
|
-
const bval = env(`${v.substring(1)}|b`);
|
|
119
|
-
return !bval;
|
|
120
|
-
}
|
|
121
|
-
return !!env(v);
|
|
122
|
-
},
|
|
123
|
-
base64: base64Handler(),
|
|
124
|
-
regex(v: string) {
|
|
125
|
-
const [, pattern, flags] = /^\/(.*)\/([a-z]*)$/.exec(v) || [];
|
|
126
|
-
if (pattern === undefined) {
|
|
127
|
-
throw new Error(`Invalid regular expression in configuration ${v}`);
|
|
128
|
-
}
|
|
129
|
-
return new RegExp(pattern, flags);
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
// handle source and base directory intelligently
|
|
133
|
-
path: betterPath(basedir),
|
|
134
|
-
sourcepath: pathHandler(sourcedir),
|
|
135
|
-
file: betterFile(basedir),
|
|
136
|
-
sourcefile: fileHandler(sourcedir),
|
|
137
|
-
require: betterRequire(basedir),
|
|
138
|
-
sourcerequire: betterRequire(sourcedir),
|
|
139
|
-
|
|
140
|
-
// Sometimes yaml is more pleasant for configuration
|
|
141
|
-
yaml: yamlHandler(basedir),
|
|
142
|
-
|
|
143
|
-
// Switch on service type
|
|
144
|
-
servicetype: serviceTypeFactory(service.name),
|
|
145
|
-
servicename: (v: string) => v.replace(/\$\{name\}/g, service.name),
|
|
146
|
-
|
|
147
|
-
os(p: string) {
|
|
148
|
-
return osMethods[p as keyof typeof osMethods]();
|
|
149
|
-
},
|
|
150
|
-
// No-op in case you have values that start with a shortstop handler name (and colon)
|
|
151
|
-
literal(v: string) {
|
|
152
|
-
return v;
|
|
153
|
-
},
|
|
154
|
-
} satisfies Record<string, ShortstopHandler<string, unknown>>;
|
|
155
|
-
}
|
package/src/config/validation.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ConfigurationSchema } from './schema.js';
|
|
2
|
-
|
|
3
|
-
export interface ConfigValidationError {
|
|
4
|
-
path: string;
|
|
5
|
-
message: string;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export type ConfigurationValidator<Config extends ConfigurationSchema> = (config: Config) => {
|
|
9
|
-
success: boolean;
|
|
10
|
-
errors: ConfigValidationError[];
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export function validateConfiguration<Config extends ConfigurationSchema>(
|
|
14
|
-
config: Config,
|
|
15
|
-
validator: ConfigurationValidator<Config>,
|
|
16
|
-
) {
|
|
17
|
-
const result = validator(config);
|
|
18
|
-
if (!result.success) {
|
|
19
|
-
const errorMessages = result.errors.map((e) => ` - ${e.path}: ${e.message}`).join('\n');
|
|
20
|
-
throw new Error(`Configuration validation failed:
|
|
21
|
-
${errorMessages}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import net from 'net';
|
|
2
|
-
|
|
3
|
-
import { isTest } from '../env.js';
|
|
4
|
-
|
|
5
|
-
// Inspired by https://github.com/kessler/find-port/blob/master/lib/findPort.js
|
|
6
|
-
async function isAvailable(port: number) {
|
|
7
|
-
return new Promise((accept, reject) => {
|
|
8
|
-
const server = net.createServer().listen(port);
|
|
9
|
-
|
|
10
|
-
const timeoutRef = setTimeout(() => {
|
|
11
|
-
accept(false);
|
|
12
|
-
}, 2000);
|
|
13
|
-
|
|
14
|
-
timeoutRef.unref();
|
|
15
|
-
|
|
16
|
-
server.once('listening', () => {
|
|
17
|
-
clearTimeout(timeoutRef);
|
|
18
|
-
server.close();
|
|
19
|
-
accept(true);
|
|
20
|
-
});
|
|
21
|
-
server.once('error', (err) => {
|
|
22
|
-
clearTimeout(timeoutRef);
|
|
23
|
-
|
|
24
|
-
if ((err as { code?: string }).code === 'EADDRINUSE') {
|
|
25
|
-
accept(false);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
reject(err);
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function findPort(start: number) {
|
|
35
|
-
for (let p = start; p < start + 1000; p += 1) {
|
|
36
|
-
if (await isAvailable(p)) {
|
|
37
|
-
return p;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return 0;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function getEphemeralPort(): Promise<number> {
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
const server = net.createServer();
|
|
46
|
-
|
|
47
|
-
server.listen(0, () => {
|
|
48
|
-
const address = server.address();
|
|
49
|
-
if (typeof address === 'string' || !address) {
|
|
50
|
-
reject(new Error('Invalid address'));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
const port = address.port; // Retrieve the ephemeral port
|
|
54
|
-
server.close((err) => {
|
|
55
|
-
if (err) {
|
|
56
|
-
reject(err);
|
|
57
|
-
} else {
|
|
58
|
-
resolve(port);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export async function getAvailablePort(basePort: number): Promise<number> {
|
|
66
|
-
return isTest() || process.env.TEST_RUNNER ? getEphemeralPort() : findPort(basePort);
|
|
67
|
-
}
|
package/src/development/repl.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import type { REPLServer } from 'node:repl';
|
|
2
|
-
import repl from 'node:repl';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
import { glob } from 'glob';
|
|
7
|
-
import { set } from 'moderndash';
|
|
8
|
-
|
|
9
|
-
import type { AnyServiceLocals, ServiceExpress, ServiceLocals } from '../types.js';
|
|
10
|
-
import type { ConfigurationSchema } from '../config/schema.js';
|
|
11
|
-
|
|
12
|
-
const REPL_PROP = '$$repl$$';
|
|
13
|
-
|
|
14
|
-
interface WithReplProp {
|
|
15
|
-
[REPL_PROP]?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function serviceRepl<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>>(
|
|
19
|
-
app: ServiceExpress<SLocals>,
|
|
20
|
-
codepath: string | undefined,
|
|
21
|
-
onExit: () => void,
|
|
22
|
-
) {
|
|
23
|
-
class FakeReq {
|
|
24
|
-
locals: { app: ServiceExpress<SLocals> } = { app };
|
|
25
|
-
headers: Record<string, string | string[] | undefined> = {};
|
|
26
|
-
query = new URLSearchParams();
|
|
27
|
-
body: unknown = {};
|
|
28
|
-
|
|
29
|
-
constructor(public path: string) {
|
|
30
|
-
this.locals.app = app;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const rl = repl.start({
|
|
35
|
-
prompt: '> ',
|
|
36
|
-
});
|
|
37
|
-
Object.assign(rl.context, app.locals, {
|
|
38
|
-
app,
|
|
39
|
-
req: new FakeReq('/'),
|
|
40
|
-
dump(o: unknown) {
|
|
41
|
-
console.log(JSON.stringify(o, null, '\t'));
|
|
42
|
-
},
|
|
43
|
-
// Use iTerm2's escape code to copy to clipboard
|
|
44
|
-
pbcopy(str: string) {
|
|
45
|
-
const encoded = Buffer.from(str.toString(), 'utf8').toString('base64');
|
|
46
|
-
process.stdout.write(`\x1b]52;c;${encoded}\x07`);
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
rl.setupHistory(path.resolve('.node_repl_history'), (err) => {
|
|
50
|
-
if (err) {
|
|
51
|
-
// eslint-disable-next-line no-console
|
|
52
|
-
console.error('History setup failed', err);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
app.locals.service.attachRepl?.(app, rl);
|
|
56
|
-
|
|
57
|
-
loadReplFunctions(app, codepath, rl).catch((error) => {
|
|
58
|
-
// eslint-disable-next-line no-console
|
|
59
|
-
console.error('Failed to load REPL functions', error);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
rl.on('exit', onExit);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function loadReplFunctions<
|
|
66
|
-
SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
|
|
67
|
-
>(app: ServiceExpress<SLocals>, codepath: string | undefined, rl: REPLServer) {
|
|
68
|
-
if (!codepath) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const files = glob.sync(path.join(codepath, '**/*.{js,ts}'));
|
|
73
|
-
|
|
74
|
-
for (const file of files) {
|
|
75
|
-
try {
|
|
76
|
-
// Read the file content as text
|
|
77
|
-
const fileContent = fs.readFileSync(file, 'utf-8');
|
|
78
|
-
|
|
79
|
-
// Check if repl$ is present, in a very rudimentary way (note built JS has close paren not open)
|
|
80
|
-
if (/repl\$[()]/.test(fileContent)) {
|
|
81
|
-
const module = await import(path.resolve(file));
|
|
82
|
-
|
|
83
|
-
// Look for functions with the REPL_PROP marker
|
|
84
|
-
for (const exported of Object.values(module as Record<string, unknown>)) {
|
|
85
|
-
if (!exported) {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (typeof exported === 'function') {
|
|
89
|
-
const replName = (exported as WithReplProp)[REPL_PROP];
|
|
90
|
-
if (replName) {
|
|
91
|
-
set(rl.context, replName, exported.bind(null, app));
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
} catch (err) {
|
|
97
|
-
// eslint-disable-next-line no-console
|
|
98
|
-
console.error(`Failed to load REPL functions from ${file}:`, err);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Can't seem to sort out proper generics here, so we'll just use any since it's dev only
|
|
104
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
105
|
-
type ReplAny = any;
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* This decorator-like function can be applied to functions and the service will load and expose
|
|
109
|
-
* the function when the repl is engaged.
|
|
110
|
-
*
|
|
111
|
-
* async function myFunction(app: MyService['App'], arg1: string, arg2: number) {
|
|
112
|
-
* }
|
|
113
|
-
* repl$(myFunction);
|
|
114
|
-
*
|
|
115
|
-
* or
|
|
116
|
-
*
|
|
117
|
-
* repl(myFunction, 'some.func.name');
|
|
118
|
-
*/
|
|
119
|
-
export function repl$<
|
|
120
|
-
S extends ServiceExpress<ReplAny>,
|
|
121
|
-
T extends (app: S, ...args: ReplAny[]) => ReplAny,
|
|
122
|
-
>(fn: T, name?: string) {
|
|
123
|
-
const functionName = name || fn.name;
|
|
124
|
-
if (!functionName) {
|
|
125
|
-
throw new Error('Function must have a name or a name must be provided.');
|
|
126
|
-
}
|
|
127
|
-
Object.defineProperty(fn, REPL_PROP, {
|
|
128
|
-
enumerable: false,
|
|
129
|
-
value: functionName,
|
|
130
|
-
});
|
|
131
|
-
}
|
package/src/env.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
type ValidEnv = 'development' | 'production' | 'staging' | 'test';
|
|
2
|
-
|
|
3
|
-
export function getNodeEnv(): ValidEnv {
|
|
4
|
-
switch (process.env.APP_ENV || process.env.NODE_ENV) {
|
|
5
|
-
case 'production':
|
|
6
|
-
case 'staging':
|
|
7
|
-
case 'test':
|
|
8
|
-
return (process.env.APP_ENV || process.env.NODE_ENV) as ValidEnv;
|
|
9
|
-
case undefined:
|
|
10
|
-
default:
|
|
11
|
-
return 'development';
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function isDev() {
|
|
16
|
-
return getNodeEnv() === 'development';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function isProd() {
|
|
20
|
-
return getNodeEnv() === 'production';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function isStaging() {
|
|
24
|
-
return getNodeEnv() === 'staging';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function isTest() {
|
|
28
|
-
return getNodeEnv() === 'test';
|
|
29
|
-
}
|
package/src/error.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import type { ConfigurationSchema } from './config/schema.js';
|
|
2
|
-
import type { AnyServiceLocals, ServiceLike, ServiceLocals } from './types.js';
|
|
3
|
-
|
|
4
|
-
export interface ServiceErrorSpec {
|
|
5
|
-
status?: number;
|
|
6
|
-
code?: string;
|
|
7
|
-
domain?: string;
|
|
8
|
-
display_message?: string;
|
|
9
|
-
log_stack?: boolean;
|
|
10
|
-
expected_error?: boolean;
|
|
11
|
-
client_metadata?: Record<string, unknown>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* An error that gives more structured information to callers. Throw inside a handler as
|
|
16
|
-
*
|
|
17
|
-
* throw new Error(req, 'Something broke', { code: 'SomethingBroke', status: 400 });
|
|
18
|
-
*
|
|
19
|
-
* You can also include a display_message which is intended to be viewed by the end user
|
|
20
|
-
*/
|
|
21
|
-
export class ServiceError<
|
|
22
|
-
SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
|
|
23
|
-
> extends Error {
|
|
24
|
-
public status: number | undefined;
|
|
25
|
-
|
|
26
|
-
public code?: string;
|
|
27
|
-
|
|
28
|
-
public domain: string;
|
|
29
|
-
|
|
30
|
-
public display_message?: string;
|
|
31
|
-
|
|
32
|
-
public log_stack?: boolean;
|
|
33
|
-
|
|
34
|
-
// Additional data TO BE RETURNED TO THE CLIENT
|
|
35
|
-
public client_metadata?: Record<string, unknown>;
|
|
36
|
-
|
|
37
|
-
// If true, this shouldn't be logged as an error, but as an info log.
|
|
38
|
-
// This is common when the error needs to go to the client, but should not
|
|
39
|
-
// take up the valuable mental space of an error log.
|
|
40
|
-
public expected_error?: boolean;
|
|
41
|
-
|
|
42
|
-
constructor(app: ServiceLike<SLocals>, message: string, spec?: ServiceErrorSpec) {
|
|
43
|
-
super(message);
|
|
44
|
-
this.domain = app.locals.name;
|
|
45
|
-
Object.assign(this, spec);
|
|
46
|
-
}
|
|
47
|
-
}
|