diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 0cd8f155..44fbe95d 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -43,7 +43,7 @@ jobs: needs: build strategy: matrix: - package: [realtime, sdk, yjs, 'room'] + package: [realtime, sdk, yjs, room, video] steps: - name: Install pnpm uses: pnpm/action-setup@v4 diff --git a/package.json b/package.json index c00699ea..1de19fee 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "build": "turbo build", "dev": "turbo dev", "lint": "turbo lint", - "watch": "turbo run watch --concurrency=12", + "watch": "turbo run watch --concurrency=13", "semantic-release": "turbo run semantic-release", "test:unit": "turbo run test:unit", "test:unit:watch": "turbo run test:unit:watch", diff --git a/packages/video/.esbuild/build.js b/packages/video/.esbuild/build.js new file mode 100644 index 00000000..68fde21e --- /dev/null +++ b/packages/video/.esbuild/build.js @@ -0,0 +1,21 @@ +const { cjsConfig, esmConfig } = require('./config'); +const esbuild = require('esbuild'); + +(async () => { + try { + await Promise.all([ + esbuild.build({ + ...cjsConfig, + outfile: 'dist/index.cjs.js', + }), + + esbuild.build({ + ...esmConfig, + outdir: 'dist', + }), + ]); + } catch (error) { + console.error(error); + process.exit(1); + } +})(); diff --git a/packages/video/.esbuild/config.js b/packages/video/.esbuild/config.js new file mode 100644 index 00000000..c6f755eb --- /dev/null +++ b/packages/video/.esbuild/config.js @@ -0,0 +1,53 @@ +require('dotenv').config(); +const { style } = require('./plugins/style-loader'); + +const entries = Object.entries(process.env).filter((key) => key[0].startsWith('SDK_')); +const env = Object.fromEntries(entries); + +const config = { + loader: { + '.png': 'file', + '.svg': 'file', + '.woff': 'file', + '.woff2': 'file', + '.eot': 'file', + '.ttf': 'file', + }, + plugins: [style()], + bundle: true, + color: true, + minify: true, + logLevel: 'info', + sourcemap: true, + chunkNames: 'chunks/[name]-[hash]', + define: { + 'process.env': JSON.stringify(env), + }, +}; + +const esmConfig = { + ...config, + entryPoints: ['src/index.ts'], + bundle: true, + sourcemap: true, + minify: true, + splitting: true, + target: 'es6', + format: 'esm', + define: { global: 'window', 'process.env': JSON.stringify(env) }, +}; + +const cjsConfig = { + ...config, + entryPoints: ['src/index.ts'], + bundle: true, + sourcemap: true, + minify: true, + platform: 'node', + target: ['node16'], +}; + +module.exports = { + esmConfig, + cjsConfig, +}; diff --git a/packages/video/.esbuild/plugins/style-loader.js b/packages/video/.esbuild/plugins/style-loader.js new file mode 100644 index 00000000..69e9befb --- /dev/null +++ b/packages/video/.esbuild/plugins/style-loader.js @@ -0,0 +1,60 @@ +const esbuild = require('esbuild'); +const fs = require('fs'); +const path = require('path'); + +module.exports = { + style({ minify = true, charset = 'utf8' } = {}) { + return { + name: 'style', + setup({ onResolve, onLoad }) { + const cwd = process.cwd(); + const opt = { logLevel: 'silent', bundle: true, write: false, charset, minify }; + + onResolve({ filter: /\.css$/, namespace: 'file' }, (args) => { + const absPath = path.join(args.resolveDir, args.path); + const relPath = path.relative(cwd, absPath); + const resolved = fs.existsSync(absPath) ? relPath : args.path; + return { path: resolved, namespace: 'style-stub' }; + }); + + onResolve({ filter: /\.css$/, namespace: 'style-stub' }, (args) => { + return { path: args.path, namespace: 'style-content' }; + }); + + onResolve({ filter: /^__style_helper__$/, namespace: 'style-stub' }, (args) => ({ + path: args.path, + namespace: 'style-helper', + sideEffects: false, + })); + + onLoad({ filter: /.*/, namespace: 'style-helper' }, async () => ({ + contents: ` + export function injectStyle(text) { + if (typeof document !== 'undefined') { + const style = document.createElement('style') + style.id = 'superviz-style' + const node = document.createTextNode(text) + style.appendChild(node) + document.head.appendChild(style) + } + } + `, + })); + + onLoad({ filter: /.*/, namespace: 'style-stub' }, async (args) => ({ + contents: ` + import { injectStyle } from "__style_helper__" + import css from ${JSON.stringify(args.path)} + injectStyle(css) + `, + })); + + onLoad({ filter: /.*/, namespace: 'style-content' }, async (args) => { + const options = { entryPoints: [args.path], ...opt }; + const { errors, warnings, outputFiles } = await esbuild.build(options); + return { errors, warnings, contents: outputFiles[0].text, loader: 'text' }; + }); + }, + }; + }, +}; diff --git a/packages/video/.esbuild/watch.js b/packages/video/.esbuild/watch.js new file mode 100644 index 00000000..c3baf868 --- /dev/null +++ b/packages/video/.esbuild/watch.js @@ -0,0 +1,24 @@ +const { cjsConfig, esmConfig } = require('./config'); +const esbuild = require('esbuild'); + +(async () => { + try { + const [cjsContext, esmContext] = await Promise.all([ + esbuild.context({ + ...cjsConfig, + outfile: 'dist/index.cjs.js', + }), + + esbuild.context({ + ...esmConfig, + outdir: 'dist', + }), + ]); + + cjsContext.watch(); + esmContext.watch(); + } catch (error) { + console.error(error); + process.exit(1); + } +})(); diff --git a/packages/video/.eslintrc.js b/packages/video/.eslintrc.js new file mode 100644 index 00000000..bb41a969 --- /dev/null +++ b/packages/video/.eslintrc.js @@ -0,0 +1,9 @@ +module.exports = { + root: true, + extends: ["@superviz/eslint-config/library.js"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: "./tsconfig.lint.json", + tsconfigRootDir: __dirname, + }, +}; diff --git a/packages/video/.prettierrc.js b/packages/video/.prettierrc.js new file mode 100644 index 00000000..cf70d4f3 --- /dev/null +++ b/packages/video/.prettierrc.js @@ -0,0 +1,8 @@ +module.exports = { + semi: true, + trailingComma: 'all', + arrowParens: 'always', + printWidth: 100, + singleQuote: true, + tabWidth: 2, +}; diff --git a/packages/video/.releaserc b/packages/video/.releaserc new file mode 100644 index 00000000..fc961f1f --- /dev/null +++ b/packages/video/.releaserc @@ -0,0 +1,55 @@ +{ + "branches": [ + "main", + { + "name": "beta", + "channel": "beta", + "prerelease": true + }, + { + "name": "lab", + "channel": "lab", + "prerelease": true + } + ], + "tagFormat": "@superviz/video/${version}", + "plugins": [ + "@semantic-release/commit-analyzer", + [ + "@semantic-release/release-notes-generator", + { + "preset": "angular", + "parserOpts": { + "noteKeywords": [ + "BREAKING CHANGE", + "BREAKING CHANGES", + "BREAKING" + ] + }, + "writerOpts": { + "commitsSort": [ + "subject", + "scope" + ] + } + } + ], + "@anolilab/semantic-release-pnpm", + [ + { + "assets": [ + "package.json" + ], + "message": "release: @superviz/video/${nextRelease.version} \n\n${nextRelease.notes}" + } + ], + [ + "@semantic-release/github", + { + "successComment": ":tada: This issue has been resolved in version @superviz/video/${nextRelease.version} :tada:\n\nThe release is available on [GitHub release]()" + } + ] + + ], + "preset": "angular" +} diff --git a/packages/video/.remote-config.js b/packages/video/.remote-config.js new file mode 100644 index 00000000..cbeb9cc3 --- /dev/null +++ b/packages/video/.remote-config.js @@ -0,0 +1 @@ +{"apiUrl":"https://localhost:3000","conferenceLayerUrl":"https://localhost:8080"} \ No newline at end of file diff --git a/packages/video/LICENSE b/packages/video/LICENSE new file mode 100644 index 00000000..2065de93 --- /dev/null +++ b/packages/video/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2022, SuperViz +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/video/README.md b/packages/video/README.md new file mode 100644 index 00000000..a769b5f2 --- /dev/null +++ b/packages/video/README.md @@ -0,0 +1,11 @@ +

