refactor!: cleanup platform support (#306)

* refactor!: cleanup platform support

* chore: lint

* fix: web platform

* feat: provide UniversalCache

Provide UniversalCache as a wrapper around Platform.shim.Cache.

* fix: invalid import

* refactor: remove isolated-vm support

* fix: type info

* refactor: cleanup exports

* fix: mark jintr as external dependency

In the bundled CJS node build, mark jintr as external.

* chore: add additional exports

web exports provide a way to select web implementation manually without
relying on the bundler to select it correctly from the "exports" field

web points to src/platform/web.js
web.bundle points to bundle/browser.js
web.bundle.browser points to bundle/browser.min.js

agnostic exports provide users of the library to provide their own
platform implementation without first importing the default one.

agnostic points to src/platform/lib.ts

* fix: toDash on web

* revert: eval is synchronous

* fix: use serializeDOM in FormatUtils

* ci: automate releases with `release-please`

* chore: clean up workflow files

* ci: fix NPM publish action

---------

Co-authored-by: LuanRT <luan.lrt4@gmail.com>
This commit is contained in:
Daniel Wykerd
2023-02-12 09:21:44 +02:00
committed by GitHub
parent a69e43bf3a
commit 2ccbe2ce62
504 changed files with 11184 additions and 6279 deletions

131
src/platform/node.ts Normal file
View File

@@ -0,0 +1,131 @@
// Node.js Platform Support
import { ReadableStream } from 'stream/web';
import {
fetch as defaultFetch,
Request,
Response,
Headers,
FormData,
File
} from 'undici';
import { ICache } from '../types/Cache.js';
import { Platform } from '../utils/Utils.js';
import crypto from 'crypto';
import { FetchFunction } from '../types/PlatformShim.js';
import path from 'path';
import os from 'os';
import fs from 'fs/promises';
import { readFileSync } from 'fs';
import DOMParser from './polyfills/server-dom.js';
import { fileURLToPath } from 'url';
import evaluate from './jsruntime/jinter.js';
const meta_url = import.meta.url;
const is_cjs = !meta_url;
const __dirname__ = is_cjs ? __dirname : path.dirname(fileURLToPath(meta_url));
const package_json = JSON.parse(readFileSync(path.resolve(__dirname__, is_cjs ? '../package.json' : '../../package.json'), 'utf-8'));
class Cache implements ICache {
#persistent_directory: string;
#persistent: boolean;
constructor(persistent = false, persistent_directory?: string) {
this.#persistent_directory = persistent_directory || Cache.default_persistent_directory;
this.#persistent = persistent;
}
static get temp_directory() {
return `${os.tmpdir()}/youtubei.js`;
}
static get default_persistent_directory() {
return path.resolve(__dirname__, '..', '..', '.cache', 'youtubei.js');
}
get cache_dir() {
return this.#persistent ? this.#persistent_directory : Cache.temp_directory;
}
async #createCache() {
const dir = this.cache_dir;
try {
const cwd = await fs.stat(dir);
if (!cwd.isDirectory())
throw new Error('An unexpected file was found in place of the cache directory');
} catch (e: any) {
if (e?.code === 'ENOENT')
await fs.mkdir(dir, { recursive: true });
else
throw e;
}
}
async get(key: string) {
await this.#createCache();
const file = path.resolve(this.cache_dir, key);
try {
const stat = await fs.stat(file);
if (stat.isFile()) {
const data: Buffer = await fs.readFile(file);
return data.buffer;
}
throw new Error('An unexpected file was found in place of the cache key');
} catch (e: any) {
if (e?.code === 'ENOENT')
return undefined;
throw e;
}
}
async set(key: string, value: ArrayBuffer) {
await this.#createCache();
const file = path.resolve(this.cache_dir, key);
await fs.writeFile(file, new Uint8Array(value));
}
async remove(key: string) {
await this.#createCache();
const file = path.resolve(this.cache_dir, key);
try {
await fs.unlink(file);
} catch (e: any) {
if (e?.code === 'ENOENT') return;
throw e;
}
}
}
Platform.load({
runtime: 'node',
info: {
version: package_json.version,
bugs_url: package_json.bugs.url,
repo_url: package_json.homepage.split('#')[0]
},
server: true,
Cache: Cache,
sha1Hash: async (data: string) => {
return crypto.createHash('sha1').update(data).digest('hex');
},
uuidv4() {
return crypto.randomUUID();
},
serializeDOM(document) {
return document.toString();
},
eval: evaluate,
DOMParser,
fetch: defaultFetch as unknown as FetchFunction,
Request: Request as unknown as typeof globalThis.Request,
Response: Response as unknown as typeof globalThis.Response,
Headers: Headers as unknown as typeof globalThis.Headers,
FormData: FormData as unknown as typeof globalThis.FormData,
File: File as unknown as typeof globalThis.File,
ReadableStream: ReadableStream as unknown as typeof globalThis.ReadableStream
});
export * from './lib.js';
import Innertube from './lib.js';
export default Innertube;