@openapi-typescript-infra/service 6.10.1 → 6.10.3

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 (50) hide show
  1. package/build/bootstrap.js +4 -2
  2. package/build/bootstrap.js.map +1 -1
  3. package/package.json +8 -2
  4. package/.github/workflows/codeql-analysis.yml +0 -77
  5. package/.github/workflows/nodejs.yml +0 -62
  6. package/.trunk/configs/.markdownlint.yaml +0 -10
  7. package/.trunk/configs/.yamllint.yaml +0 -10
  8. package/.trunk/trunk.yaml +0 -35
  9. package/.yarn/patches/confit-npm-3.0.0-eade8c7ce1.patch +0 -52
  10. package/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +0 -541
  11. package/.yarn/releases/yarn-3.2.3.cjs +0 -783
  12. package/.yarnrc.yml +0 -7
  13. package/CHANGELOG.md +0 -525
  14. package/SECURITY.md +0 -12
  15. package/__tests__/config.test.ts +0 -53
  16. package/__tests__/fake-serv/api/fake-serv.yaml +0 -48
  17. package/__tests__/fake-serv/config/config.json +0 -13
  18. package/__tests__/fake-serv/src/handlers/hello.ts +0 -17
  19. package/__tests__/fake-serv/src/index.ts +0 -36
  20. package/__tests__/fake-serv/src/routes/error.ts +0 -16
  21. package/__tests__/fake-serv/src/routes/index.ts +0 -19
  22. package/__tests__/fake-serv/src/routes/other/world.ts +0 -7
  23. package/__tests__/fake-serv.test.ts +0 -119
  24. package/__tests__/vitest.test-setup.ts +0 -15
  25. package/src/bin/start-service.ts +0 -32
  26. package/src/bootstrap.ts +0 -160
  27. package/src/config/index.ts +0 -124
  28. package/src/config/schema.ts +0 -70
  29. package/src/config/shortstops.ts +0 -155
  30. package/src/config/validation.ts +0 -23
  31. package/src/development/port-finder.ts +0 -67
  32. package/src/development/repl.ts +0 -131
  33. package/src/env.ts +0 -29
  34. package/src/error.ts +0 -47
  35. package/src/express-app/app.ts +0 -438
  36. package/src/express-app/index.ts +0 -3
  37. package/src/express-app/internal-server.ts +0 -43
  38. package/src/express-app/modules.ts +0 -10
  39. package/src/express-app/route-loader.ts +0 -40
  40. package/src/express-app/types.ts +0 -32
  41. package/src/hook.ts +0 -36
  42. package/src/index.ts +0 -9
  43. package/src/openapi.ts +0 -184
  44. package/src/telemetry/DummyExporter.ts +0 -17
  45. package/src/telemetry/hook-modules.ts +0 -8
  46. package/src/telemetry/index.ts +0 -168
  47. package/src/telemetry/instrumentations.ts +0 -103
  48. package/src/telemetry/requestLogger.ts +0 -267
  49. package/src/tsx.d.ts +0 -1
  50. package/src/types.ts +0 -223
@@ -1,36 +0,0 @@
1
- import type { Service, ServiceLocals } from '../../../src/types.js';
2
- import { useService } from '../../../src/index.js';
3
-
4
- export interface FakeServLocals extends ServiceLocals {
5
- services: {
6
- fakeServ: {
7
- get_something(): Promise<{ things: string[] } | Error>;
8
- };
9
- };
10
- }
11
-
12
- export function service(): Service<FakeServLocals> {
13
- const base = useService<FakeServLocals>();
14
- return {
15
- ...base,
16
- async start(app) {
17
- await base.start(app);
18
- app.locals.services = app.locals.services || {};
19
- app.locals.services.fakeServ = {
20
- async get_something() {
21
- throw new Error('Should not be called.');
22
- },
23
- };
24
- },
25
- async onRequest(req, res) {
26
- await base.onRequest?.(req, res);
27
- res.locals.rawBody = true;
28
- },
29
- async healthy(app) {
30
- await base.healthy?.(app);
31
- return new Promise((accept) => {
32
- setTimeout(accept, 1000);
33
- });
34
- },
35
- };
36
- }
@@ -1,16 +0,0 @@
1
- import type { ServiceRouter } from '../../../../src/index';
2
- import { ServiceError } from '../../../../src/error';
3
-
4
- export function route(router: ServiceRouter) {
5
- router.get('/sync', (req) => {
6
- throw new ServiceError(req.app, 'Synchronous error', { code: 'SyncError' });
7
- });
8
-
9
- router.get('/async', async (req) => {
10
- await new Promise((accept) => {
11
- setTimeout(accept, 100);
12
- }).then(() => {
13
- throw new ServiceError(req.app, 'Async error', { code: 'AsyncError' });
14
- });
15
- });
16
- }
@@ -1,19 +0,0 @@
1
- import type { ServiceExpress, ServiceRouter } from '../../../../src/index';
2
- import type { FakeServLocals } from '../index';
3
-
4
- export function route(router: ServiceRouter<FakeServLocals>, app: ServiceExpress<FakeServLocals>) {
5
- const worldRequests = app.locals.meter.createCounter('world_requests', {
6
- description: 'Metrics about requests to world',
7
- });
8
-
9
- router.get('/world', (req, res) => {
10
- worldRequests.add(1, { method: 'get' });
11
- res.json({ hello: 'world' });
12
- });
13
-
14
- router.post('/world', async (req, res) => {
15
- await app.locals.services.fakeServ.get_something();
16
- worldRequests.add(1);
17
- res.sendStatus(204);
18
- });
19
- }
@@ -1,7 +0,0 @@
1
- import type { Router } from 'express';
2
-
3
- export function route(router: Router) {
4
- router.get('/', (req, res) => {
5
- res.json({ hello: 'jupiter' });
6
- });
7
- }
@@ -1,119 +0,0 @@
1
- import http from 'http';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
-
5
- import { describe, expect, test } from 'vitest';
6
- import request from 'supertest';
7
-
8
- import type {
9
- ServiceStartOptions} from '../src/index.js';
10
- import {
11
- listen,
12
- startApp,
13
- } from '../src/index.js';
14
-
15
- import { type FakeServLocals, service } from './fake-serv/src/index.js';
16
-
17
- function httpRequest(options: http.RequestOptions) {
18
- return new Promise((resolve, reject) => {
19
- const req = http.request(options, (res) => {
20
- let responseData = '';
21
- res.on('data', (chunk) => {
22
- responseData += chunk;
23
- });
24
- res.on('end', () => {
25
- resolve(responseData);
26
- });
27
- });
28
- req.on('error', (e) => {
29
- reject(e);
30
- });
31
- req.end();
32
- });
33
- }
34
-
35
- describe('fake-serv', () => {
36
- test('basic service functionality', async () => {
37
- const __filename = fileURLToPath(import.meta.url);
38
- const __dirname = path.dirname(__filename);
39
-
40
- const options: ServiceStartOptions<FakeServLocals> = {
41
- service,
42
- name: 'fake-serv',
43
- rootDirectory: path.resolve(__dirname, './fake-serv'),
44
- codepath: 'src',
45
- version: '1.0.0',
46
- };
47
-
48
- const app = await startApp(options).catch((error) => {
49
- // eslint-disable-next-line no-console
50
- console.error(error);
51
- throw error;
52
- });
53
- expect(app.locals.config.server.port).not.toEqual(0);
54
- expect(app).toBeTruthy();
55
-
56
- let { body } = await request(app).get('/world').timeout(500).expect(200);
57
- expect(body.hello).toEqual('world');
58
-
59
- ({ body } = await request(app).get('/other/world').timeout(500).expect(200));
60
- expect(body.hello).toEqual('jupiter');
61
-
62
- ({ body } = await request(app)
63
- .get('/hello')
64
- .query({ greeting: 'Hello Pluto!', number: '6', break_things: true })
65
- .expect(200));
66
- expect(body.greeting).toEqual('Hello Pluto!');
67
-
68
- // Can't convert green to a number
69
- await request(app)
70
- .get('/hello')
71
- .query({ greeting: 'Hello Pluto!', number: 'green' })
72
- .expect(400);
73
-
74
- // Make sure body paramater conversion works
75
- await request(app).post('/hello').send({ number: 'green' }).expect(400);
76
- await request(app).post('/hello').send({ number: '6' }).expect(204);
77
- await request(app).post('/hello').send({ number: 6 }).expect(204);
78
-
79
- ({ body } = await request(app).get('/error/sync').timeout(1000).expect(500));
80
- expect(body.code).toEqual('SyncError');
81
-
82
- ({ body } = await request(app).get('/error/async').timeout(1000).expect(500));
83
- expect(body.code).toEqual('AsyncError');
84
-
85
- // Mocking
86
- await request(app).post('/world').expect(500);
87
-
88
- const server = await listen(app);
89
- // Exercise the http module
90
- await httpRequest({
91
- hostname: 'localhost',
92
- port: app.locals.config.server.port,
93
- path: '/hello?greeting=Hello&number=6',
94
- method: 'GET',
95
- });
96
- await request(app.locals.internalApp)
97
- .get('/metrics')
98
- .expect(200)
99
- .expect((res) => {
100
- expect(res.text).toMatch(/nodejs_version_info{version/);
101
- expect(res.text).toMatch(/# UNIT http_server_duration ms/);
102
- expect(res.text).toMatch(/world_requests_total{method="get".*} 1/);
103
- expect(res.text).toContain(
104
- 'http_request_duration_seconds_bucket{status_code="200",method="GET",path="/world",service="fake-serv"',
105
- );
106
- });
107
-
108
- // Clean shutdown
109
- await new Promise<void>((accept, reject) => {
110
- server.close((e) => {
111
- if (e) {
112
- reject(e);
113
- } else {
114
- accept();
115
- }
116
- });
117
- });
118
- });
119
- });
@@ -1,15 +0,0 @@
1
- import { afterAll, beforeAll } from 'vitest';
2
-
3
- import { shutdownGlobalTelemetry, startGlobalTelemetry } from '../src/telemetry/index.js';
4
-
5
- // Even in testing, this needs to run first so that the instrumentation
6
- // is loaded BEFORE express is loaded.
7
- const startPromise = startGlobalTelemetry('fake-serv');
8
-
9
- beforeAll(async () => {
10
- await startPromise;
11
- });
12
-
13
- afterAll(async () => {
14
- await shutdownGlobalTelemetry().catch(() => undefined);
15
- });
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env node
2
- import minimist from 'minimist';
3
-
4
- import { serviceRepl } from '../development/repl.js';
5
- import { isDev } from '../env.js';
6
- import { bootstrap } from '../bootstrap.js';
7
-
8
- /**
9
- * built - forces the use of the build directory. Defaults to true in stage/prod, not in dev
10
- * repl - launch the REPL (defaults to disabling telemetry)
11
- * telemetry - whether to use OpenTelemetry. Defaults to false in dev or with repl
12
- * nobind - do not listen on http port or expose metrics
13
- */
14
- const argv = minimist(process.argv.slice(2), {
15
- boolean: ['built', 'repl', 'telemetry', 'nobind'],
16
- });
17
-
18
- if (argv.telemetry) {
19
- await import('../telemetry/hook-modules.js');
20
- }
21
-
22
- const noTelemetry = (argv.repl || isDev()) && !argv.telemetry;
23
- void bootstrap({
24
- ...argv,
25
- telemetry: !noTelemetry,
26
- }).then(({ app, codepath, server }) => {
27
- if (argv.repl) {
28
- serviceRepl(app, codepath, () => {
29
- server?.close();
30
- });
31
- }
32
- });
package/src/bootstrap.ts DELETED
@@ -1,160 +0,0 @@
1
- import path from 'node:path';
2
- import assert from 'node:assert';
3
-
4
- import { config } from 'dotenv';
5
- import { readPackageUp } from 'read-package-up';
6
- import type { NormalizedPackageJson } from 'read-package-up';
7
- import type { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
8
-
9
- import type {
10
- AnyServiceLocals,
11
- RequestLocals,
12
- ServiceLocals,
13
- ServiceStartOptions,
14
- } from './types.js';
15
- import { isDev } from './env.js';
16
- import { startWithTelemetry } from './telemetry/index.js';
17
- import type { ConfigurationSchema } from './config/schema.js';
18
-
19
- interface BootstrapArguments {
20
- // The name of the service, else discovered via read-package-up
21
- name?: string;
22
- // The name of the file with the service function, relative to root
23
- main?: string;
24
- // Root directory of the app, else discovered via read-package-up
25
- root?: string;
26
- // Use built directory. Omitting lets us determine a sensible default
27
- built?: boolean;
28
- // The location of the package.json used for discovery (defaults to cwd)
29
- packageDir?: string;
30
- // Whether to engage telemetry
31
- telemetry?: boolean;
32
- // Don't bind to http port or expose metrics
33
- nobind?: boolean;
34
- // The version of the app, else discovered via read-package-up
35
- version?: string;
36
- }
37
-
38
- function resolveMain(packageJson: NormalizedPackageJson) {
39
- if (typeof packageJson.main === 'string') {
40
- return packageJson.main;
41
- }
42
- return undefined;
43
- }
44
-
45
- async function getServiceDetails(argv: BootstrapArguments = {}) {
46
- if (argv.name && argv.root) {
47
- return {
48
- rootDirectory: argv.root,
49
- name: argv.name,
50
- version: argv.version || '0.0.0',
51
- main: argv.main || (isDev() && !argv.built ? 'src/index.ts' : 'build/index.js'),
52
- };
53
- }
54
- const cwd = argv.packageDir ? path.resolve(argv.packageDir) : process.cwd();
55
- const pkg = await readPackageUp({ cwd });
56
- if (!pkg) {
57
- throw new Error(
58
- `Unable to find package.json in ${cwd} to get main module. Make sure you are running from the package root directory.`,
59
- );
60
- }
61
- const main = resolveMain(pkg.packageJson);
62
- const parts = pkg.packageJson.name.split('/');
63
- return {
64
- main,
65
- rootDirectory: path.dirname(pkg.path),
66
- name: parts[parts.length - 1],
67
- version: pkg.packageJson.version,
68
- customizer: (pkg.packageJson.config?.telemetry as { customizer?: string })?.customizer,
69
- };
70
- }
71
-
72
- function getBuildDir(main: string): 'build' | 'dist' {
73
- const dir = /^(?:\.?\/?)(build|dist)\//.exec(main);
74
- assert(dir, 'Could not determine build directory - should be dist or build');
75
- return dir[1] as 'build' | 'dist';
76
- }
77
-
78
- // Automagically start your app by using common patterns
79
- // to find your implementation and settings. This is most useful
80
- // for jobs or other scripts that need service infra but are
81
- // not simply the service
82
- export async function bootstrap<
83
- SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
84
- RLocals extends RequestLocals = RequestLocals,
85
- >(argv?: BootstrapArguments) {
86
- const { main, rootDirectory, name, version, customizer } = await getServiceDetails(argv);
87
-
88
- let entrypoint: string;
89
- let codepath: 'build' | 'dist' | 'src' = 'build';
90
- if (isDev() && argv?.built !== true) {
91
- const handlesTs = parseInt(process.versions.node.split('.')[0], 10) >= 24;
92
- if (!handlesTs) {
93
- await import('tsx/esm');
94
- }
95
- if (main) {
96
- entrypoint = handlesTs
97
- ? main
98
- : main.replace(/^(\.?\/?)(build|dist)\//, '$1src/').replace(/\.js$/, '.ts');
99
- } else {
100
- entrypoint = './src/index.ts';
101
- }
102
- codepath = 'src';
103
- } else if (main) {
104
- codepath = getBuildDir(main);
105
- entrypoint = main;
106
- } else {
107
- entrypoint = './build/index.js';
108
- }
109
-
110
- config({ quiet: true });
111
-
112
- const absoluteEntrypoint = path.resolve(rootDirectory, entrypoint);
113
- if (argv?.telemetry) {
114
- let otelCustomizer:
115
- | ((options: Partial<NodeSDKConfiguration>) => Partial<NodeSDKConfiguration>)
116
- | undefined = undefined;
117
- if (customizer) {
118
- // Customize OTEL with a dynamic import based on the codePath (so put it in src, generally)
119
- otelCustomizer = (await import(path.resolve(`${codepath}/${customizer}`)))
120
- .NodeSDKConfiguration;
121
- if (typeof otelCustomizer === 'object') {
122
- otelCustomizer = (v) => ({ ...v, ...(otelCustomizer as Partial<NodeSDKConfiguration>) });
123
- }
124
- }
125
- return startWithTelemetry<SLocals, RLocals>({
126
- name,
127
- rootDirectory,
128
- service: absoluteEntrypoint,
129
- codepath,
130
- version,
131
- customizer: otelCustomizer,
132
- });
133
- }
134
-
135
- // This needs to be required for TS on-the-fly to work
136
-
137
- const impl = await import(absoluteEntrypoint);
138
- const opts: ServiceStartOptions<SLocals, RLocals> = {
139
- name,
140
- version,
141
- rootDirectory,
142
- service: impl.default || impl.service,
143
- codepath,
144
- };
145
-
146
- const { startApp, listen } = await import('./express-app/app.js');
147
- const app = await startApp<SLocals, RLocals>(opts);
148
- const server = argv?.nobind ? undefined : await listen(app);
149
- return { server, app, codepath };
150
- }
151
-
152
- export function bootstrapCli<
153
- SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
154
- RLocals extends RequestLocals = RequestLocals,
155
- >(argv?: BootstrapArguments) {
156
- return bootstrap<SLocals, RLocals>({
157
- nobind: true,
158
- ...argv,
159
- });
160
- }
@@ -1,124 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
-
5
- import type { BaseConfitSchema, Confit, Factory, ShortstopHandler } from '@sesamecare-oss/confit';
6
- import { confit } from '@sesamecare-oss/confit';
7
-
8
- import { getAvailablePort } from '../development/port-finder.js';
9
-
10
- import type { ConfigurationSchema } from './schema.js';
11
-
12
- // Order matters here.
13
- const ENVIRONMENTS = ['production', 'staging', 'test', 'development'] as const;
14
-
15
- async function pathExists(f: string) {
16
- return new Promise((accept, reject) => {
17
- fs.stat(f, (err) => {
18
- if (!err) {
19
- accept(true);
20
- } else if (err.code === 'ENOENT') {
21
- accept(false);
22
- } else {
23
- reject(err);
24
- }
25
- });
26
- });
27
- }
28
-
29
- async function addDefaultConfiguration<Config extends ConfigurationSchema = ConfigurationSchema>(
30
- configFactory: Factory<Config>,
31
- directory: string,
32
- envConfit: Confit<BaseConfitSchema>,
33
- ) {
34
- const addIfEnv = async (e: (typeof ENVIRONMENTS)[number]) => {
35
- const c = path.join(directory, `${e}.json`);
36
- if (envConfit.get().env[e] && (await pathExists(c))) {
37
- configFactory.addDefault(c);
38
- return true;
39
- }
40
- return false;
41
- };
42
-
43
- await ENVIRONMENTS.reduce(
44
- (runningPromise, environment) => runningPromise.then((prev) => prev || addIfEnv(environment)),
45
- Promise.resolve(false),
46
- );
47
-
48
- const baseConfig = path.join(directory, 'config.json');
49
- if (await pathExists(baseConfig)) {
50
- configFactory.addDefault(baseConfig);
51
- }
52
- }
53
-
54
- export interface ServiceConfigurationSpec {
55
- // The LAST configuration is the most "specific" - if a configuration value
56
- // exists in all directories, the last one wins
57
- configurationDirectories: string[];
58
- shortstopHandlers: Record<string, ShortstopHandler<string, unknown>>;
59
- }
60
-
61
- export async function loadConfiguration<Config extends ConfigurationSchema>({
62
- configurationDirectories: dirs,
63
- shortstopHandlers,
64
- }: ServiceConfigurationSpec): Promise<Config> {
65
- const specificConfig = dirs[dirs.length - 1];
66
-
67
- // This confit version just gets us environment info
68
- const envConfit = await confit({ basedir: specificConfig }).create();
69
- const configFactory = confit<Config>({
70
- basedir: specificConfig,
71
- protocols: shortstopHandlers,
72
- });
73
-
74
- /**
75
- * Note that in confit, when using addDefault,
76
- * the FIRST addDefault takes precendence over the next (and so on), so
77
- * if you override this method, you should register your defaults first.
78
- */
79
- const defaultOrder = dirs.slice(0, dirs.length - 1).reverse();
80
- const __filename = fileURLToPath(import.meta.url);
81
- const __dirname = path.dirname(__filename);
82
-
83
- defaultOrder.push(path.join(__dirname, '../..', 'config'));
84
- await defaultOrder.reduce(
85
- (promise, dir) => promise.then(() => addDefaultConfiguration(configFactory, dir, envConfit)),
86
- Promise.resolve(),
87
- );
88
-
89
- const loaded = await configFactory.create();
90
-
91
- // Because other things need to know the port we choose, we pick it here if it's
92
- // configured to auto-select
93
- const serverConfig = loaded.get().server;
94
- if (serverConfig.port === 0) {
95
- const port = await getAvailablePort(8001);
96
- const store = loaded.get();
97
- store.server = store.server || {};
98
- store.server.port = port;
99
- }
100
-
101
- // TODO init other stuff based on config here, such as key management or
102
- // other cloud-aware shortstop handlers
103
-
104
- // Not sure why this is necessary, but it is
105
- return loaded.get();
106
- }
107
-
108
- export function insertConfigurationBefore(
109
- configDirs: string[] | undefined,
110
- insert: string,
111
- before: string,
112
- ) {
113
- const copy = [...(configDirs || [])];
114
- const index = copy.indexOf(before);
115
- if (index === -1) {
116
- copy.push(insert, before);
117
- } else {
118
- copy.splice(index, 0, insert);
119
- }
120
- return copy;
121
- }
122
-
123
- export * from './schema.js';
124
- export * from './validation.js';
@@ -1,70 +0,0 @@
1
- import type { BaseConfitSchema } from '@sesamecare-oss/confit';
2
- import type { middleware } from 'express-openapi-validator';
3
- import type { Level } from 'pino';
4
- import type bodyParser from 'body-parser';
5
-
6
- export interface ConfigurationItemEnabled {
7
- enabled?: boolean;
8
- }
9
-
10
- export interface ConfigurationSchema extends BaseConfitSchema {
11
- trustProxy?: string[] | boolean;
12
- logging?: {
13
- level?: Level;
14
- logRequestBody?: boolean;
15
- logResponseBody?: boolean;
16
- // Whether to log a "pre" message when request processing starts. Most useful in
17
- // situations where there is some problem causing requests to hang
18
- preLog?: boolean;
19
- };
20
- routing?: {
21
- openapi?: boolean | Partial<Parameters<typeof middleware>[0]>;
22
- // Relative to the *root directory* of the app
23
- routes?: string;
24
- // Whether to add middleware that "freezes" the query string
25
- // rather than preserving the new Express@5 behavior of reparsing
26
- // every time (which causes problems for OpenAPI validation)
27
- freezeQuery?: boolean;
28
- // Whether to compute etag headers. http://expressjs.com/en/api.html#etag.options.table
29
- etag?: boolean;
30
- cookieParser?: boolean;
31
- bodyParsers?: {
32
- json?: boolean | Parameters<typeof bodyParser.json>[0];
33
- form?: boolean | Parameters<typeof bodyParser.urlencoded>[0];
34
- };
35
- // Set static.enabled to true to enable static assets to be served
36
- static?: ConfigurationItemEnabled & {
37
- // The path relative to the root directory of the app
38
- path?: string;
39
- // The path on which to mount the static assets (defaults to /)
40
- mountPath?: string;
41
- };
42
- finalHandlers: {
43
- // Whether to create and return errors for unhandled routes
44
- notFound?: boolean;
45
- // Whether to handle errors and return them to clients
46
- // (currently means we will return JSON errors)
47
- errors?: ConfigurationItemEnabled & {
48
- render?: boolean;
49
- // Check to see if we got an error from an upstream
50
- // service that has code/domain/message, and if so return
51
- // that as is. Otherwise we will sanitize it to avoid leaking
52
- // information.
53
- unnest: boolean;
54
- };
55
- };
56
- };
57
- server: {
58
- internalPort?: number;
59
- port?: number;
60
- // To enable HTTPS on the main service, set the key and cert to the
61
- // actual key material (not the path). Use shortstop file: handler.
62
- // Note that generally it's better to offload tls termination,
63
- // but this is useful for dev.
64
- key?: string | Uint8Array;
65
- certificate?: string | Uint8Array;
66
- // If you have an alternate host name (other than localhost) that
67
- // should be used when referring to this service, set it here.
68
- hostname?: string;
69
- };
70
- }