@contrast/rewriter 1.4.2 → 1.6.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/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright: 2022 Contrast Security, Inc
1
+ Copyright: 2024 Contrast Security, Inc
2
2
  Contact: support@contrastsecurity.com
3
3
  License: Commercial
4
4
 
package/README.md CHANGED
@@ -2,32 +2,5 @@
2
2
 
3
3
  Rewrite javascript code with custom rewrite transforms.
4
4
 
5
- For example, Assess will register transforms for `+` -> `contrast_add()` so that it can perform propagation
6
- via instrumentation of `contrast_add()`.
7
-
8
-
9
- #### Example Service Usage
10
-
11
- ```typescript
12
- const { Rewriter } = require('.');
13
-
14
- const rewriter = new Rewriter({ logger });
15
-
16
- rewriter.addTransforms({
17
- BinaryExpression(path) {
18
- const method = methodLookups[path.node.operator];
19
- if (method) {
20
- path.replaceWith(
21
- t.callExpression(
22
- expression('ContrastMethods.%%method%%')({ method }), [
23
- path.node.left,
24
- path.node.right
25
- ]
26
- )
27
- );
28
- }
29
- }
30
- });
31
-
32
- const result = rewriter.rewrite('function add(x, y) { return x + y; }');
33
- ```
5
+ For example, Assess will register transforms for `+` -> `ContrastMethods.add()`
6
+ so that it can perform propagation via instrumentation of `ContrastMethods.add()`.
package/lib/cache.js ADDED
@@ -0,0 +1,263 @@
1
+ /*
2
+ * Copyright: 2024 Contrast Security, Inc
3
+ * Contact: support@contrastsecurity.com
4
+ * License: Commercial
5
+
6
+ * NOTICE: This Software and the patented inventions embodied within may only be
7
+ * used as part of Contrast Security’s commercial offerings. Even though it is
8
+ * made available through public repositories, use of this Software is subject to
9
+ * the applicable End User Licensing Agreement found at
10
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
11
+ * between Contrast Security and the End User. The Software may not be reverse
12
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
13
+ * way not consistent with the End User License Agreement.
14
+ */
15
+ // @ts-check
16
+ 'use strict';
17
+
18
+ const fs = require('node:fs');
19
+ const fsPromises = require('node:fs/promises');
20
+ const os = require('node:os');
21
+ const path = require('node:path');
22
+
23
+ /**
24
+ * Returns the modification time of a file as a number.
25
+ * @param {string} filename
26
+ * @returns {Promise<number>}
27
+ */
28
+ const mtime = async (filename) => +(await fsPromises.stat(filename)).mtime;
29
+
30
+ /**
31
+ * Returns the modification time of a file as a number.
32
+ * @param {string} filename
33
+ * @returns {number}
34
+ */
35
+ const mtimeSync = (filename) => +fs.statSync(filename).mtime;
36
+
37
+ module.exports.Cache = class Cache {
38
+ /**
39
+ * @param {import('.').Core} core
40
+ */
41
+ constructor(core) {
42
+ this.config = core.config;
43
+ this.logger = core.logger.child({ name: 'contrast:rewriter:cache' });
44
+ /** @type {Set<import('.').Mode>} */
45
+ this.modes = new Set();
46
+ this.appDirRegex = new RegExp(`^${core.appInfo.app_dir.replace(/\\/g, '\\\\')}`);
47
+ this.cacheDir = path.join(
48
+ core.config.agent.node.rewrite.cache.path,
49
+ core.appInfo.name.replace('/', '_'),
50
+ core.agentVersion,
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Sets the rewriter to 'assess' or 'protect' mode, enabling different
56
+ * transforms.
57
+ * @param {import('.').Mode} mode
58
+ */
59
+ install(mode) {
60
+ this.modes.add(mode);
61
+ }
62
+
63
+ /**
64
+ * Returns the filename of a cached rewrite result. Paths within the `app_dir`
65
+ * directory are nested under the `_` directory to prevent potential
66
+ * collisions with absolute paths.
67
+ * /path/to/app/node_modules/mod/index.js
68
+ * -> '.contrast/app_name/5.1.2/assess/_/node_modules/mod/index.js
69
+ * /somewhere/else/index.js
70
+ * -> .contrast/app_name/5.1.2/assess/somewhere/else/index.js
71
+ * @param {string} filename
72
+ * @returns {string}
73
+ */
74
+ getCachedFilename(filename) {
75
+ filename = filename.replace(this.appDirRegex, '_');
76
+
77
+ if (os.platform() === 'win32') {
78
+ filename = filename.replace(/^([A-Za-z]):/, '$1_');
79
+ }
80
+
81
+ return path.join(
82
+ this.cacheDir,
83
+ this.modes.has('assess') ? 'assess' : 'protect',
84
+ filename,
85
+ );
86
+ }
87
+
88
+ /**
89
+ * Looks up and returns the cached filename for a previously rewritten file
90
+ * asynchronously.
91
+ * @param {string} filename
92
+ * @returns {Promise<string | undefined>}
93
+ */
94
+ async find(filename) {
95
+ const filenameCached = this.getCachedFilename(filename);
96
+
97
+ try {
98
+ const [time, timeCached] = await Promise.all([mtime(filename), mtime(filenameCached)]);
99
+ if (time > timeCached) {
100
+ this.logger.trace(
101
+ {
102
+ filename,
103
+ filenameCached,
104
+ mtime: time,
105
+ mtimeCached: timeCached,
106
+ },
107
+ 'Cache stale, falling back to compiling.'
108
+ );
109
+
110
+ return undefined;
111
+ }
112
+
113
+ this.logger.trace(
114
+ {
115
+ filename,
116
+ filenameCached,
117
+ mtime: time,
118
+ mtimeCached: timeCached,
119
+ },
120
+ 'Cache current.'
121
+ );
122
+
123
+ return filenameCached;
124
+ } catch (err) {
125
+ // @ts-expect-error ts treats errors poorly.
126
+ if (err.code !== 'ENOENT') {
127
+ this.logger.error(
128
+ { err, filename, filenameCached },
129
+ 'An unexpected error occurred, falling back to compiling.'
130
+ );
131
+ } else {
132
+ this.logger.trace(
133
+ { filename, filenameCached },
134
+ 'Cache miss, falling back to compiling.',
135
+ );
136
+ }
137
+
138
+ return undefined;
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Looks up and returns the source map for a previously rewritten file.
144
+ * @param {string} filename
145
+ * @returns {Promise<string | undefined>}
146
+ */
147
+ async readMap(filename) {
148
+ const filenameCached = this.getCachedFilename(filename);
149
+ const sourceMap = `${filenameCached}.map`;
150
+
151
+ try {
152
+ return fsPromises.readFile(sourceMap, 'utf8');
153
+ } catch (err) {
154
+ // @ts-expect-error ts treats errors poorly.
155
+ if (err.code !== 'ENOENT') {
156
+ this.logger.warn(
157
+ { err, filename, filenameCached, sourceMap },
158
+ 'An unexpected error occurred finding source map.'
159
+ );
160
+ }
161
+ return undefined;
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Looks up and returns the string content of a previously rewritten file
167
+ * synchronously. Used when we need to block on cache lookups.
168
+ * @param {string} filename
169
+ * @returns {string | undefined}
170
+ */
171
+ readSync(filename) {
172
+ const filenameCached = this.getCachedFilename(filename);
173
+
174
+ try {
175
+ const time = mtimeSync(filename);
176
+ const timeCached = mtimeSync(filenameCached);
177
+ if (time > timeCached) {
178
+ this.logger.trace(
179
+ {
180
+ filename,
181
+ filenameCached,
182
+ mtime: time,
183
+ mtimeCached: timeCached,
184
+ },
185
+ 'Cache stale, falling back to compiling.'
186
+ );
187
+
188
+ return undefined;
189
+ }
190
+
191
+ this.logger.trace(
192
+ {
193
+ filename,
194
+ filenameCached,
195
+ mtime: time,
196
+ mtimeCached: timeCached,
197
+ },
198
+ 'Cache current.'
199
+ );
200
+
201
+ return fs.readFileSync(filenameCached, 'utf8');
202
+ } catch (err) {
203
+ // @ts-expect-error ts treats errors poorly.
204
+ if (err.code !== 'ENOENT') {
205
+ this.logger.error(
206
+ { err, filename, filenameCached },
207
+ 'An unexpected error occurred, falling back to compiling.'
208
+ );
209
+ } else {
210
+ this.logger.trace(
211
+ { filename, filenameCached },
212
+ 'Cache miss, falling back to compiling.',
213
+ );
214
+ }
215
+
216
+ return undefined;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Writes a rewritten file to the cache directory. Runs asynchronously so that
222
+ * disk I/O doesn't impact startup times, regardless of whether we're in a CJS
223
+ * or ESM environment.
224
+ * @param {string} filename
225
+ * @param {import('@swc/core').Output} result
226
+ */
227
+ async write(filename, result) {
228
+ const filenameCached = this.getCachedFilename(filename);
229
+
230
+ try {
231
+ await fsPromises.mkdir(path.dirname(filenameCached), { recursive: true });
232
+
233
+ const writePromises = [
234
+ fsPromises.writeFile(filenameCached, result.code, 'utf8')
235
+ ];
236
+
237
+ if (result.map) {
238
+ writePromises.push(
239
+ fsPromises.writeFile(`${filenameCached}.map`, result.map, 'utf8')
240
+ );
241
+ }
242
+
243
+ await Promise.all(writePromises);
244
+
245
+ this.logger.trace(
246
+ {
247
+ filename,
248
+ filenameCached,
249
+ },
250
+ 'Cache entry created.'
251
+ );
252
+ } catch (err) {
253
+ this.logger.warn(
254
+ {
255
+ err,
256
+ filename,
257
+ filenameCached,
258
+ },
259
+ 'Unable to cache rewrite results.'
260
+ );
261
+ }
262
+ }
263
+ };
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -13,25 +13,108 @@
13
13
  * way not consistent with the End User License Agreement.
14
14
  */
15
15
  // @ts-check
16
-
17
16
  'use strict';
18
17
 
19
- const { transformSync } = require('@swc/core');
20
- const Module = require('module');
18
+ const { transform, transformSync } = require('@swc/core');
19
+ const Module = require('node:module');
20
+ const { Cache } = require('./cache');
21
+ const INJECTION_FIXES = [
22
+ // assigning to global breaks things like `"string".constructor === global.String`
23
+ { search: 'global.Function = global.ContrastMethods.Function', replace: 'var Function = global.ContrastMethods?.Function' },
24
+ { search: 'global.Number = global.ContrastMethods.Number', replace: 'var Number = global.ContrastMethods?.Number' },
25
+ { search: 'global.Object = global.ContrastMethods.Object', replace: 'var Object = global.ContrastMethods?.Object' },
26
+ { search: 'global.String = global.ContrastMethods.String', replace: 'var String = global.ContrastMethods?.String' },
27
+ ];
21
28
 
22
- const rewriterPath = require.resolve('@contrast/agent-swc-plugin');
23
- const unwriterPath = require.resolve('@contrast/agent-swc-plugin-unwrite');
29
+
30
+ /**
31
+ * @typedef {Object} Core
32
+ * @prop {import('@contrast/common').AppInfo} appInfo
33
+ * @prop {string} agentVersion
34
+ * @prop {import('@contrast/config').Config} config
35
+ * @prop {import('@contrast/logger').Logger} logger
36
+ */
37
+ /**
38
+ * @typedef {'assess' | 'protect'} Mode
39
+ */
40
+ /**
41
+ * @typedef {Object} RewriteOpts
42
+ * @prop {string=} filename e.g. 'index.js'
43
+ * @prop {boolean=} isModule if true, file is parsed as an ES module instead of a CJS script
44
+ * @prop {boolean=} inject if true, injects ContrastMethods on the global object
45
+ * @prop {boolean=} wrap if true, wraps the content with a modified module wrapper IIFE
46
+ * @prop {boolean=} trim if true, removes added characters from the end of the generated code
47
+ */
24
48
 
25
49
  // @ts-expect-error `wrapper` is missing from @types/node
26
50
  const prefix = Module.wrapper[0];
27
51
  // @ts-expect-error `wrapper` is missing from @types/node
28
52
  const suffix = Module.wrapper[1].replace(/;$/, '.apply(this, arguments);');
29
53
 
30
- /** @typedef {'assess' | 'protect'} Mode */
54
+ const rewriterPath = require.resolve('@contrast/agent-swc-plugin');
55
+ const unwriterPath = require.resolve('@contrast/agent-swc-plugin-unwrite');
56
+
57
+ /**
58
+ * Wraps the source content as necessary to support rewriting.
59
+ * Wrapping must occur before rewriting since the underlying rewriter cannot
60
+ * parse certain valid statements such as `return` statements in a CJS script.
61
+ * @param {string} content
62
+ * @returns {string}
63
+ */
64
+ const wrap = (content) => {
65
+ let shebang = '';
66
+
67
+ // The shebang will be commented out since it cannot be present in a
68
+ // function body. swc doesn't include the commented shebang in the generated
69
+ // code despite including comments otherwise.
70
+ if (content.charAt(0) === '#') {
71
+ shebang = content.substring(0, content.indexOf('\n') + 1);
72
+ content = `//${content}`;
73
+ }
31
74
 
32
- const rewriter = {
33
- /** @type {Set<Mode>} */
34
- modes: new Set(),
75
+ content = `${shebang}${prefix}${content}${suffix}`;
76
+
77
+ return content;
78
+ };
79
+
80
+ /**
81
+ * Trims extraneous characters that may have been added by the rewriter.
82
+ * Handles newline or semicolon insertion, removing the added characters if they
83
+ * were not present in the original source content.
84
+ * @param {string} content
85
+ * @param {import('@swc/core').Output} result
86
+ * @returns {import('@swc/core').Output}
87
+ */
88
+ const trim = (content, result) => {
89
+ let carriageReturn = 0;
90
+ // swc always adds a newline, so we only need to check the input
91
+ if (!content.endsWith('\n')) {
92
+ result.code = result.code.substring(0, result.code.length - 1);
93
+ } else if (content.endsWith('\r\n')) {
94
+ // if EOL is \r\n, then we need to account for that when we check the
95
+ // negative index of the last semicolon below
96
+ carriageReturn = 1;
97
+ }
98
+ const resultSemicolonIdx = result.code.lastIndexOf(';');
99
+ const contentSemicolonIdx = content.lastIndexOf(';');
100
+ if (contentSemicolonIdx === -1 || resultSemicolonIdx - result.code.length !== contentSemicolonIdx - content.length + carriageReturn) {
101
+ result.code = result.code.substring(0, resultSemicolonIdx) + result.code.substring(resultSemicolonIdx + 1, result.code.length);
102
+ }
103
+
104
+ return result;
105
+ };
106
+
107
+ class Rewriter {
108
+ /**
109
+ * @param {Core} core
110
+ */
111
+ constructor(core) {
112
+ this.core = core;
113
+ this.logger = core.logger.child({ name: 'contrast:rewriter' });
114
+ /** @type {Set<Mode>} */
115
+ this.modes = new Set();
116
+ this.cache = new Cache(core);
117
+ }
35
118
 
36
119
  /**
37
120
  * Sets the rewriter to 'assess' or 'protect' mode, enabling different
@@ -39,94 +122,125 @@ const rewriter = {
39
122
  * @param {Mode} mode
40
123
  */
41
124
  install(mode) {
125
+ this.logger.trace('installing rewriter mode: %s', mode);
42
126
  this.modes.add(mode);
43
- },
127
+ this.cache.install(mode);
128
+ }
44
129
 
45
130
  /**
46
- * @param {string} content the source code
47
- * @param {object} opts
48
- * @param {string=} opts.filename e.g. 'index.js'
49
- * @param {boolean=} opts.isModule if true, file is parsed as an ES module instead of a CJS script
50
- * @param {boolean=} opts.inject if true, injects ContrastMethods on the global object
51
- * @param {boolean=} opts.wrap if true, wraps the content with a modified module wrapper IIFE
52
- * @returns {import("@swc/core").Output}
131
+ * @param {RewriteOpts} opts
132
+ * @returns {import('@swc/core').Options}
53
133
  */
54
- rewrite(content, opts = {}) {
55
- let shebang = '';
56
-
57
- if (content.charAt(0) === '#') {
58
- shebang = content.substring(0, content.indexOf('\n') + 1);
59
- // see the test output: swc doesn't include the commented shebang in the generated code despite including comments otherwise
60
- content = `//${content}`;
61
- }
62
-
63
- if (opts.wrap) {
64
- content = `${shebang}${prefix}${content}${suffix}`;
65
- }
66
-
67
- const result = transformSync(content, {
134
+ rewriteConfig(opts) {
135
+ return {
68
136
  filename: opts.filename,
69
137
  isModule: opts.isModule,
138
+ env: {
139
+ targets: {
140
+ node: process.versions.node
141
+ }
142
+ },
70
143
  jsc: {
71
- target: 'es2019', // should work for node >14
72
144
  experimental: {
73
- plugins: [
74
- [
75
- rewriterPath,
76
- {
77
- assess: this.modes.has('assess'),
78
- inject: opts.inject,
79
- },
80
- ],
81
- ],
145
+ plugins: [[rewriterPath, {
146
+ assess: this.modes.has('assess'),
147
+ inject: opts.inject,
148
+ }]],
82
149
  },
83
150
  },
84
- sourceMaps: true,
85
- });
86
-
87
- if (!opts.wrap) {
88
- let carriageReturn = 0;
89
- // swc always adds a newline, so we only need to check the input
90
- if (!content.endsWith('\n')) {
91
- result.code = result.code.substring(0, result.code.length - 1);
92
- } else if (content.endsWith('\r\n')) {
93
- // if EOL is \r\n, then we need to account for that when we check the
94
- // negative index of the last semicolon below
95
- carriageReturn = 1;
151
+ // if we're trimming the output we're not rewriting an entire file, which
152
+ // means source maps are not relevant.
153
+ sourceMaps: !opts.trim && this.core.config.agent.node.source_maps.enable,
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Rewrites the provided source code string asynchronously to be consumed by
159
+ * ESM hooks.
160
+ * @param {string} content
161
+ * @param {RewriteOpts=} opts
162
+ * @returns {Promise<import('@swc/core').Output>}
163
+ */
164
+ async rewrite(content, opts = {}) {
165
+ this.logger.trace({ opts }, 'rewriting %s', opts.filename);
166
+
167
+ if (opts.wrap) {
168
+ content = wrap(content);
169
+ }
170
+
171
+ let result = await transform(content, this.rewriteConfig(opts));
172
+
173
+ if (opts.trim) {
174
+ result = trim(content, result);
175
+ }
176
+
177
+ for (const { search, replace } of INJECTION_FIXES) {
178
+ if (result.code.indexOf(search) >= 0) {
179
+ result.code = result.code.replace(search, replace);
96
180
  }
97
- const resultSemicolonIdx = result.code.lastIndexOf(';');
98
- const contentSemicolonIdx = content.lastIndexOf(';');
99
- if (contentSemicolonIdx === -1 || resultSemicolonIdx - result.code.length !== contentSemicolonIdx - content.length + carriageReturn) {
100
- result.code = result.code.substring(0, resultSemicolonIdx) + result.code.substring(resultSemicolonIdx + 1, result.code.length);
181
+ }
182
+
183
+ return result;
184
+ }
185
+
186
+ /**
187
+ * Rewrites the provided source code string synchronously to be consumed by
188
+ * CJS hooks.
189
+ * @param {string} content
190
+ * @param {RewriteOpts=} opts
191
+ * @returns {import('@swc/core').Output}
192
+ */
193
+ rewriteSync(content, opts = {}) {
194
+ this.logger.trace({ opts }, 'rewriting %s', opts.filename);
195
+
196
+ if (opts.wrap) {
197
+ content = wrap(content);
198
+ }
199
+
200
+ let result = transformSync(content, this.rewriteConfig(opts));
201
+
202
+ if (opts.trim) {
203
+ result = trim(content, result);
204
+ }
205
+
206
+ for (const { search, replace } of INJECTION_FIXES) {
207
+ if (result.code.indexOf(search) >= 0) {
208
+ result.code = result.code.replace(search, replace);
101
209
  }
102
210
  }
103
211
 
104
212
  return result;
105
- },
213
+ }
106
214
 
107
215
  /**
216
+ * Removes contrast-related rewritten code from provided source code string.
108
217
  * @param {string} content
109
218
  * @returns {string}
110
219
  */
111
- unwrite(content) {
220
+ unwriteSync(content) {
112
221
  return transformSync(content, {
222
+ env: {
223
+ targets: {
224
+ node: process.versions.node
225
+ }
226
+ },
113
227
  jsc: {
114
- target: 'es2019', // should work for node >14
115
228
  experimental: {
116
229
  plugins: [[unwriterPath, {}]],
117
230
  },
118
231
  },
232
+ sourceMaps: false,
119
233
  }).code;
120
- },
121
- };
122
-
123
- /** @typedef {typeof rewriter} Rewriter */
234
+ }
235
+ }
124
236
 
125
237
  /**
126
- * @param {{ rewriter: Rewriter }} core
238
+ * @param {Core & { rewriter?: Rewriter; }} core
127
239
  * @returns {Rewriter}
128
240
  */
129
241
  module.exports = function init(core) {
130
- core.rewriter = rewriter;
131
- return rewriter;
242
+ core.rewriter = new Rewriter(core);
243
+ return core.rewriter;
132
244
  };
245
+
246
+ module.exports.Rewriter = Rewriter;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2024 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -19,27 +19,26 @@ const { readFileSync, existsSync } = require('fs');
19
19
  const { transfer } = require('multi-stage-sourcemap');
20
20
  const { SourceMapConsumer } = require('@contrast/synchronous-source-maps');
21
21
 
22
- module.exports = function(deps) {
22
+ module.exports = function (deps) {
23
23
  const sourceMaps = deps.rewriter.sourceMaps = {};
24
24
  const consumerCache = sourceMaps.consumerCache = {};
25
25
 
26
- sourceMaps.cacheConsumerMap = function(filename, map) {
26
+ sourceMaps.cacheConsumerMap = function (filename, map) {
27
27
  consumerCache[filename] = new SourceMapConsumer(map);
28
28
  };
29
29
 
30
30
  /**
31
31
  */
32
- sourceMaps.chain = function(filename, map) {
32
+ sourceMaps.chain = function (filename, map) {
33
33
  let ret;
34
34
 
35
35
  if (existsSync(`${filename}.map`)) {
36
36
  try {
37
37
  const existingMap = JSON.parse(readFileSync(`${filename}.map`, 'utf8'));
38
38
  ret = transfer({ fromSourceMap: map, toSourceMap: existingMap });
39
- deps.logger.trace(`Merged sourcemap from ${filename}.map`);
40
- } catch (e) {
41
- deps.logger.debug(`Unable to read ${filename}.map.js`);
42
- deps.logger.debug(`${e}`);
39
+ deps.logger.trace('Merged sourcemap from %s.map', filename);
40
+ } catch (err) {
41
+ deps.logger.debug({ err }, 'Unable to read %s.map.js', filename);
43
42
  }
44
43
  }
45
44
 
package/package.json CHANGED
@@ -1,14 +1,11 @@
1
1
  {
2
2
  "name": "@contrast/rewriter",
3
- "version": "1.4.2",
3
+ "version": "1.6.0",
4
4
  "description": "A transpilation tool mainly used for instrumentation",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
7
- "files": [
8
- "lib/"
9
- ],
10
7
  "main": "lib/index.js",
11
- "types": "lib/index.d.ts",
8
+ "types": "types/index.d.ts",
12
9
  "engines": {
13
10
  "npm": ">=6.13.7 <7 || >= 8.3.1",
14
11
  "node": ">= 14.15.0"
@@ -17,10 +14,11 @@
17
14
  "test": "../scripts/test.sh"
18
15
  },
19
16
  "dependencies": {
20
- "@contrast/agent-swc-plugin": "^1.2.0",
21
- "@contrast/agent-swc-plugin-unwrite": "^1.2.0",
17
+ "@contrast/agent-swc-plugin": "1.3.0",
18
+ "@contrast/agent-swc-plugin-unwrite": "1.3.0",
19
+ "@contrast/common": "1.20.0",
22
20
  "@contrast/synchronous-source-maps": "^1.1.3",
23
21
  "@swc/core": "1.3.39",
24
22
  "multi-stage-sourcemap": "^0.3.1"
25
23
  }
26
- }
24
+ }