+ Nest Logo +

+ +

+ Discord + GitHub pull requests + License + npm type definitions + Downloads +

/src/**/*.ts', + '/src/**/*.js', + '!/src/web-components/**/*', + '!/src/index.ts', + ], + coverageDirectory: '/coverage', + coverageReporters: ['html', 'lcov'].concat(argv.coverage ? ['text'] : []), + testPathIgnorePatterns: [ + '/node_modules/', + '/dist/', + '/coverage/', + '/e2e/', + '/src/web-components', + ], + transform: { + '^.+\\.ts$': 'ts-jest', + '^.+\\.js$': 'ts-jest', + }, + setupFiles: ['/jest.setup.js'], + reporters: [ + 'default', + ['jest-ctrf-json-reporter', {}], + ], +}; diff --git a/packages/video/jest.setup.js b/packages/video/jest.setup.js new file mode 100644 index 00000000..05fb7685 --- /dev/null +++ b/packages/video/jest.setup.js @@ -0,0 +1,20 @@ +/* eslint-disable no-undef */ +const fs = require('fs'); +const { MOCK_IO } = require('./__mocks__/io.mock'); + +(() => { + const filename = '.remote-config.js'; + + if (!fs.existsSync(filename)) { + fs.writeFileSync( + filename, + JSON.stringify({ + apiUrl: 'https://localhost:3000', + conferenceLayerUrl: 'https://localhost:8080', + }), + ); + } +})(); + + +jest.mock('@superviz/socket-client', () => MOCK_IO); \ No newline at end of file diff --git a/packages/video/package.json b/packages/video/package.json new file mode 100644 index 00000000..2e5a3cde --- /dev/null +++ b/packages/video/package.json @@ -0,0 +1,77 @@ +{ + "name": "@superviz/video", + "version": "0.0.0", + "description": "SuperViz Video SDK", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "import": "./dist/index.js", + "require": "./dist/index.cjs.js" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "node ./.esbuild/build.js", + "postbuild": "./node_modules/typescript/bin/tsc", + "watch": "concurrently -n code,types \"pnpm watch:code\" \"pnpm watch:types\"", + "watch:code": "node ./.esbuild/watch.js", + "watch:types": "./node_modules/typescript/bin/tsc --watch --outDir ./dist", + "prepack": "pnpm build", + "test:unit": "jest", + "test:unit:watch": "jest --watch", + "test:unit:coverage": "jest --coverage", + "test:unit:ci": "jest --ci", + "semantic-release": "semantic-release" + }, + "publishConfig": { + "access": "public", + "scope": "@superviz" + }, + "repository": { + "type": "git", + "url": "https://github.com/SuperViz/superviz.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/SuperViz/superviz/issues" + }, + "homepage": "https://github.com/SuperViz/superviz/tree/main/packages/sdk#readme", + "devDependencies": { + "@anolilab/semantic-release-pnpm": "^1.1.3", + "@esm-bundle/chai": "^4.3.4-fix.0", + "@jest/globals": "^29.7.0", + "@open-wc/testing-helpers": "^3.0.1", + "@semantic-release/release-notes-generator": "^14.0.1", + "@superviz/eslint-config": "workspace:*", + "@superviz/typescript-config": "workspace:*", + "@types/debug": "^4.1.12", + "@types/jest": "^29.5.12", + "@types/luxon": "^3.4.2", + "@types/node": "^20.14.8", + "concurrently": "^8.2.2", + "cz-conventional-changelog": "^3.3.0", + "dotenv": "^16.4.5", + "esbuild": "^0.21.5", + "eslint": "8.56.0", + "glob": "^11.0.0", + "husky": "^9.0.11", + "jest": "^29.7.0", + "jest-browser-globals": "^25.1.0-beta", + "jest-environment-jsdom": "^29.7.0", + "rimraf": "^5.0.7", + "semantic-release": "^24.0.0", + "ts-jest": "^29.1.5", + "tsc": "^2.0.4", + "typescript": "^5.6.2", + "yargs": "^17.7.2" + }, + "dependencies": { + "@superviz/socket-client": "workspace:*", + "debug": "^4.3.4", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "zod": "^3.23.8" + } +} diff --git a/packages/video/src/common/utils/logger.ts b/packages/video/src/common/utils/logger.ts new file mode 100644 index 00000000..fd229cd0 --- /dev/null +++ b/packages/video/src/common/utils/logger.ts @@ -0,0 +1,15 @@ +import debug from 'debug'; + +export type Message = string | Error | number | Object; + +export class Logger { + private debug: debug.Debugger; + + constructor(scope: string) { + this.debug = debug(scope); + } + + log(formatter: Message, ...args: Array) { + this.debug(formatter, ...args); + } +} diff --git a/packages/video/src/core/.gitkeep b/packages/video/src/core/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/video/src/index.test.ts b/packages/video/src/index.test.ts new file mode 100644 index 00000000..13a1a909 --- /dev/null +++ b/packages/video/src/index.test.ts @@ -0,0 +1,7 @@ + + +describe('video', () => { + it('should work', () => { + expect(true).toBe(true); + }); +}); \ No newline at end of file diff --git a/packages/video/src/index.ts b/packages/video/src/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/packages/video/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/video/src/services/.gitkeep b/packages/video/src/services/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/video/src/shims.d.ts b/packages/video/src/shims.d.ts new file mode 100644 index 00000000..21c57e79 --- /dev/null +++ b/packages/video/src/shims.d.ts @@ -0,0 +1,5 @@ +declare global { + interface Window { + SuperViz: Record; + } +} diff --git a/packages/video/tsconfig.json b/packages/video/tsconfig.json new file mode 100644 index 00000000..1665f74e --- /dev/null +++ b/packages/video/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@superviz/typescript-config/base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "src/**/*.test.ts"] +} diff --git a/packages/video/tsconfig.lint.json b/packages/video/tsconfig.lint.json new file mode 100644 index 00000000..0cd2844a --- /dev/null +++ b/packages/video/tsconfig.lint.json @@ -0,0 +1,8 @@ +{ + "extends": "@superviz/typescript-config/base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "turbo"], + "exclude": ["node_modules", "dist"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbb188a0..fda1aaad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -993,6 +993,106 @@ importers: packages/typescript-config: {} + packages/video: + dependencies: + '@superviz/socket-client': + specifier: workspace:* + version: link:../socket-client + debug: + specifier: ^4.3.4 + version: 4.3.7 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + rxjs: + specifier: ^7.8.1 + version: 7.8.1 + zod: + specifier: ^3.23.8 + version: 3.23.8 + devDependencies: + '@anolilab/semantic-release-pnpm': + specifier: ^1.1.3 + version: 1.1.3(yaml@2.5.1) + '@esm-bundle/chai': + specifier: ^4.3.4-fix.0 + version: 4.3.4-fix.0 + '@jest/globals': + specifier: ^29.7.0 + version: 29.7.0 + '@open-wc/testing-helpers': + specifier: ^3.0.1 + version: 3.0.1 + '@semantic-release/release-notes-generator': + specifier: ^14.0.1 + version: 14.0.1(semantic-release@24.0.0(typescript@5.6.2)) + '@superviz/eslint-config': + specifier: workspace:* + version: link:../eslint-config + '@superviz/typescript-config': + specifier: workspace:* + version: link:../typescript-config + '@types/debug': + specifier: ^4.1.12 + version: 4.1.12 + '@types/jest': + specifier: ^29.5.12 + version: 29.5.13 + '@types/luxon': + specifier: ^3.4.2 + version: 3.4.2 + '@types/node': + specifier: ^20.14.8 + version: 20.16.5 + concurrently: + specifier: ^8.2.2 + version: 8.2.2 + cz-conventional-changelog: + specifier: ^3.3.0 + version: 3.3.0(@types/node@20.16.5)(typescript@5.6.2) + dotenv: + specifier: ^16.4.5 + version: 16.4.7 + esbuild: + specifier: ^0.21.5 + version: 0.21.5 + eslint: + specifier: 8.56.0 + version: 8.56.0 + glob: + specifier: ^11.0.0 + version: 11.0.0 + husky: + specifier: ^9.0.11 + version: 9.1.6 + jest: + specifier: ^29.7.0 + version: 29.7.0(@types/node@20.16.5) + jest-browser-globals: + specifier: ^25.1.0-beta + version: 25.1.0-beta + jest-environment-jsdom: + specifier: ^29.7.0 + version: 29.7.0 + rimraf: + specifier: ^5.0.7 + version: 5.0.10 + semantic-release: + specifier: ^24.0.0 + version: 24.0.0(typescript@5.6.2) + ts-jest: + specifier: ^29.1.5 + version: 29.2.5(@babel/core@7.25.2)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.2))(esbuild@0.21.5)(jest@29.7.0(@types/node@20.16.5))(typescript@5.6.2) + tsc: + specifier: ^2.0.4 + version: 2.0.4 + typescript: + specifier: ^5.6.2 + version: 5.6.2 + yargs: + specifier: ^17.7.2 + version: 17.7.2 + packages/yjs: dependencies: debug: