@travetto/runtime 6.0.0-rc.1 → 6.0.0

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/README.md CHANGED
@@ -57,7 +57,7 @@ class $Runtime {
57
57
  /** Produce a workspace path for tooling, with '@' being replaced by node_module/name folder */
58
58
  toolPath(...rel: string[]): string;
59
59
  /** Resolve single module path */
60
- modulePath(modulePath: string): string;
60
+ modulePath(modulePath: string, overrides?: Record<string, string>): string;
61
61
  /** Resolve resource paths */
62
62
  resourcePaths(paths: string[] = []): string[];
63
63
  /** Get source for function */
@@ -125,7 +125,7 @@ interface EnvData {
125
125
  /**
126
126
  * trvc log level
127
127
  */
128
- TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn',
128
+ TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn';
129
129
  /**
130
130
  * Should break on first line of a method when using the @DebugBreak decorator
131
131
  * @default false
@@ -168,7 +168,7 @@ export class EnvProp<T> {
168
168
  ```
169
169
 
170
170
  ## Standard Error Support
171
- While the framework is 100 % compatible with standard `Error` instances, there are cases in which additional functionality is desired. Within the framework we use [AppError](https://github.com/travetto/travetto/tree/main/module/runtime/src/error.ts#L26) (or its derivatives) to represent framework errors. This class is available for use in your own projects. Some of the additional benefits of using this class is enhanced error reporting, as well as better integration with other modules (e.g. the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") module and HTTP status codes).
171
+ While the framework is 100 % compatible with standard `Error` instances, there are cases in which additional functionality is desired. Within the framework we use [AppError](https://github.com/travetto/travetto/tree/main/module/runtime/src/error.ts#L26) (or its derivatives) to represent framework errors. This class is available for use in your own projects. Some of the additional benefits of using this class is enhanced error reporting, as well as better integration with other modules (e.g. the [Web API](https://github.com/travetto/travetto/tree/main/module/web#readme "Declarative api for Web Applications with support for the dependency injection.") module and HTTP status codes).
172
172
 
173
173
  The [AppError](https://github.com/travetto/travetto/tree/main/module/runtime/src/error.ts#L26) takes in a message, and an optional payload and / or error classification. The currently supported error classifications are:
174
174
  * `general` - General purpose errors
@@ -193,7 +193,7 @@ The supported operations are:
193
193
  **Note**: All other console methods are excluded, specifically `trace`, `inspect`, `dir`, `time`/`timeEnd`
194
194
 
195
195
  ### How Logging is Instrumented
196
- All of the logging instrumentation occurs at transpilation time. All `console.*` methods are replaced with a call to a globally defined variable that delegates to the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/runtime/src/console.ts#L35). This module, hooks into the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/runtime/src/console.ts#L35) and receives all logging events from all files compiled by the [Travetto](https://travetto.dev).
196
+ All of the logging instrumentation occurs at transpilation time. All `console.*` methods are replaced with a call to a globally defined variable that delegates to the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/runtime/src/console.ts#L43). This module, hooks into the [ConsoleManager](https://github.com/travetto/travetto/tree/main/module/runtime/src/console.ts#L43) and receives all logging events from all files compiled by the [Travetto](https://travetto.dev).
197
197
 
198
198
  A sample of the instrumentation would be:
199
199
 
@@ -242,7 +242,7 @@ The `debug` messages can be filtered using the patterns from the [debug](https:/
242
242
  # Debug
243
243
  $ DEBUG=-@travetto/model npx trv run app
244
244
  $ DEBUG=-@travetto/registry npx trv run app
245
- $ DEBUG=@travetto/rest npx trv run app
245
+ $ DEBUG=@travetto/web npx trv run app
246
246
  $ DEBUG=@travetto/*,-@travetto/model npx trv run app
247
247
  ```
248
248
 
@@ -252,7 +252,7 @@ Additionally, the logging framework will merge [debug](https://www.npmjs.com/pac
252
252
  ```bash
253
253
 
254
254
  # Debug
255
- $ DEBUG=express:*,@travetto/rest npx trv run rest
255
+ $ DEBUG=express:*,@travetto/web npx trv run web
256
256
  ```
257
257
 
258
258
  ## Resource Access
@@ -263,7 +263,7 @@ The [FileLoader](https://github.com/travetto/travetto/tree/main/module/runtime/s
263
263
  The [FileLoader](https://github.com/travetto/travetto/tree/main/module/runtime/src/file-loader.ts#L11) also supports tying itself to [Env](https://github.com/travetto/travetto/tree/main/module/runtime/src/env.ts#L114)'s `TRV_RESOURCES` information on where to attempt to find a requested resource.
264
264
 
265
265
  ## Common Utilities
266
- Common utilities used throughout the framework. Currently [Util](https://github.com/travetto/travetto/tree/main/module/runtime/src/util.ts#L17) includes:
266
+ Common utilities used throughout the framework. Currently [Util](https://github.com/travetto/travetto/tree/main/module/runtime/src/util.ts#L12) includes:
267
267
  * `uuid(len: number)` generates a simple uuid for use within the application.
268
268
  * `allowDenyMatcher(rules[])` builds a matching function that leverages the rules as an allow/deny list, where order of the rules matters. Negative rules are prefixed by '!'.
269
269
  * `hash(text: string, size?: number)` produces a full sha512 hash.
package/__index__.ts CHANGED
@@ -1,19 +1,19 @@
1
- import type { } from './src/global';
2
- import type { } from './src/trv';
3
- export * from './src/binary';
4
- export * from './src/console';
5
- export * from './src/context';
6
- export * from './src/debug';
7
- export * from './src/error';
8
- export * from './src/exec';
9
- export * from './src/env';
10
- export * from './src/file-loader';
11
- export * from './src/function';
12
- export * from './src/manifest-index';
13
- export * from './src/queue';
14
- export * from './src/resources';
15
- export * from './src/shutdown';
16
- export * from './src/time';
17
- export * from './src/types';
18
- export * from './src/watch';
19
- export * from './src/util';
1
+ import type { } from './src/global.d.ts';
2
+ import type { } from './src/trv.d.ts';
3
+ export * from './src/binary.ts';
4
+ export * from './src/console.ts';
5
+ export * from './src/context.ts';
6
+ export * from './src/debug.ts';
7
+ export * from './src/error.ts';
8
+ export * from './src/exec.ts';
9
+ export * from './src/env.ts';
10
+ export * from './src/file-loader.ts';
11
+ export * from './src/function.ts';
12
+ export * from './src/manifest-index.ts';
13
+ export * from './src/queue.ts';
14
+ export * from './src/resources.ts';
15
+ export * from './src/shutdown.ts';
16
+ export * from './src/time.ts';
17
+ export * from './src/types.ts';
18
+ export * from './src/watch.ts';
19
+ export * from './src/util.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/runtime",
3
- "version": "6.0.0-rc.1",
3
+ "version": "6.0.0",
4
4
  "description": "Runtime for travetto applications.",
5
5
  "keywords": [
6
6
  "console-manager",
@@ -25,12 +25,12 @@
25
25
  "directory": "module/runtime"
26
26
  },
27
27
  "dependencies": {
28
- "@travetto/manifest": "^6.0.0-rc.1",
28
+ "@travetto/manifest": "^6.0.0",
29
29
  "@types/debug": "^4.1.12",
30
30
  "debug": "^4.4.0"
31
31
  },
32
32
  "peerDependencies": {
33
- "@travetto/transformer": "^6.0.0-rc.1"
33
+ "@travetto/transformer": "^6.0.0"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
package/src/binary.ts CHANGED
@@ -2,21 +2,38 @@ import path from 'node:path';
2
2
  import os from 'node:os';
3
3
  import crypto from 'node:crypto';
4
4
  import fs from 'node:fs/promises';
5
- import { PassThrough, Readable, Transform } from 'node:stream';
5
+ import { PassThrough, Readable } from 'node:stream';
6
6
  import { pipeline } from 'node:stream/promises';
7
7
  import { ReadableStream } from 'node:stream/web';
8
- import { text as toText, arrayBuffer as toBuffer } from 'node:stream/consumers';
8
+ import { text as toText, arrayBuffer as toArrayBuffer } from 'node:stream/consumers';
9
+ import { isArrayBuffer } from 'node:util/types';
9
10
 
10
- import { BinaryInput, BlobMeta } from './types';
11
- import { AppError } from './error';
12
- import { Util } from './util';
11
+ import { BinaryInput, BlobMeta, hasFunction } from './types.ts';
12
+ import { Util } from './util.ts';
13
13
 
14
- const BlobMetaSymbol = Symbol.for('@travetto/runtime:blob-meta');
14
+ const BlobMetaSymbol = Symbol();
15
15
 
16
16
  /**
17
17
  * Common functions for dealing with binary data/streams
18
18
  */
19
19
  export class BinaryUtil {
20
+ /** Is Array Buffer */
21
+ static isArrayBuffer = isArrayBuffer;
22
+ /** Is Readable */
23
+ static isReadable = hasFunction<Readable>('pipe');
24
+ /** Is ReadableStream */
25
+ static isReadableStream = hasFunction<ReadableStream>('pipeTo');
26
+ /** Is Async Iterable */
27
+ static isAsyncIterable = (v: unknown): v is AsyncIterable<unknown> =>
28
+ !!v && (typeof v === 'object' || typeof v === 'function') && Symbol.asyncIterator in v;
29
+
30
+ /**
31
+ * Is src a binary type
32
+ */
33
+ static isBinaryType(src: unknown): boolean {
34
+ return src instanceof Blob || Buffer.isBuffer(src) || this.isReadable(src) ||
35
+ this.isArrayBuffer(src) || this.isReadableStream(src) || this.isAsyncIterable(src);
36
+ }
20
37
 
21
38
  /**
22
39
  * Generate a proper sha512 hash from a src value
@@ -26,8 +43,8 @@ export class BinaryUtil {
26
43
  static hash(src: string, len: number = -1): string {
27
44
  const hash = crypto.createHash('sha512');
28
45
  hash.update(src);
29
- const ret = hash.digest('hex');
30
- return len > 0 ? ret.substring(0, len) : ret;
46
+ const digest = hash.digest('hex');
47
+ return len > 0 ? digest.substring(0, len) : digest;
31
48
  }
32
49
 
33
50
  /**
@@ -83,9 +100,9 @@ export class BinaryUtil {
83
100
  return Object.defineProperties(out, {
84
101
  size: { value: size },
85
102
  stream: { value: () => ReadableStream.from(go()) },
86
- arrayBuffer: { value: () => toBuffer(go()) },
103
+ arrayBuffer: { value: () => toArrayBuffer(go()) },
87
104
  text: { value: () => toText(go()) },
88
- bytes: { value: () => toBuffer(go()).then(v => new Uint8Array(v)) },
105
+ bytes: { value: () => toArrayBuffer(go()).then(v => new Uint8Array(v)) },
89
106
  [BlobMetaSymbol]: { value: metadata }
90
107
  });
91
108
  }
@@ -98,24 +115,6 @@ export class BinaryUtil {
98
115
  return withMeta[BlobMetaSymbol];
99
116
  }
100
117
 
101
- /**
102
- * Write limiter
103
- * @returns
104
- */
105
- static limitWrite(maxSize: number): Transform {
106
- let read = 0;
107
- return new Transform({
108
- transform(chunk, encoding, callback): void {
109
- read += (Buffer.isBuffer(chunk) || typeof chunk === 'string') ? chunk.length : (chunk instanceof Uint8Array ? chunk.byteLength : 0);
110
- if (read > maxSize) {
111
- callback(new AppError('File size exceeded', { category: 'data', details: { read, size: maxSize } }));
112
- } else {
113
- callback(null, chunk);
114
- }
115
- },
116
- });
117
- }
118
-
119
118
  /**
120
119
  * Get a hashed location/path for a blob
121
120
  */
package/src/console.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import util from 'node:util';
2
2
  import debug from 'debug';
3
3
 
4
- import { RuntimeIndex } from './manifest-index';
4
+ import { RuntimeIndex } from './manifest-index.ts';
5
5
 
6
- export type ConsoleEvent = {
6
+ /**
7
+ * @concrete
8
+ */
9
+ export interface ConsoleEvent {
7
10
  /** Time of event */
8
11
  timestamp: Date;
9
12
  /** The level of the console event */
@@ -20,6 +23,9 @@ export type ConsoleEvent = {
20
23
  args: unknown[];
21
24
  };
22
25
 
26
+ /**
27
+ * @concrete
28
+ */
23
29
  export interface ConsoleListener {
24
30
  log(ev: ConsoleEvent): void;
25
31
  }
@@ -31,6 +37,8 @@ const DEBUG_OG = { formatArgs: debug.formatArgs, log: debug.log };
31
37
  *
32
38
  * The transpiler will replace all console.* calls in the typescript files for the framework and those provided by the user.
33
39
  * Any console.log statements elsewhere will not be affected.
40
+ *
41
+ * @alias ConsoleManager
34
42
  */
35
43
  class $ConsoleManager implements ConsoleListener {
36
44
 
package/src/context.ts CHANGED
@@ -1,13 +1,12 @@
1
- import { existsSync } from 'node:fs';
2
1
  import fs from 'node:fs/promises';
3
2
  import path from 'node:path';
4
3
 
5
4
  import { type ManifestIndex, type ManifestContext, ManifestModuleUtil } from '@travetto/manifest';
6
5
 
7
- import { Env } from './env';
8
- import { RuntimeIndex } from './manifest-index';
9
- import { describeFunction } from './function';
10
- import { castTo } from './types';
6
+ import { Env } from './env.ts';
7
+ import { RuntimeIndex } from './manifest-index.ts';
8
+ import { describeFunction } from './function.ts';
9
+ import { castTo } from './types.ts';
11
10
 
12
11
  /** Constrained version of {@type ManifestContext} */
13
12
  class $Runtime {
@@ -85,8 +84,9 @@ class $Runtime {
85
84
  }
86
85
 
87
86
  /** Resolve single module path */
88
- modulePath(modulePath: string): string {
89
- const [base, sub] = (this.#resourceOverrides?.[modulePath] ?? modulePath)
87
+ modulePath(modulePath: string, overrides?: Record<string, string>): string {
88
+ const combined = { ...this.#resourceOverrides, ...overrides };
89
+ const [base, sub] = (combined[modulePath] ?? modulePath)
90
90
  .replace(/^([^#]*)(#|$)/g, (_, v, r) => `${this.#moduleAliases[v] ?? v}${r}`)
91
91
  .split('#');
92
92
  return path.resolve(this.#idx.getModule(base)?.sourcePath ?? base, sub ?? '.');
@@ -110,7 +110,7 @@ class $Runtime {
110
110
  /** Import from a given path */
111
111
  async importFrom<T = unknown>(imp?: string): Promise<T> {
112
112
  const file = path.resolve(this.#idx.mainModule.sourcePath, imp!);
113
- if (existsSync(file)) {
113
+ if (await fs.stat(file).catch(() => false)) {
114
114
  imp = this.#idx.getFromSource(file)?.import;
115
115
  }
116
116
 
@@ -121,7 +121,7 @@ class $Runtime {
121
121
  return fs.readFile(imp, 'utf8').then(JSON.parse);
122
122
  }
123
123
 
124
- if (!/[.][cm]?[tj]sx?$/.test(imp)) {
124
+ if (!ManifestModuleUtil.SOURCE_EXT_RE.test(imp)) {
125
125
  if (imp.startsWith('@')) {
126
126
  if (/[/].*?[/]/.test(imp)) {
127
127
  imp = `${imp}.ts`;
package/src/debug.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { Env } from './env';
2
- import { ClassInstance } from './types';
1
+ import { Env } from './env.ts';
2
+ import { ClassInstance } from './types.ts';
3
3
 
4
4
  /**
5
5
  * The `@DebugBreak` indicates that a function inserts an optional debugger keyword to stop on entry
package/src/env.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { castKey, castTo } from './types';
1
+ import { castKey, castTo } from './types.ts';
2
2
 
3
3
  const IS_TRUE = /^(true|yes|on|1)$/i;
4
4
  const IS_FALSE = /^(false|no|off|0)$/i;
package/src/error.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { castTo } from './types';
1
+ import { castTo } from './types.ts';
2
2
 
3
3
  export type ErrorCategory =
4
4
  'general' |
package/src/exec.ts CHANGED
@@ -2,11 +2,11 @@ import { ChildProcess } from 'node:child_process';
2
2
  import { Readable } from 'node:stream';
3
3
  import { createInterface } from 'node:readline/promises';
4
4
 
5
- import { castTo } from './types';
5
+ import { castTo } from './types.ts';
6
6
 
7
7
  const MINUTE = (1000 * 60);
8
8
 
9
- const RESULT = Symbol.for('@travetto/runtime:exec-result');
9
+ const ResultSymbol = Symbol();
10
10
 
11
11
  interface ExecutionBaseResult {
12
12
  /**
@@ -49,14 +49,18 @@ export class ExecUtil {
49
49
  * @param run The factory to produce the next running process
50
50
  * @param maxRetriesPerMinute The number of times to allow a retry within a minute
51
51
  */
52
- static async withRestart(run: () => ChildProcess, maxRetriesPerMinute?: number): Promise<ExecutionResult> {
52
+ static async withRestart(
53
+ run: () => ChildProcess,
54
+ maxRetriesPerMinute?: number,
55
+ killSignal: 'SIGINT' | 'SIGTERM' = 'SIGINT'
56
+ ): Promise<ExecutionResult> {
53
57
  const maxRetries = maxRetriesPerMinute ?? 5;
54
58
  const restarts: number[] = [];
55
59
 
56
60
  for (; ;) {
57
61
  const proc = run();
58
62
 
59
- const toKill = (): void => { proc.kill('SIGKILL'); };
63
+ const toKill = (): void => { proc.kill(killSignal); };
60
64
  const toMessage = (v: unknown): void => { proc.send?.(v!); };
61
65
 
62
66
  // Proxy kill requests
@@ -95,12 +99,12 @@ export class ExecUtil {
95
99
  static getResult(proc: ChildProcess, options: { catch?: boolean, binary?: false }): Promise<ExecutionResult<string>>;
96
100
  static getResult(proc: ChildProcess, options: { catch?: boolean, binary: true }): Promise<ExecutionResult<Buffer>>;
97
101
  static getResult<T extends string | Buffer>(proc: ChildProcess, options: { catch?: boolean, binary?: boolean } = {}): Promise<ExecutionResult<T>> {
98
- const _proc: ChildProcess & { [RESULT]?: Promise<ExecutionResult> } = proc;
99
- const res = _proc[RESULT] ??= new Promise<ExecutionResult>(resolve => {
102
+ const _proc: ChildProcess & { [ResultSymbol]?: Promise<ExecutionResult> } = proc;
103
+ const result = _proc[ResultSymbol] ??= new Promise<ExecutionResult>(resolve => {
100
104
  const stdout: Buffer[] = [];
101
105
  const stderr: Buffer[] = [];
102
106
  let done = false;
103
- const finish = (result: ExecutionBaseResult): void => {
107
+ const finish = (finalResult: ExecutionBaseResult): void => {
104
108
  if (done) {
105
109
  return;
106
110
  }
@@ -114,7 +118,7 @@ export class ExecUtil {
114
118
  const final = {
115
119
  stdout: options.binary ? buffers.stdout : buffers.stdout.toString('utf8'),
116
120
  stderr: options.binary ? buffers.stderr : buffers.stderr.toString('utf8'),
117
- ...result
121
+ ...finalResult
118
122
  };
119
123
 
120
124
  resolve(!final.valid ?
@@ -123,8 +127,8 @@ export class ExecUtil {
123
127
  );
124
128
  };
125
129
 
126
- proc.stdout?.on('data', (d: string | Buffer) => stdout.push(Buffer.from(d)));
127
- proc.stderr?.on('data', (d: string | Buffer) => stderr.push(Buffer.from(d)));
130
+ proc.stdout?.on('data', (d: string | Buffer) => stdout.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
131
+ proc.stderr?.on('data', (d: string | Buffer) => stderr.push(Buffer.isBuffer(d) ? d : Buffer.from(d)));
128
132
 
129
133
  proc.on('error', (err: Error) =>
130
134
  finish({ code: 1, message: err.message, valid: false }));
@@ -137,7 +141,7 @@ export class ExecUtil {
137
141
  }
138
142
  });
139
143
 
140
- return castTo(options.catch ? res : res.then(v => {
144
+ return castTo(options.catch ? result : result.then(v => {
141
145
  if (v.valid) {
142
146
  return v;
143
147
  } else {
@@ -3,7 +3,7 @@ import { Readable } from 'node:stream';
3
3
  import fs from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
 
6
- import { AppError } from './error';
6
+ import { AppError } from './error.ts';
7
7
 
8
8
  /**
9
9
  * File loader that will search for files across the provided search paths
@@ -56,4 +56,12 @@ export class FileLoader {
56
56
  const file = await this.resolve(relativePath);
57
57
  return createReadStream(file, { encoding: binary ? undefined : 'utf8' });
58
58
  }
59
+
60
+ /**
61
+ * Read a file as a File object
62
+ * @param relativePath The path to read
63
+ */
64
+ async readFile(relativePath: string): Promise<File> {
65
+ return new File([await this.read(relativePath, true)], path.basename(relativePath));
66
+ }
59
67
  }
package/src/function.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { ManifestModuleUtil } from '@travetto/manifest';
2
+
1
3
  export type FunctionMetadataTag = { hash: number, lines: [start: number, end: number, bodyStart?: number] };
2
4
  export type FunctionMetadata = FunctionMetadataTag & {
3
5
  id: string;
@@ -9,7 +11,7 @@ export type FunctionMetadata = FunctionMetadataTag & {
9
11
  abstract?: boolean;
10
12
  };
11
13
 
12
- const MetadataSymbol = Symbol.for('@travetto/runtime:function-metadata');
14
+ const MetadataSymbol = Symbol();
13
15
 
14
16
  const pending = new Set<Function>([]);
15
17
 
@@ -27,7 +29,7 @@ export function registerFunction(
27
29
  fn: Function, [pkg, pth]: [string, string], tag: FunctionMetadataTag,
28
30
  methods?: Record<string, FunctionMetadataTag>, abstract?: boolean,
29
31
  ): void {
30
- const modulePath = pth.replace(/[.][cm]?[tj]sx?$/, '');
32
+ const modulePath = ManifestModuleUtil.withoutSourceExtension(pth);
31
33
 
32
34
  const metadata: FunctionMetadata = {
33
35
  id: (fn.name ? `${pkg}:${modulePath}#${fn.name}` : `${pkg}:${modulePath}`),
package/src/global.d.ts CHANGED
@@ -11,4 +11,28 @@ declare global {
11
11
  /* Exposed for use within framework, only applies to framework managed classes */
12
12
  readonly Ⲑid: string;
13
13
  }
14
+
15
+ /**
16
+ * @concrete node:buffer#Blob
17
+ */
18
+ interface Blob { }
19
+
20
+ /**
21
+ * @concrete node:buffer#File
22
+ */
23
+ interface File { }
24
+
25
+ namespace NodeJS {
26
+ /**
27
+ * @concrete node:stream#Readable
28
+ */
29
+ interface ReadableStream { }
30
+ }
31
+ }
32
+
33
+ declare module 'stream' {
34
+ /**
35
+ * @concrete node:stream#Readable
36
+ */
37
+ interface Readable { }
14
38
  }
package/src/queue.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { Util } from './util';
2
-
3
1
  /**
4
2
  * An asynchronous queue
5
3
  */
@@ -7,7 +5,7 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
7
5
 
8
6
  #queue: X[] = [];
9
7
  #done = false;
10
- #ready = Util.resolvablePromise();
8
+ #ready = Promise.withResolvers<void>();
11
9
 
12
10
  /**
13
11
  * Initial set of items
@@ -31,7 +29,7 @@ export class AsyncQueue<X> implements AsyncIterator<X>, AsyncIterable<X> {
31
29
  async next(): Promise<IteratorResult<X>> {
32
30
  while (!this.#done && !this.#queue.length) {
33
31
  await this.#ready.promise;
34
- this.#ready = Util.resolvablePromise();
32
+ this.#ready = Promise.withResolvers<void>();
35
33
  }
36
34
  return { value: (this.#queue.length ? this.#queue.shift() : undefined)!, done: this.#done };
37
35
  }
package/src/resources.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Runtime } from './context';
2
- import { Env } from './env';
3
- import { FileLoader } from './file-loader';
1
+ import { Runtime } from './context.ts';
2
+ import { Env } from './env.ts';
3
+ import { FileLoader } from './file-loader.ts';
4
4
 
5
5
  /**
6
6
  * Environment aware file loader
package/src/shutdown.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { Env } from './env';
2
- import { Util } from './util';
3
- import { TimeUtil } from './time';
1
+ import { Env } from './env.ts';
2
+ import { Util } from './util.ts';
3
+ import { TimeUtil } from './time.ts';
4
4
 
5
5
  /**
6
6
  * Shutdown manager, allowing for listening for graceful shutdowns
@@ -8,14 +8,14 @@ import { TimeUtil } from './time';
8
8
  export class ShutdownManager {
9
9
 
10
10
  static #registered = false;
11
- static #handlers: { name?: string, handler: () => Promise<void> }[] = [];
11
+ static #handlers: { name?: string, handler: () => (void | Promise<void>) }[] = [];
12
12
 
13
13
  /**
14
14
  * On Shutdown requested
15
15
  * @param name name to log for
16
16
  * @param handler synchronous or asynchronous handler
17
17
  */
18
- static onGracefulShutdown(handler: () => Promise<void>, name?: string | { constructor: Function }): () => void {
18
+ static onGracefulShutdown(handler: () => (void | Promise<void>), name?: string | { constructor: Function }): () => void {
19
19
  if (!this.#registered) {
20
20
  this.#registered = true;
21
21
  const done = (): void => { this.gracefulShutdown(0); };
@@ -34,6 +34,8 @@ export class ShutdownManager {
34
34
  * Wait for graceful shutdown to run and complete
35
35
  */
36
36
  static async gracefulShutdown(code: number | string | undefined = process.exitCode): Promise<void> {
37
+ await Util.queueMacroTask(); // Force the event loop to wait one cycle
38
+
37
39
  if (code !== undefined) {
38
40
  process.exitCode = code;
39
41
  }
@@ -42,13 +44,15 @@ export class ShutdownManager {
42
44
  console.debug('Graceful shutdown: started');
43
45
 
44
46
  const items = this.#handlers.splice(0, this.#handlers.length);
45
- const handlers = Promise.all(items.map(({ name, handler }) => {
47
+ const handlers = Promise.all(items.map(async ({ name, handler }) => {
46
48
  if (name) {
47
49
  console.debug('Stopping', { name });
48
50
  }
49
- return handler().catch(err => {
51
+ try {
52
+ return await handler();
53
+ } catch (err) {
50
54
  console.error('Error shutting down', { name, err });
51
- });
55
+ }
52
56
  }));
53
57
 
54
58
  await Promise.race([
package/src/trv.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type ManifestModuleRole } from '@travetto/manifest';
2
- import { type TimeSpan } from './time';
2
+ import { type TimeSpan } from './time.ts';
3
3
  type Role = Exclude<ManifestModuleRole, 'std' | 'compile'>;
4
4
 
5
5
  declare module "@travetto/runtime" {
@@ -51,7 +51,7 @@ declare module "@travetto/runtime" {
51
51
  /**
52
52
  * trvc log level
53
53
  */
54
- TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn',
54
+ TRV_BUILD: 'none' | 'info' | 'debug' | 'error' | 'warn';
55
55
  /**
56
56
  * Should break on first line of a method when using the @DebugBreak decorator
57
57
  * @default false
package/src/types.ts CHANGED
@@ -49,6 +49,10 @@ export const hasFunction = <T>(key: keyof T) => (o: unknown): o is T =>
49
49
 
50
50
  export const hasToJSON = hasFunction<{ toJSON(): object }>('toJSON');
51
51
 
52
+ export function toConcrete<T extends unknown>(): Class<T> {
53
+ return arguments[0];
54
+ }
55
+
52
56
  /**
53
57
  * Range of bytes, inclusive
54
58
  */
package/src/util.ts CHANGED
@@ -1,13 +1,8 @@
1
1
  import crypto from 'node:crypto';
2
2
  import timers from 'node:timers/promises';
3
3
 
4
- import { castTo } from './types';
5
-
6
- type PromiseWithResolvers<T> = {
7
- resolve: (v: T) => void;
8
- reject: (err?: unknown) => void;
9
- promise: Promise<T>;
10
- };
4
+ import { castTo, hasToJSON } from './types.ts';
5
+ import { AppError } from './error.ts';
11
6
 
12
7
  type MapFn<T, U> = (val: T, i: number) => U | Promise<U>;
13
8
 
@@ -56,15 +51,6 @@ export class Util {
56
51
  return bytes.toString('hex').substring(0, len);
57
52
  }
58
53
 
59
- /**
60
- * Produce a promise that is externally resolvable
61
- */
62
- static resolvablePromise<T = void>(): PromiseWithResolvers<T> {
63
- let ops: Pick<PromiseWithResolvers<T>, 'reject' | 'resolve'>;
64
- const prom = new Promise<T>((resolve, reject) => ops = { resolve, reject });
65
- return { ...ops!, promise: prom };
66
- }
67
-
68
54
  /**
69
55
  * Map an async iterable with various mapping functions
70
56
  */
@@ -164,4 +150,49 @@ export class Util {
164
150
 
165
151
  return JSON.parse(decoded, undefined);
166
152
  }
153
+
154
+ /**
155
+ * Serialize to JSON
156
+ */
157
+ static serializeToJSON<T>(out: T): string {
158
+ return JSON.stringify(out, function (k, v) {
159
+ const ov = this[k];
160
+ if (ov && ov instanceof Error) {
161
+ return {
162
+ $: true,
163
+ ...hasToJSON(ov) ? ov.toJSON() : ov,
164
+ name: ov.name,
165
+ message: ov.message,
166
+ stack: ov.stack,
167
+ };
168
+ } else if (typeof v === 'bigint') {
169
+ return `${v.toString()}$n`;
170
+ } else {
171
+ return v;
172
+ }
173
+ });
174
+ }
175
+
176
+ /**
177
+ * Deserialize from JSON
178
+ */
179
+ static deserializeFromJson<T = unknown>(input: string): T {
180
+ return JSON.parse(input, function (k, v) {
181
+ if (v && typeof v === 'object' && '$' in v) {
182
+ const err = AppError.fromJSON(v) ?? new Error();
183
+ if (!(err instanceof AppError)) {
184
+ const { $: _, ...rest } = v;
185
+ Object.assign(err, rest);
186
+ }
187
+ err.message = v.message;
188
+ err.stack = v.stack;
189
+ err.name = v.name;
190
+ return err;
191
+ } else if (typeof v === 'string' && /^\d+[$]n$/.test(v)) {
192
+ return BigInt(v.split('$')[0]);
193
+ } else {
194
+ return v;
195
+ }
196
+ });
197
+ }
167
198
  }
package/src/watch.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { RuntimeIndex } from './manifest-index';
2
- import { ExecUtil } from './exec';
3
- import { ShutdownManager } from './shutdown';
4
- import { Util } from './util';
1
+ import { RuntimeIndex } from './manifest-index.ts';
2
+ import { ExecUtil } from './exec.ts';
3
+ import { ShutdownManager } from './shutdown.ts';
4
+ import { Util } from './util.ts';
5
5
 
6
6
  export type WatchEvent = { file: string, action: 'create' | 'update' | 'delete', output: string, module: string, time: number };
7
7
 
8
8
  export async function* watchCompiler(cfg?: { restartOnExit?: boolean, signal?: AbortSignal }): AsyncIterable<WatchEvent> {
9
9
  // Load at runtime
10
- const { CompilerClient } = await import('@travetto/compiler/support/server/client');
10
+ const { CompilerClient } = await import('@travetto/compiler/support/server/client.ts');
11
11
 
12
12
  const client = new CompilerClient(RuntimeIndex.manifest, {
13
13
  warn(message, ...args): void { console.error('warn', message, ...args); },
@@ -0,0 +1,104 @@
1
+ import * as ts from 'typescript';
2
+
3
+ import { FunctionMetadataTag } from '@travetto/runtime';
4
+ import { CoreUtil, Import, SystemUtil, TransformerState } from '@travetto/transformer';
5
+
6
+ const RegisterImportSymbol = Symbol();
7
+
8
+ interface MetadataInfo {
9
+ [RegisterImportSymbol]?: Import;
10
+ }
11
+
12
+ /**
13
+ * Utils for registering function/class metadata at compile time
14
+ */
15
+ export class MetadataRegistrationUtil {
16
+
17
+ static REGISTER_IMPORT = '@travetto/runtime/src/function.ts';
18
+ static REGISTER_FN = 'registerFunction';
19
+
20
+ static isValid({ importName: imp }: TransformerState): boolean {
21
+ return imp !== this.REGISTER_IMPORT;
22
+ }
23
+
24
+ static tag(state: TransformerState, node: ts.Node): FunctionMetadataTag {
25
+ const text = node.getText();
26
+ const hash = SystemUtil.naiveHash(text);
27
+ try {
28
+ const range = CoreUtil.getRangeOf(state.source, node) ?? [0, 0];
29
+ if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
30
+ const bodyStart = CoreUtil.getRangeOf(state.source, node?.body?.statements[0])?.[0];
31
+ if (bodyStart) {
32
+ range.push(bodyStart);
33
+ }
34
+ }
35
+ return { hash, lines: range };
36
+ } catch {
37
+ return { hash, lines: [0, 0] };
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Register metadata on a function
43
+ */
44
+ static registerFunction(state: TransformerState & MetadataInfo,
45
+ node: ts.FunctionDeclaration | ts.FunctionExpression,
46
+ src?: ts.FunctionDeclaration | ts.FunctionExpression | ts.InterfaceDeclaration | ts.TypeAliasDeclaration
47
+ ): void {
48
+ // If we have a class like function
49
+ state[RegisterImportSymbol] ??= state.importFile(this.REGISTER_IMPORT);
50
+
51
+ const tag = this.tag(state, src ?? node);
52
+ const meta = state.factory.createCallExpression(
53
+ state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN),
54
+ [],
55
+ [
56
+ state.createIdentifier(node.name!.text),
57
+ state.getModuleIdentifier(),
58
+ state.fromLiteral(tag),
59
+ ]
60
+ );
61
+ state.addStatements([state.factory.createExpressionStatement(meta)]);
62
+ }
63
+
64
+ /**
65
+ * Register metadata on a class
66
+ */
67
+ static registerClass(
68
+ state: TransformerState & MetadataInfo, node: ts.ClassDeclaration,
69
+ cls: FunctionMetadataTag, methods?: Record<string, FunctionMetadataTag>
70
+ ): ts.ClassDeclaration {
71
+
72
+ state[RegisterImportSymbol] ??= state.importFile(this.REGISTER_IMPORT);
73
+
74
+ const name = node.name?.escapedText.toString() ?? '';
75
+
76
+ const meta = state.factory.createCallExpression(
77
+ state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN),
78
+ [],
79
+ [
80
+ state.createIdentifier(name),
81
+ state.getModuleIdentifier(),
82
+ state.fromLiteral(cls),
83
+ state.extendObjectLiteral(methods ?? {}),
84
+ state.fromLiteral(CoreUtil.isAbstract(node)),
85
+ ]
86
+ );
87
+
88
+ return state.factory.updateClassDeclaration(
89
+ node,
90
+ node.modifiers,
91
+ node.name,
92
+ node.typeParameters,
93
+ node.heritageClauses,
94
+ [
95
+ state.factory.createClassStaticBlockDeclaration(
96
+ state.factory.createBlock([
97
+ state.factory.createExpressionStatement(meta)
98
+ ])
99
+ ),
100
+ ...node.members
101
+ ]
102
+ );
103
+ }
104
+ }
@@ -0,0 +1,87 @@
1
+ import ts from 'typescript';
2
+
3
+ import { TransformerState, OnInterface, OnCall, OnTypeAlias } from '@travetto/transformer';
4
+
5
+ import { MetadataRegistrationUtil } from './transformer/metadata.ts';
6
+
7
+ const SRC = '@travetto/runtime/src/types.ts';
8
+
9
+ /**
10
+ * Providing support for concrete types
11
+ */
12
+ export class ConcreteTransformer {
13
+
14
+ static #isConcreteSimple(node: ts.InterfaceDeclaration | ts.TypeAliasDeclaration): boolean {
15
+ return /^\s*[*]\s+@concrete\s*$/gm.test(node.getFullText());
16
+ }
17
+
18
+ static #createConcreteFunction(state: TransformerState, name: string | ts.Identifier): ts.FunctionDeclaration {
19
+ const final = typeof name === 'string' ? name : name.getText();
20
+
21
+ const dec = state.factory.createFunctionDeclaration(
22
+ // eslint-disable-next-line no-bitwise
23
+ state.factory.createModifiersFromModifierFlags(ts.ModifierFlags.Export | ts.ModifierFlags.Const),
24
+ undefined, `${final}$Concrete`, [], [], undefined,
25
+ state.factory.createBlock([])
26
+ );
27
+
28
+ state.addStatements([
29
+ dec,
30
+ state.factory.createExpressionStatement(
31
+ state.factory.createCallExpression(
32
+ state.createAccess('Object', 'defineProperty'),
33
+ undefined,
34
+ [
35
+ dec.name!,
36
+ state.fromLiteral('name'),
37
+ state.fromLiteral({ value: final })
38
+ ]
39
+ )
40
+ )
41
+ ]);
42
+
43
+ return dec;
44
+ }
45
+
46
+ /**
47
+ * Handle concrete interface
48
+ */
49
+ @OnInterface()
50
+ static onInterface(state: TransformerState, node: ts.InterfaceDeclaration): typeof node {
51
+ if (this.#isConcreteSimple(node)) {
52
+ const func = this.#createConcreteFunction(state, node.name);
53
+ MetadataRegistrationUtil.registerFunction(state, func, node);
54
+ }
55
+ return node;
56
+ }
57
+
58
+ /**
59
+ * Handle type alias
60
+ */
61
+ @OnTypeAlias()
62
+ static onTypeAlias(state: TransformerState, node: ts.TypeAliasDeclaration): typeof node {
63
+ if (this.#isConcreteSimple(node)) {
64
+ const func = this.#createConcreteFunction(state, node.name);
65
+ MetadataRegistrationUtil.registerFunction(state, func, node);
66
+ }
67
+ return node;
68
+ }
69
+
70
+ @OnCall()
71
+ static onToConcreteCall(state: TransformerState, node: ts.CallExpression): typeof node {
72
+ if (ts.isIdentifier(node.expression) && node.expression.text === 'toConcrete' && node.typeArguments?.length && node.arguments.length === 0) {
73
+ const type = state.resolveType(node.expression);
74
+ if ('importName' in type && type.importName === SRC) {
75
+ const [target] = node.typeArguments;
76
+ return state.factory.updateCallExpression(
77
+ node,
78
+ node.expression,
79
+ node.typeArguments,
80
+ [state.getConcreteType(target)]
81
+ );
82
+ }
83
+ }
84
+
85
+ return node;
86
+ }
87
+ }
@@ -2,7 +2,7 @@ import ts from 'typescript';
2
2
 
3
3
  import { TransformerState, OnCall, LiteralUtil, OnClass, AfterClass, OnMethod, AfterMethod, AfterFunction, OnFunction } from '@travetto/transformer';
4
4
 
5
- const CONSOLE_IMPORT = '@travetto/runtime/src/console';
5
+ const CONSOLE_IMPORT = '@travetto/runtime/src/console.ts';
6
6
 
7
7
  type CustomState = TransformerState & {
8
8
  scope: { type: 'method' | 'class' | 'function', name: string }[];
@@ -2,7 +2,7 @@ import ts from 'typescript';
2
2
 
3
3
  import { TransformerState, OnMethod, CoreUtil } from '@travetto/transformer';
4
4
 
5
- const DebugSymbol = Symbol.for('@travetto/runtime:debug');
5
+ const DebugSymbol = Symbol();
6
6
 
7
7
  /**
8
8
  * Debug transformation state
@@ -19,7 +19,7 @@ export class DebugEntryTransformer {
19
19
  @OnMethod('DebugBreak')
20
20
  static debugOnEntry(state: TransformerState & DebugState, node: ts.MethodDeclaration): ts.MethodDeclaration {
21
21
  if (!state[DebugSymbol]) {
22
- const imp = state.importFile('@travetto/runtime/src/debug').ident;
22
+ const imp = state.importFile('@travetto/runtime/src/debug.ts').ident;
23
23
  state[DebugSymbol] = CoreUtil.createAccess(state.factory, imp, 'tryDebugger');
24
24
  }
25
25
 
@@ -1,19 +1,12 @@
1
1
  import ts from 'typescript';
2
2
 
3
- import { LiteralUtil, OnCall, TransformerState } from '@travetto/transformer';
3
+ import { OnCall, TransformerState } from '@travetto/transformer';
4
4
 
5
5
  /**
6
6
  * Dynamic Import Transformer
7
7
  */
8
8
  export class DynamicImportTransformer {
9
9
 
10
- static #rewriteModuleSpecifier(state: TransformerState, spec: ts.LiteralExpression | ts.Expression | undefined): ts.Expression | undefined {
11
- if (spec && ts.isStringLiteral(spec) && state.isUntypedImport(spec)) {
12
- return LiteralUtil.fromLiteral(state.factory, `${spec.text.replace(/['"]/g, '')}.js`);
13
- }
14
- return spec;
15
- }
16
-
17
10
  @OnCall()
18
11
  static onLogCall(state: TransformerState, node: ts.CallExpression): typeof node | ts.Identifier {
19
12
  if (
@@ -22,7 +15,7 @@ export class DynamicImportTransformer {
22
15
  node.arguments.length &&
23
16
  ts.isLiteralExpression(node.arguments[0])
24
17
  ) {
25
- const lit = this.#rewriteModuleSpecifier(state, node.arguments[0])!;
18
+ const lit = state.normalizeModuleSpecifier(node.arguments[0])!;
26
19
  if (lit !== node.arguments[0]) {
27
20
  return state.factory.updateCallExpression(node, node.expression, node.typeArguments, [lit, ...node.arguments.slice(1)]);
28
21
  }
@@ -1,24 +1,16 @@
1
1
  import ts from 'typescript';
2
2
 
3
- import { TransformerState, OnMethod, OnClass, AfterClass, CoreUtil, SystemUtil, Import, OnFunction } from '@travetto/transformer';
3
+ import { TransformerState, OnMethod, OnClass, AfterClass, CoreUtil, OnFunction } from '@travetto/transformer';
4
4
 
5
- import type { FunctionMetadataTag } from '../src/function';
5
+ import type { FunctionMetadataTag } from '../src/function.ts';
6
+ import { MetadataRegistrationUtil } from './transformer/metadata.ts';
6
7
 
7
- const RUNTIME_MOD = '@travetto/runtime';
8
- const RUNTIME_MOD_SRC = `${RUNTIME_MOD}/src`;
9
- const REGISTER_IMPORT = `${RUNTIME_MOD_SRC}/function`;
10
-
11
- const methods = Symbol.for(`${RUNTIME_MOD}:methods`);
12
- const cls = Symbol.for(`${RUNTIME_MOD}:class`);
13
- const fn = Symbol.for(`${RUNTIME_MOD}:function`);
14
- const registerImport = Symbol.for(`${RUNTIME_MOD}:registerImport`);
15
- const registerFn = 'registerFunction';
8
+ const MethodsSymbol = Symbol();
9
+ const ClassSymbol = Symbol();
16
10
 
17
11
  interface MetadataInfo {
18
- [registerImport]?: Import;
19
- [methods]?: Record<string, FunctionMetadataTag>;
20
- [cls]?: FunctionMetadataTag;
21
- [fn]?: number;
12
+ [MethodsSymbol]?: Record<string, FunctionMetadataTag>;
13
+ [ClassSymbol]?: FunctionMetadataTag;
22
14
  }
23
15
 
24
16
  /**
@@ -26,35 +18,17 @@ interface MetadataInfo {
26
18
  */
27
19
  export class RegisterTransformer {
28
20
 
29
- static #tag(state: TransformerState, node: ts.Node): FunctionMetadataTag {
30
- const hash = SystemUtil.naiveHash(node.getText());
31
- try {
32
- const range = CoreUtil.getRangeOf(state.source, node) ?? [0, 0];
33
- if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
34
- const bodyStart = CoreUtil.getRangeOf(state.source, node?.body?.statements[0])?.[0];
35
- if (bodyStart) {
36
- range.push(bodyStart);
37
- }
38
- }
39
- return { hash, lines: range };
40
- } catch {
41
- return { hash, lines: [0, 0] };
42
- }
43
- }
44
-
45
- static #valid({ importName: imp }: TransformerState): boolean {
46
- return !imp.startsWith(REGISTER_IMPORT);
47
- }
48
-
49
21
  /**
50
22
  * Hash each class
51
23
  */
52
24
  @OnClass()
53
25
  static collectClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
54
- if (!this.#valid(state)) {
26
+ if (!MetadataRegistrationUtil.isValid(state)) {
55
27
  return node; // Exclude self
56
28
  }
57
- state[cls] = this.#tag(state, node);
29
+
30
+ state[ClassSymbol] = MetadataRegistrationUtil.tag(state, node);
31
+ state[MethodsSymbol] = {};
58
32
  return node;
59
33
  }
60
34
 
@@ -63,9 +37,8 @@ export class RegisterTransformer {
63
37
  */
64
38
  @OnMethod()
65
39
  static collectMethodMetadata(state: TransformerState & MetadataInfo, node: ts.MethodDeclaration): ts.MethodDeclaration {
66
- if (state[cls] && ts.isIdentifier(node.name) && !CoreUtil.isAbstract(node) && ts.isClassDeclaration(node.parent)) {
67
- state[methods] ??= {};
68
- state[methods]![node.name.escapedText.toString()] = this.#tag(state, node);
40
+ if (state[ClassSymbol] && ts.isIdentifier(node.name) && !CoreUtil.isAbstract(node) && ts.isClassDeclaration(node.parent)) {
41
+ state[MethodsSymbol]![node.name.escapedText.toString()] = MetadataRegistrationUtil.tag(state, node);
69
42
  }
70
43
  return node;
71
44
  }
@@ -75,69 +48,26 @@ export class RegisterTransformer {
75
48
  */
76
49
  @AfterClass()
77
50
  static registerClassMetadata(state: TransformerState & MetadataInfo, node: ts.ClassDeclaration): ts.ClassDeclaration {
78
- if (!state[cls]) {
51
+ if (!state[ClassSymbol]) {
79
52
  return node;
80
53
  }
81
54
 
82
- state[registerImport] ??= state.importFile(REGISTER_IMPORT);
83
-
84
- const name = node.name?.escapedText.toString() ?? '';
85
-
86
- const meta = state.factory.createCallExpression(
87
- state.createAccess(state[registerImport].ident, registerFn),
88
- [],
89
- [
90
- state.createIdentifier(name),
91
- state.getModuleIdentifier(),
92
- state.fromLiteral(state[cls]),
93
- state.extendObjectLiteral(state[methods] || {}),
94
- state.fromLiteral(CoreUtil.isAbstract(node)),
95
- ]
96
- );
97
-
98
- state[methods] = {};
99
- delete state[cls];
100
-
101
- return state.factory.updateClassDeclaration(
102
- node,
103
- node.modifiers,
104
- node.name,
105
- node.typeParameters,
106
- node.heritageClauses,
107
- [
108
- state.factory.createClassStaticBlockDeclaration(
109
- state.factory.createBlock([
110
- state.factory.createExpressionStatement(meta)
111
- ])
112
- ),
113
- ...node.members
114
- ]
115
- );
55
+ const { [MethodsSymbol]: m, [ClassSymbol]: c } = state;
56
+ delete state[ClassSymbol];
57
+ return MetadataRegistrationUtil.registerClass(state, node, c!, m);
116
58
  }
117
59
 
118
60
  /**
119
- * Give proper functions a file name
61
+ * Register proper functions
120
62
  */
121
63
  @OnFunction()
122
64
  static registerFunctionMetadata(state: TransformerState & MetadataInfo, node: ts.FunctionDeclaration | ts.FunctionExpression): typeof node {
123
- if (!this.#valid(state)) {
65
+ if (!MetadataRegistrationUtil.isValid(state) || !ts.isFunctionDeclaration(node)) {
124
66
  return node;
125
67
  }
126
68
 
127
- if (ts.isFunctionDeclaration(node) && node.name && node.parent && ts.isSourceFile(node.parent)) {
128
- // If we have a class like function
129
- state[registerImport] ??= state.importFile(REGISTER_IMPORT);
130
- const tag = this.#tag(state, node);
131
- const meta = state.factory.createCallExpression(
132
- state.createAccess(state[registerImport].ident, registerFn),
133
- [],
134
- [
135
- state.createIdentifier(node.name),
136
- state.getModuleIdentifier(),
137
- state.fromLiteral(tag),
138
- ]
139
- );
140
- state.addStatements([state.factory.createExpressionStatement(meta)]);
69
+ if (node.name && node.parent && ts.isSourceFile(node.parent)) {
70
+ MetadataRegistrationUtil.registerFunction(state, node);
141
71
  }
142
72
  return node;
143
73
  }
@@ -3,7 +3,7 @@ import ts from 'typescript';
3
3
  import { TransformerState, OnFile } from '@travetto/transformer';
4
4
 
5
5
  const PATH_REGEX = /^['"](node:)?path['"]$/;
6
- const PATH_TARGET = '@travetto/manifest/src/path';
6
+ const PATH_IMPORT = '@travetto/manifest/src/path.ts';
7
7
 
8
8
  /**
9
9
  * Rewriting path imports to use manifest's path
@@ -22,7 +22,7 @@ export class PathImportTransformer {
22
22
  stmt,
23
23
  stmt.modifiers,
24
24
  stmt.importClause,
25
- state.factory.createStringLiteral(PATH_TARGET),
25
+ state.factory.createStringLiteral(PATH_IMPORT),
26
26
  stmt.attributes
27
27
  );
28
28
  return state.factory.updateSourceFile(node, node.statements.map(x =>
@@ -1,25 +0,0 @@
1
- import ts from 'typescript';
2
-
3
- import { TransformerState, OnCall } from '@travetto/transformer';
4
-
5
- const SRC = '@travetto/runtime/src/types.ts';
6
-
7
- /**
8
- * Allows for removal of type helpers at compile time
9
- */
10
- export class TypeHelpersTransformer {
11
- @OnCall()
12
- static onTypeHelper(state: TransformerState, node: ts.CallExpression): ts.Node {
13
- if (
14
- ts.isIdentifier(node.expression) &&
15
- node.arguments.length === 1 &&
16
- /as(Class|Constructable|Full)|cast(To|Key)/.test(node.expression.escapedText.toString())
17
- ) {
18
- const type = state.resolveType(node.expression);
19
- if (type.key === 'unknown' && 'importName' in type && type.importName === SRC) {
20
- // return node.arguments[0];
21
- }
22
- }
23
- return node;
24
- }
25
- }