diff --git a/package-lock.json b/package-lock.json index 3c079d3..3842406 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ci-trap-web", - "version": "1.0.5", + "version": "1.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "ci-trap-web", - "version": "1.0.5", + "version": "1.0.6", "license": "MIT", "dependencies": { "fflate": "^0.8.0", @@ -22,6 +22,7 @@ "@rollup/plugin-commonjs": "^25.0.2", "@rollup/plugin-node-resolve": "^15.1.0", "@rollup/plugin-terser": "^0.4.3", + "@rollup/plugin-typescript": "^11.1.6", "@testing-library/dom": "^9.3.1", "@testing-library/jest-dom": "^5.16.5", "babel-jest": "^29.6.0", @@ -40,7 +41,10 @@ "jest-websocket-mock": "^2.4.0", "jsdom": "^22.1.0", "rollup": "^3.26.0", - "rollup-plugin-dotenv": "^0.5.0" + "rollup-plugin-dotenv": "^0.5.0", + "rollup-plugin-dts": "^6.1.0", + "tslib": "^2.6.2", + "typescript": "^5.4.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -2924,10 +2928,36 @@ } } }, + "node_modules/@rollup/plugin-typescript": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", + "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0||^4.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, "node_modules/@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", "dev": true, "dependencies": { "@types/estree": "^1.0.0", @@ -2938,7 +2968,7 @@ "node": ">=14.0.0" }, "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { @@ -9895,9 +9925,9 @@ } }, "node_modules/rollup": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.0.tgz", - "integrity": "sha512-YzJH0eunH2hr3knvF3i6IkLO/jTjAEwU4HoMUbQl4//Tnl3ou0e7P5SjxdDr8HQJdeUJShlbEHXrrnEHy1l7Yg==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -9926,6 +9956,40 @@ "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0" } }, + "node_modules/rollup-plugin-dts": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.1.0.tgz", + "integrity": "sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.4" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/Swatinem" + }, + "optionalDependencies": { + "@babel/code-frame": "^7.22.13" + }, + "peerDependencies": { + "rollup": "^3.29.4 || ^4", + "typescript": "^4.5 || ^5.0" + } + }, + "node_modules/rollup-plugin-dts/node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/rrweb-cssom": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", @@ -10492,6 +10556,12 @@ "node": ">=4" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -10624,17 +10694,16 @@ } }, "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { @@ -13143,10 +13212,20 @@ "terser": "^5.17.4" } }, + "@rollup/plugin-typescript": { + "version": "11.1.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz", + "integrity": "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.1.0", + "resolve": "^1.22.1" + } + }, "@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", "dev": true, "requires": { "@types/estree": "^1.0.0", @@ -18308,9 +18387,9 @@ } }, "rollup": { - "version": "3.26.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.0.tgz", - "integrity": "sha512-YzJH0eunH2hr3knvF3i6IkLO/jTjAEwU4HoMUbQl4//Tnl3ou0e7P5SjxdDr8HQJdeUJShlbEHXrrnEHy1l7Yg==", + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -18326,6 +18405,27 @@ "dotenv": "^16.0.3" } }, + "rollup-plugin-dts": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-6.1.0.tgz", + "integrity": "sha512-ijSCPICkRMDKDLBK9torss07+8dl9UpY9z1N/zTeA1cIqdzMlpkV3MOOC7zukyvQfDyxa1s3Dl2+DeiP/G6DOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "magic-string": "^0.30.4" + }, + "dependencies": { + "magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + } + } + }, "rrweb-cssom": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", @@ -18760,6 +18860,12 @@ } } }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -18855,11 +18961,10 @@ } }, "typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", - "dev": true, - "peer": true + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", + "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", + "dev": true }, "unbox-primitive": { "version": "1.0.2", diff --git a/package.json b/package.json index 6667a6e..3df3784 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { "name": "ci-trap-web", - "version": "1.0.5", + "version": "1.0.6", "description": "Lightweight mouse and touch event tracker library for browsers.", "main": "dist/trap-umd.min.js", "module": "src/trap.js", + "types": "dist/trap.d.ts", "scripts": { "build": "npm run build:prod", "build:prod": "cross-env NODE_ENV=production rollup -c", @@ -64,6 +65,7 @@ "@rollup/plugin-commonjs": "^25.0.2", "@rollup/plugin-node-resolve": "^15.1.0", "@rollup/plugin-terser": "^0.4.3", + "@rollup/plugin-typescript": "^11.1.6", "@testing-library/dom": "^9.3.1", "@testing-library/jest-dom": "^5.16.5", "babel-jest": "^29.6.0", @@ -82,6 +84,9 @@ "jest-websocket-mock": "^2.4.0", "jsdom": "^22.1.0", "rollup": "^3.26.0", - "rollup-plugin-dotenv": "^0.5.0" + "rollup-plugin-dotenv": "^0.5.0", + "rollup-plugin-dts": "^6.1.0", + "tslib": "^2.6.2", + "typescript": "^5.4.3" } } diff --git a/rollup.config.mjs b/rollup.config.mjs index 5ba1942..39339b6 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -5,6 +5,8 @@ import commonjs from '@rollup/plugin-commonjs'; import rollupDotenv from 'rollup-plugin-dotenv'; import resolve from '@rollup/plugin-node-resolve'; import terser from '@rollup/plugin-terser'; +import typescript from '@rollup/plugin-typescript'; +import dts from 'rollup-plugin-dts'; const env = dotenv.config(); dotenvExpand.expand(env); @@ -91,6 +93,16 @@ export default [{ plugins: [ terser(terserOptions), ], +}, { + // Typescript Type definitions + input: 'src/trap.js', + + output: [{ file: 'dist/trap.d.ts', format: 'es' }], + + plugins: [ + typescript(), + dts(), + ], }, { // Browser friendly, preconfigured, IIFE tracker module input: 'src/tracker.js', diff --git a/src/http.js b/src/http.js index 4fa4d39..00cb534 100644 --- a/src/http.js +++ b/src/http.js @@ -17,13 +17,6 @@ class HTTP extends Transport { simpleAutoBind(this); } - // `url` setter - // - // It replaces ${sessionId} and ${streamId} occurences with their values. - set url(url) { - this._url = this.createUrl(url); - } - // `enableCompression` setter set enableCompression(enableCompression) { this._enableCompression = !!enableCompression; diff --git a/src/metadata.js b/src/metadata.js index d2ae937..b29b432 100644 --- a/src/metadata.js +++ b/src/metadata.js @@ -136,6 +136,12 @@ class Metadata { this.submit(); } + // Remove custom metadata by key + remove(key) { + delete this._customMetadata[key]; + this.submit(); + } + // Return custom, user-defined metadata get custom() { return this._customMetadata; diff --git a/src/transport.js b/src/transport.js index 5fc4594..f032827 100644 --- a/src/transport.js +++ b/src/transport.js @@ -28,7 +28,14 @@ class Transport { this._enableCompression = DEFAULT_TRAP_ENABLE_COMPRESSION; // Set default URL - this.url = DEFAULT_TRAP_SERVER_URL; + this._url = this.createUrl(DEFAULT_TRAP_SERVER_URL); + } + + // `url` setter + // + // It replaces ${sessionId} and ${streamId} occurences with their values. + set url(url) { + this._url = this.createUrl(url); } createUrl(url) { diff --git a/src/trap.js b/src/trap.js index a94b8fa..20f3169 100644 --- a/src/trap.js +++ b/src/trap.js @@ -33,8 +33,18 @@ import { SCHEMA, } from './constants'; +/** + * Trap class for managing the data collection + * + * @class Trap + * @typedef {Trap} + */ class Trap { - // Trap constructor + /** + * Creates an instance of Trap. + * + * @constructor + */ constructor() { simpleAutoBind(this); @@ -70,82 +80,140 @@ class Trap { // Metadata API - // `streamId` getter + /** + * `streamId` getter + * + * @returns {string} + */ streamId() { return this._metadata.streamId; } - // `sessionId` getter + /** + * `sessionId` getter + * + * @returns {string} + */ sessionId() { return this._metadata.sessionId; } - // Set API key header name + /** + * Set API key header name + * + * @param {string} apiKeyName + */ apiKeyName(apiKeyName) { this._metadata.apiKeyName = apiKeyName; } - // Set API key value that is used to identify a client + /** + * Set API key value that is used to identify the integrator + * organization/person who uses the data collection library. + * + * @param {string} apiKeyValue + */ apiKeyValue(apiKeyValue) { this._metadata.apiKeyValue = apiKeyValue; } - // Deprecated alias of `apiKeyValue` setter + /** + * Deprecated alias of `apiKeyValue` setter + * @deprecated use apiKeyValue instead + * + * @param {string} apiKeyValue + */ apiKey(apiKeyValue) { this._metadata.apiKeyValue = apiKeyValue; } // Handler API - // Mount Trap to a DOM element + /** + * Mount Trap to a DOM element + * + * @param {EventTarget} element + */ mount(element) { - return this._handlers.mount(element); + this._handlers.mount(element); } - // Umount Trap from a DOM element + /** + * Umount Trap from a DOM element + * + * @param {EventTarget} element + */ umount(element) { - return this._handlers.umount(element); + this._handlers.umount(element); } - // Set Buffer's idleTimeout + /** + * Set Buffer's idleTimeout + * + * @param {number} idleTimeout + */ idleTimeout(idleTimeout) { this._buffer.idleTimeout = idleTimeout; } - // Enable/start data collection + /** + * Enable/start data collection + */ start() { this._buffer.enable(); this.addHeaderToBuffer(); this._metadata.enable(); } - // Disable/stop data collection + /** + * Disable/stop data collection + */ stop() { this._buffer.disable(); this._metadata.disable(); } - // `bufferSizeLimit` setter proxy + /** + * `bufferSizeLimit` setter proxy + * + * @param {number} bufferSizeLimit + */ bufferSizeLimit(bufferSizeLimit) { this._buffer.bufferSizeLimit = bufferSizeLimit; } - // `bufferTimeout` setter proxy + /** + * `bufferTimeout` setter proxy + * + * @param {number} bufferTimeout + */ bufferTimeout(bufferTimeout) { this._buffer.bufferTimeout = bufferTimeout; } - // `enableCompression` setter proxy + /** + * `enableCompression` setter proxy + * + * @param {boolean} enableCompression + */ enableCompression(enableCompression) { this.state.transport.enableCompression = enableCompression; } - // Remote Trap server URL setter + /** + * Remote Trap server URL setter + * + * @param {string} url + */ url(url) { this.state.transport.url = url; } - // Inject and later send custom event to stream + /** + * Inject and later send custom event to stream + * + * @param {string|object} props + */ send(props) { if (typeof props === 'string') { this.pushMessage([ @@ -162,11 +230,22 @@ class Trap { } } + /** + * Add message to buffer + * @private + * + * @param {*} message + */ pushMessage(message) { this._buffer.push(...message); } - // Submit data manually + /** + * Submit data manually + * + * @param {boolean} final + * @returns {Promise} + */ submit(final) { if (this._buffer.isEmpty()) { return new Promise(() => { }); @@ -179,6 +258,10 @@ class Trap { return this.state.transport.submit(events); } + /** + * Add the header message to the buffer + * @private + */ addHeaderToBuffer() { // eslint-disable-next-line no-plusplus const sequenceNumber = this.state.sequenceNumber++; @@ -193,19 +276,52 @@ class Trap { ); } - // Set application specific, custom metadata key-value pair + /** + * Set application specific, custom metadata key-value pair + * @deprecated Use addCustomMetadata instead + * + * @param {string} key + * @param {string} value + */ metadata(key, value) { + this.addCustomMetadata(key, value); + } + + /** + * Set application specific, custom metadata key-value pair + * + * @param {string} key + * @param {string} value + */ + addCustomMetadata(key, value) { this._metadata.set(key, value); } - // Return application specified custom metadata + /** + * Remove application specific, custom metadata by key + * + * @param {string} key + */ + removeCustomMetadata(key) { + this._metadata.remove(key); + } + + /** + * Return application specified custom metadata + * + * @returns {object} + */ customMetadata() { return this._metadata.custom; } - // TODO: remove this if it is unnecessary - // - // Set log destination helper to an element + /** + * Set log destination helper to an element + * + * TODO: remove this if it is unnecessary + * + * @param {string|object|any} destination + */ setLogDestination(destination) { switch (typeof destination) { case 'string': @@ -224,6 +340,11 @@ class Trap { } } + /** + * Sets the transport method. `True` sets WS, `False` sets HTTP method. + * + * @param {boolean} useWsTransport + */ setUseWsTransport(useWsTransport) { this.state.transport.close(); @@ -234,7 +355,11 @@ class Trap { } } - // Log arbitrary messages + /** + * Log arbitrary messages + * + * @param {...any} props + */ log(...props) { this.state.logger(...props); } @@ -243,7 +368,11 @@ class Trap { // Append mixins Object.assign(Trap.prototype, disableTouchEventMixin); -// Make it a singleton instance +/** + * Singleton instance of Trap + * + * @type {Trap} + */ const instance = new Trap(); Object.freeze(instance); diff --git a/src/ws.js b/src/ws.js index b8b434a..839fced 100644 --- a/src/ws.js +++ b/src/ws.js @@ -31,7 +31,7 @@ class WS extends Transport { this._socket.close(); this._socket = null; } - this._url = this.createUrl(url); + super.url = url; } // Submit diff --git a/test/trap.custom-metadata.test.js b/test/trap.custom-metadata.test.js index b79d8f1..247907f 100644 --- a/test/trap.custom-metadata.test.js +++ b/test/trap.custom-metadata.test.js @@ -5,6 +5,8 @@ import { METADATA_MESSAGE_TYPE } from '../src/constants'; import trap from '../src/trap'; const initialHtml = 'some text'; +const customKey = 'custom-key'; +const customValue = 'custom-value'; describe('custom metadata', () => { beforeAll(() => { @@ -31,7 +33,7 @@ describe('custom metadata', () => { fetch.mockResponse(() => Promise.resolve({ result: 'ok' })); // Set custom K/V pair - trap.metadata('custom-key', 'custom-value'); + trap.metadata(customKey, customValue); // Send a simple custom message trap.send('message'); @@ -47,6 +49,42 @@ describe('custom metadata', () => { // Expect a single chunk to be submitted with the actual "custom-key": // "custom-value" keypair serialized into the body - expect(metadata).toHaveProperty('2.custom.custom-key', 'custom-value'); + expect(metadata).toHaveProperty(`2.custom.${customKey}`, customValue); + }); + + test('Add and remove custom metadata', () => { + // Set up fetch() mocks + fetch.mockResponse(() => Promise.resolve({ result: 'ok' })); + + // Set custom K/V pair + trap.addCustomMetadata(customKey, customValue); + + // Manually invoke chunk submission + trap.submit(); + + // Fetch "fetch body" and parse its JSON + let jsonBody = JSON.parse(fetch.mock.calls[0][1].body); + + // Select first metadata event + let metadata = jsonBody.filter((e) => e[0] === METADATA_MESSAGE_TYPE)[0]; + + // Expect a single chunk to be submitted with the actual "custom-key": + // "custom-value" keypair serialized into the body + expect(metadata).toHaveProperty(`2.custom.${customKey}`, customValue); + + // Removes the custom key + trap.removeCustomMetadata(customKey); + + // Manually invoke chunk submission + trap.submit(); + + // Fetch "fetch body" and parse its JSON + jsonBody = JSON.parse(fetch.mock.calls[1][1].body); + + // Select first metadata event + [metadata] = jsonBody.filter((e) => e[0] === METADATA_MESSAGE_TYPE); + + // Expect no custom metadata + expect(metadata).toHaveProperty('2.custom', {}); }); }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6465c60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "include": ["src/**/*"], + "compilerOptions": { + "allowJs": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist", + "declarationMap": true + } +}