deploy: current vibn theia state
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
2026-02-27 12:01:08 -08:00
commit 8bb5110148
3782 changed files with 640947 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: [
'../../configs/build.eslintrc.json'
],
parserOptions: {
tsconfigRootDir: __dirname,
project: 'tsconfig.json'
}
};

View File

@@ -0,0 +1,3 @@
# `ffmeg.node`
This is a [Node Native Addon](https://nodejs.org/docs/latest-v14.x/api/n-api.html) to dynamically link to Electron's `ffmpeg.dll` and fetch a list of included codecs.

View File

@@ -0,0 +1,29 @@
{
'targets': [{
'defines': ['NAPI_VERSION=2'],
'target_name': 'ffmpeg',
'sources': [
'native/ffmpeg.c',
],
'conditions': [
['OS=="linux"', {
'sources': [
'native/linux-ffmpeg.c',
],
'libraries': [
'-ldl',
]
}],
['OS=="mac"', {
'sources': [
'native/mac-ffmpeg.c',
]
}],
['OS=="win"', {
'sources': [
'native/win-ffmpeg.c',
]
}],
],
}],
}

View File

@@ -0,0 +1,146 @@
// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
/**
* https://nodejs.org/docs/latest-v10.x/api/n-api.html#n_api_n_api
*/
#include <node_api.h>
#include <string.h>
#include "ffmpeg.h"
/**
* Return the list of codecs registered in the FFMPEG library.
*/
napi_value codecs(napi_env env, napi_callback_info info)
{
// We will reuse this `status` for all napi calls.
napi_status status;
char *error = NULL;
// Get arguments.
size_t argc = 1;
napi_value argv[1];
status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
if (status != napi_ok || argc < 1)
{
error = "invalid arguments";
goto error;
}
// Get first argument as string.
char path[2048];
status = napi_get_value_string_utf8(env, argv[0], path, 2048, NULL);
if (status != napi_ok)
{
error = "invalid string argument";
goto error;
}
// Load ffmpeg based on the provided path.
struct FFMPEG_Library ffmpeg = NULL_FFMPEG_LIBRARY;
char *load_error = load_ffmpeg_library(&ffmpeg, path);
if (load_error != NULL)
{
error = load_error;
goto error;
}
// Create the JavaScript list that will be returned.
napi_value codecs;
status = napi_create_array(env, &codecs);
if (status != napi_ok)
{
error = "napi_create_array fail";
goto error;
}
// Iterate over the codec descriptions.
// It includes descriptions for codecs that may not be present in the library.
struct AVCodecDescriptor *descriptor = ffmpeg.avcodec_descriptor_next(NULL);
while (descriptor != NULL)
{
// Try to fetch the codec being described, returns null on missing codecs.
struct AVCodec *decoder = ffmpeg.avcodec_find_decoder(descriptor->id);
if (decoder != NULL)
{
// Create the codec object and assign the properties.
napi_value object, value;
napi_create_object(env, &object);
// id: number
napi_create_int32(env, decoder->id, &value);
napi_set_named_property(env, object, "id", value);
// name: string
napi_create_string_utf8(env, decoder->name, strlen(decoder->name), &value);
napi_set_named_property(env, object, "name", value);
// longName: string
napi_create_string_utf8(env, decoder->long_name, strlen(decoder->long_name), &value);
napi_set_named_property(env, object, "longName", value);
// Pushing into a JS array requires calling the JS method for that.
napi_value push_fn;
napi_get_named_property(env, codecs, "push", &push_fn);
napi_call_function(env, codecs, push_fn, 1, (napi_value[]){object}, NULL);
}
descriptor = ffmpeg.avcodec_descriptor_next(descriptor);
}
// Free the ffmpeg library.
char *unload_error = unload_ffmpeg_library(&ffmpeg);
if (unload_error != NULL)
{
error = unload_error;
goto error;
}
return codecs;
error:
if (error != NULL)
{
napi_throw_error(env, NULL, error);
}
return NULL;
}
/**
* https://nodejs.org/docs/latest-v10.x/api/n-api.html#n_api_module_registration
*/
napi_value initialize(napi_env env, napi_value exports)
{
napi_status status;
napi_value function_codecs;
status = napi_create_function(env, NULL, 0, codecs, NULL, &function_codecs);
if (status != napi_ok)
{
return NULL;
}
status = napi_set_named_property(env, exports, "codecs", function_codecs);
if (status != napi_ok)
{
return NULL;
}
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, initialize);

View File

@@ -0,0 +1,80 @@
#ifndef FFMPEG_H
#define FFMPEG_H
/**
* THIS FILE REDEFINES DATA AS RETURNED BY THE FFMPEG LIBRARY.
* HEADER FILES ARE NOT DISTRIBUTED IN OUR SETUP, HENCE THIS.
*/
/**
* https://github.com/FFmpeg/FFmpeg/blob/release/3.2/libavutil/avutil.h#L193-L201
*/
enum AVMediaType
{
_UNKNOWN_DATA_AVMediaType = -1,
};
/**
* https://github.com/FFmpeg/FFmpeg/blob/release/3.2/libavcodec/avcodec.h#L191-L653
*/
enum AVCodecID
{
__UNKNOWN_DATA_AVCodecID = 0,
};
/**
* https://github.com/FFmpeg/FFmpeg/blob/release/3.2/libavcodec/avcodec.h#L3611-L3721
*/
struct AVCodec
{
const char *name, *long_name;
enum AVMediaType type;
enum AVCodecID id;
};
/**
* https://github.com/FFmpeg/FFmpeg/blob/release/3.2/libavcodec/avcodec.h#L660-L688
*/
struct AVCodecDescriptor
{
enum AVCodecID id;
enum AVMediaType type;
const char *name, *long_name;
};
/**
* Wrapper around the ffmpeg library that must be loaded at runtime.
*/
struct FFMPEG_Library
{
void *handle;
/**
* https://github.com/FFmpeg/FFmpeg/blob/release/3.2/libavcodec/avcodec.h#L6228
*
* We use AVCodecDescriptor because it is the only structure that we can
* query on all platforms. Windows' ffmpeg.dll does not export a
* `av_codec_next` function, only `avcodec_descriptor_next`.
* Also it seems that this "descriptor" concept is the recommended API.
*/
struct AVCodecDescriptor *(*avcodec_descriptor_next)(const struct AVCodecDescriptor *);
/**
* https://github.com/FFmpeg/FFmpeg/blob/release/3.2/libavcodec/avcodec.h#L4646
*/
struct AVCodec *(*avcodec_find_decoder)(enum AVCodecID);
};
#define NULL_FFMPEG_LIBRARY \
(struct FFMPEG_Library) { NULL, NULL, NULL }
/**
* Loader that will inject the loaded functions into a FFMPEG_Library structure.
*/
char *load_ffmpeg_library(struct FFMPEG_Library *library, char *library_path);
/**
* Free library.
*/
char *unload_ffmpeg_library(struct FFMPEG_Library *library);
#endif // FFMPEG_H guard

View File

@@ -0,0 +1,68 @@
// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
#ifndef LINUX_FFMPEG
#define LINUX_FFMPEG
#include <stdlib.h>
#include <dlfcn.h>
#include "ffmpeg.h"
char *load_ffmpeg_library(struct FFMPEG_Library *library, char *library_path)
{
void *handle = dlopen(library_path, RTLD_NOW);
char *error = dlerror();
if (error != NULL)
{
goto error;
}
struct AVCodecDescriptor *(*avcodec_descriptor_next)(const struct AVCodecDescriptor *) = dlsym(handle, "avcodec_descriptor_next");
error = dlerror();
if (error != NULL)
{
goto error;
}
struct AVCodec *(*avcodec_find_decoder)(enum AVCodecID) = dlsym(handle, "avcodec_find_decoder");
error = dlerror();
if (error != NULL)
{
goto error;
}
library->handle = handle;
library->avcodec_descriptor_next = avcodec_descriptor_next;
library->avcodec_find_decoder = avcodec_find_decoder;
return NULL;
error:
if (handle != NULL)
{
dlclose(handle);
}
return error;
}
char *unload_ffmpeg_library(struct FFMPEG_Library *library)
{
dlclose(library->handle);
*library = NULL_FFMPEG_LIBRARY;
return dlerror();
}
#endif // LINUX_FFMPEG guard

View File

@@ -0,0 +1,26 @@
// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
#ifndef MAC_FFMPEG
#define MAC_FFMPEG
/**
* Mac seems to use the same libraries as Linux.
* Difference is that the compiler doesn't need to be told to use `-ldl`.
*/
#include "./linux-ffmpeg.c"
#endif // MAC_FFMPEG guard

View File

@@ -0,0 +1,77 @@
// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
#ifndef WIN_FFMPEG
#define WIN_FFMPEG
#include <windows.h>
#include "ffmpeg.h"
static char *error_library_not_found = "shared library not found";
static char *error_function_not_found = "function not found in shared library";
static char *error_cannot_free_library = "cannot free shared library";
char *load_ffmpeg_library(struct FFMPEG_Library *library, char *library_path)
{
char *error = NULL;
HMODULE handle = LoadLibrary(library_path);
if (!handle)
{
error = error_library_not_found;
goto error;
}
struct AVCodecDescriptor *(*av_codec_next)(const struct AVCodecDescriptor *) = (struct AVCodecDescriptor * (*)(const struct AVCodecDescriptor *))
GetProcAddress(handle, "avcodec_descriptor_next");
if (!av_codec_next)
{
error = error_function_not_found;
goto error;
}
struct AVCodec *(*avcodec_find_decoder)(enum AVCodecID) = (struct AVCodec * (*)(enum AVCodecID))
GetProcAddress(handle, "avcodec_find_decoder");
if (!avcodec_find_decoder)
{
error = error_function_not_found;
goto error;
}
library->handle = handle;
library->avcodec_descriptor_next = av_codec_next;
library->avcodec_find_decoder = avcodec_find_decoder;
return NULL;
error:
if (handle)
{
FreeLibrary(handle);
}
return error;
}
char *unload_ffmpeg_library(struct FFMPEG_Library *library)
{
if (library->handle && FreeLibrary(library->handle))
{
*library = NULL_FFMPEG_LIBRARY;
return NULL;
}
return error_cannot_free_library;
}
#endif // WIN_FFMPEG guard

View File

@@ -0,0 +1,40 @@
{
"name": "@theia/ffmpeg",
"version": "1.68.0",
"description": "Theia FFMPEG reader utility.",
"publishConfig": {
"access": "public"
},
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
"repository": {
"type": "git",
"url": "https://github.com/eclipse-theia/theia.git"
},
"bugs": {
"url": "https://github.com/eclipse-theia/theia/issues"
},
"homepage": "https://github.com/eclipse-theia/theia",
"main": "lib/index.js",
"files": [
"binding.gyp",
"lib",
"native",
"src"
],
"scripts": {
"compile": "theiaext compile",
"lint": "theiaext lint",
"build": "theiaext build",
"watch": "theiaext watch",
"clean": "theiaext clean"
},
"dependencies": {
"@electron/get": "^2.0.0",
"tslib": "^2.6.2",
"unzipper": "^0.9.11"
},
"devDependencies": {
"@types/unzipper": "^0.9.2"
},
"gitHead": "21358137e41342742707f660b8e222f940a27652"
}

View File

@@ -0,0 +1,56 @@
// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import * as ffmpeg from './ffmpeg';
export interface CheckFfmpegOptions extends ffmpeg.FfmpegOptions {
json?: boolean
}
export interface CheckFfmpegResult {
free: ffmpeg.Codec[],
proprietary: ffmpeg.Codec[],
}
export const KNOWN_PROPRIETARY_CODECS = new Set(['h264', 'aac']);
export async function checkFfmpeg(options: CheckFfmpegOptions = {}): Promise<void> {
const {
ffmpegPath = ffmpeg.ffmpegAbsolutePath(options),
json = false,
} = options;
const codecs = ffmpeg.getFfmpegCodecs(ffmpegPath);
const free = [];
const proprietary = [];
for (const codec of codecs) {
if (KNOWN_PROPRIETARY_CODECS.has(codec.name.toLowerCase())) {
proprietary.push(codec);
} else {
free.push(codec);
}
}
if (json) {
// Pretty format JSON on stdout.
const result: CheckFfmpegResult = { free, proprietary };
console.log(JSON.stringify(result, undefined, 2));
}
if (proprietary.length > 0) {
// Should be displayed on stderr to not pollute the JSON on stdout.
throw new Error(`${proprietary.length} proprietary codecs found\n${proprietary.map(codec => `> ${codec.name} detected (${codec.longName})`).join('\n')}`);
}
// Print to stderr to not pollute the JSON on stdout.
console.warn(`"${ffmpegPath}" does not contain proprietary codecs (${codecs.length} found).`);
}

View File

@@ -0,0 +1,114 @@
// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import path = require('path');
export interface Codec {
id: number
name: string
longName: string
}
export interface FfmpegNativeAddon {
codecs(ffmpegPath: string): Codec[]
}
export interface FfmpegNameAndLocation {
/**
* Name with extension of the shared library.
*/
name: string
/**
* Relative location of the file from Electron's dist root.
*/
location: string
}
export interface FfmpegOptions {
electronVersion?: string
electronDist?: string
ffmpegPath?: string
platform?: NodeJS.Platform
}
/**
* @internal
*/
export function _loadFfmpegNativeAddon(): FfmpegNativeAddon {
try {
return require('../build/Release/ffmpeg.node');
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
return require('../build/Debug/ffmpeg.node');
} else {
throw error;
}
}
}
/**
* @returns name and relative path from Electron's root where FFMPEG is located at.
*/
export function ffmpegNameAndLocation({
platform = process.platform
}: FfmpegOptions = {}): FfmpegNameAndLocation {
switch (platform) {
case 'darwin':
return {
name: 'libffmpeg.dylib',
location: 'Electron.app/Contents/Frameworks/Electron Framework.framework/Libraries/',
};
case 'win32':
return {
name: 'ffmpeg.dll',
location: '',
};
case 'linux':
return {
name: 'libffmpeg.so',
location: '',
};
default:
throw new Error(`${platform} is not supported`);
}
}
/**
* @returns relative ffmpeg shared library path from the Electron distribution root.
*/
export function ffmpegRelativePath(options: FfmpegOptions = {}): string {
const { location, name } = ffmpegNameAndLocation(options);
return path.join(location, name);
}
/**
* @returns absolute ffmpeg shared library path.
*/
export function ffmpegAbsolutePath(options: FfmpegOptions = {}): string {
const {
electronDist = path.resolve(require.resolve('electron/package.json'), '..', 'dist')
} = options;
return path.join(electronDist, ffmpegRelativePath(options));
}
/**
* Dynamically link to `ffmpegPath` and use FFMPEG APIs to list the included `Codec`s.
* @param ffmpegPath absolute path the the FFMPEG shared library.
* @returns list of codecs for the given ffmpeg shared library.
*/
export function getFfmpegCodecs(ffmpegPath: string): Codec[] {
return _loadFfmpegNativeAddon().codecs(ffmpegPath);
}

View File

@@ -0,0 +1,28 @@
// *****************************************************************************
// Copyright (C) 2021 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import crypto = require('crypto');
import fs = require('fs-extra');
export async function hashFile(filePath: string): Promise<Buffer> {
return new Promise((resolve, reject) => {
const sha256 = crypto.createHash('sha256');
fs.createReadStream(filePath)
.on('close', () => resolve(sha256.digest()))
.on('data', data => sha256.update(data))
.on('error', reject);
});
}

View File

@@ -0,0 +1,20 @@
// *****************************************************************************
// Copyright (C) 2021 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export * from './hash';
export * from './ffmpeg';
export * from './check-ffmpeg';
export * from './replace-ffmpeg';

View File

@@ -0,0 +1,80 @@
// *****************************************************************************
// Copyright (C) 2019 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import electronGet = require('@electron/get');
import fs = require('fs-extra');
import os = require('os');
import path = require('path');
import unzipper = require('unzipper');
import * as ffmpeg from './ffmpeg';
import { hashFile } from './hash';
export async function replaceFfmpeg(options: ffmpeg.FfmpegOptions = {}): Promise<void> {
let shouldDownload = true;
let shouldReplace = true;
const {
name: ffmpegName,
location: ffmpegLocation,
} = ffmpeg.ffmpegNameAndLocation(options);
const {
electronDist = path.resolve(require.resolve('electron/package.json'), '..', 'dist'),
electronVersion = await readElectronVersion(electronDist),
ffmpegPath = path.resolve(electronDist, ffmpegLocation, ffmpegName),
} = options;
const ffmpegCachedPath = path.join(os.tmpdir(), `theia-cli/cache/electron-v${electronVersion}`, ffmpegName);
if (await fs.pathExists(ffmpegCachedPath)) {
shouldDownload = false; // If the file is already cached, do not download.
console.warn('Found cached ffmpeg library.');
const [cacheHash, distHash] = await Promise.all([
hashFile(ffmpegCachedPath),
hashFile(ffmpegPath),
]);
if (cacheHash.equals(distHash)) {
shouldReplace = false; // If files are already the same, do not replace.
console.warn('Hashes are equal, not replacing the ffmpeg library.');
}
}
if (shouldDownload) {
const ffmpegZipPath = await electronGet.downloadArtifact({
version: electronVersion,
artifactName: 'ffmpeg'
});
const ffmpegZip = await unzipper.Open.file(ffmpegZipPath);
const file = ffmpegZip.files.find(f => f.path.endsWith(ffmpegName));
if (!file) {
throw new Error(`Archive did not contain "${ffmpegName}".`);
}
// Extract file to cache.
await fs.mkdirp(path.dirname(ffmpegCachedPath));
await new Promise<void>((resolve, reject) => {
file.stream()
.pipe(fs.createWriteStream(ffmpegCachedPath))
.on('finish', resolve)
.on('error', reject);
});
console.warn(`Downloaded ffmpeg shared library { version: "${electronVersion}", dist: "${electronDist}" }.`);
}
if (shouldReplace) {
await fs.copy(ffmpegCachedPath, ffmpegPath);
console.warn(`Successfully replaced "${ffmpegPath}".`);
}
}
export async function readElectronVersion(electronDist: string): Promise<string> {
const electronVersionFilePath = path.resolve(electronDist, 'version');
const version = await fs.readFile(electronVersionFilePath, 'utf8');
return version.trim();
}

View File

@@ -0,0 +1,12 @@
{
"extends": "../../configs/base.tsconfig",
"compilerOptions": {
"composite": true,
"rootDir": "src",
"outDir": "lib"
},
"include": [
"src"
],
"references": []
}