@scout9/app 1.0.0-alpha.0.1.9 → 1.0.0-alpha.0.1.91
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/README.md +33 -0
- package/dist/{index-92deaa5f.cjs → exports-212ef6be.cjs} +46636 -4591
- package/dist/index.cjs +58 -15
- package/dist/{multipart-parser-090f08a9.cjs → multipart-parser-54a3ab5f.cjs} +13 -7
- package/dist/spirits-3b603262.cjs +1218 -0
- package/dist/spirits.cjs +9 -0
- package/dist/testing-tools.cjs +48 -0
- package/package.json +37 -8
- package/src/cli.js +162 -69
- package/src/core/config/agents.js +300 -7
- package/src/core/config/entities.js +58 -28
- package/src/core/config/index.js +37 -15
- package/src/core/config/project.js +160 -6
- package/src/core/config/workflow.js +13 -12
- package/src/core/data.js +27 -0
- package/src/core/index.js +386 -137
- package/src/core/sync.js +71 -0
- package/src/core/templates/Dockerfile +22 -0
- package/src/core/templates/app.js +453 -0
- package/src/core/templates/project-files.js +36 -0
- package/src/core/templates/template-package.json +13 -0
- package/src/exports.js +21 -17
- package/src/platform.js +189 -33
- package/src/public.d.ts.text +330 -0
- package/src/report.js +117 -0
- package/src/runtime/client/api.js +56 -159
- package/src/runtime/client/config.js +60 -11
- package/src/runtime/client/entity.js +19 -6
- package/src/runtime/client/index.js +5 -3
- package/src/runtime/client/message.js +13 -3
- package/src/runtime/client/platform.js +86 -0
- package/src/runtime/client/{agent.js → users.js} +35 -3
- package/src/runtime/client/utils.js +10 -9
- package/src/runtime/client/workflow.js +132 -9
- package/src/runtime/entry.js +2 -2
- package/src/testing-tools/dev.js +373 -0
- package/src/testing-tools/index.js +1 -0
- package/src/testing-tools/mocks.js +37 -5
- package/src/testing-tools/spirits.js +530 -0
- package/src/utils/audio-buffer.js +16 -0
- package/src/utils/audio-type.js +27 -0
- package/src/utils/configs/agents.js +68 -0
- package/src/utils/configs/entities.js +145 -0
- package/src/utils/configs/project.js +23 -0
- package/src/utils/configs/workflow.js +47 -0
- package/src/utils/file-type.js +569 -0
- package/src/utils/file.js +164 -0
- package/src/utils/glob.js +30 -0
- package/src/utils/image-buffer.js +23 -0
- package/src/utils/image-type.js +39 -0
- package/src/utils/index.js +1 -0
- package/src/utils/is-svg.js +37 -0
- package/src/utils/logger.js +111 -0
- package/src/utils/module.js +14 -25
- package/src/utils/project-templates.js +191 -0
- package/src/utils/project.js +387 -0
- package/src/utils/video-type.js +29 -0
- package/types/index.d.ts +7588 -206
- package/types/index.d.ts.map +97 -22
- package/dist/index-1b8d7dd2.cjs +0 -49555
- package/dist/index-2ccb115e.cjs +0 -49514
- package/dist/index-66b06a30.cjs +0 -49549
- package/dist/index-bc029a1d.cjs +0 -49528
- package/dist/index-d9a93523.cjs +0 -49527
- package/dist/multipart-parser-1508046a.cjs +0 -413
- package/dist/multipart-parser-7007403a.cjs +0 -413
- package/dist/multipart-parser-70c32c1d.cjs +0 -413
- package/dist/multipart-parser-71dec101.cjs +0 -413
- package/dist/multipart-parser-f15bf2e0.cjs +0 -414
- package/src/public.d.ts +0 -209
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import fsp from 'node:fs/promises';
|
|
4
|
+
import { config } from 'dotenv';
|
|
5
|
+
import { checkVariableType, requireProjectFile } from './module.js';
|
|
6
|
+
import { globSync } from 'glob';
|
|
7
|
+
import { projectTemplates } from './project-templates.js';
|
|
8
|
+
import { loadUserPackageJson } from './configs/project.js';
|
|
9
|
+
import loadEntitiesConfig from './configs/entities.js';
|
|
10
|
+
import loadWorkflowsConfig from './configs/workflow.js';
|
|
11
|
+
import { Scout9ProjectBuildConfigSchema } from '../runtime/index.js';
|
|
12
|
+
import { ProgressLogger } from './logger.js';
|
|
13
|
+
import { formatGlobPattern, normalizeGlobPattern } from './glob.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Utility class to load and write project files in a targeted way
|
|
17
|
+
*/
|
|
18
|
+
class ProjectModule {
|
|
19
|
+
|
|
20
|
+
constructor({
|
|
21
|
+
autoSave = true,
|
|
22
|
+
cwd = process.cwd(),
|
|
23
|
+
src = './src',
|
|
24
|
+
defaultExe = '.js',
|
|
25
|
+
relativeFileSourcePatternWithoutExe,
|
|
26
|
+
templateBuilder,
|
|
27
|
+
logger = new ProgressLogger(),
|
|
28
|
+
} = {}) {
|
|
29
|
+
this.cwd = cwd;
|
|
30
|
+
this.autoSave = autoSave;
|
|
31
|
+
this.logger = logger;
|
|
32
|
+
this.relativeFileSourcePatternWithoutExe = relativeFileSourcePatternWithoutExe;
|
|
33
|
+
if (!defaultExe.startsWith('.')) {
|
|
34
|
+
console.warn(`defaultExe should start with a "." - ${defaultExe}, defaulting to ".${defaultExe}"`);
|
|
35
|
+
defaultExe = `.${defaultExe}`;
|
|
36
|
+
}
|
|
37
|
+
this.defaultExe = defaultExe;
|
|
38
|
+
this.src = src;
|
|
39
|
+
if (!templateBuilder) {
|
|
40
|
+
throw new Error('templateBuilder is required');
|
|
41
|
+
}
|
|
42
|
+
this.templateBuilder = templateBuilder;
|
|
43
|
+
if (path.extname(relativeFileSourcePatternWithoutExe)) {
|
|
44
|
+
throw new Error(`relativeFileSourcePatternWithoutExe should not include the file extension`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Loads a project module
|
|
50
|
+
* @param {Scout9ProjectBuildConfig | undefined} config - is required if autoSave is true
|
|
51
|
+
* @param {boolean} ignoreModule - if true, will not require the module
|
|
52
|
+
* @returns {Promise<{mod?: function, exe: string; filePath: string}>}
|
|
53
|
+
*/
|
|
54
|
+
async load(config = undefined, ignoreModule = false) {
|
|
55
|
+
let filePath;
|
|
56
|
+
try {
|
|
57
|
+
filePath = this._filepath;
|
|
58
|
+
} catch (e) {
|
|
59
|
+
if ('message' in e && e.message.startsWith('Not Found')) {
|
|
60
|
+
const _defaultFilepath = this._defaultFilepath;
|
|
61
|
+
if (fs.existsSync(_defaultFilepath)) {
|
|
62
|
+
// try {
|
|
63
|
+
// const x = this._filepath;
|
|
64
|
+
// } catch (e) {
|
|
65
|
+
// //
|
|
66
|
+
// }
|
|
67
|
+
throw new Error(`Internal: "this._filepath" was unable to derive the default file path "${_defaultFilepath}`);
|
|
68
|
+
}
|
|
69
|
+
if (this.autoSave) {
|
|
70
|
+
if (!config) {
|
|
71
|
+
throw new Error(`Missing required file ${this.relativeFileSourcePatternWithoutExe}, autoSave requires a config object`);
|
|
72
|
+
}
|
|
73
|
+
return this.save(config, ignoreModule, this._defaultFilepath);
|
|
74
|
+
} else {
|
|
75
|
+
throw new Error(`Missing required file ${this.relativeFileSourcePatternWithoutExe}, rerun "scout9 sync" to fix`);
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let mod;
|
|
83
|
+
if (!ignoreModule) {
|
|
84
|
+
mod = await requireProjectFile(filePath);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
mod,
|
|
89
|
+
exe: path.extname(filePath),
|
|
90
|
+
filePath
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Writes data into a project file
|
|
96
|
+
* @param {Scout9ProjectBuildConfig} config
|
|
97
|
+
* @param {boolean} ignoreModule - if true, will not require the module (turning this off will speed runtime)
|
|
98
|
+
* @param {string | undefined} filePathOverride - if provided, will override the default file path
|
|
99
|
+
* @returns {Promise<{mod?: function, exe: string; filePath: string}>}
|
|
100
|
+
*/
|
|
101
|
+
async save(config, ignoreModule = false, filePathOverride = undefined) {
|
|
102
|
+
const filePath = filePathOverride || this._filepath;
|
|
103
|
+
const content = this.templateBuilder(config);
|
|
104
|
+
if (!content || typeof content !== 'string') {
|
|
105
|
+
throw new Error(`Invalid templateBuilder return value for file "${filePath}"`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// @TODO why this here?
|
|
109
|
+
if (filePath.endsWith('entities/api.js')) {
|
|
110
|
+
throw new Error(`Invalid file path "${filePath}"`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const exists = fs.existsSync(filePath);
|
|
114
|
+
if (!exists) {
|
|
115
|
+
this.logger?.warn?.(`Missing "${filePath}"`);
|
|
116
|
+
}
|
|
117
|
+
// Ensures directory exists
|
|
118
|
+
await fsp.mkdir(path.dirname(filePath), {recursive: true});
|
|
119
|
+
await fsp.writeFile(filePath, content);
|
|
120
|
+
this.logger?.info?.(`${exists ? 'Synced' : 'Created'} "${filePath}"`);
|
|
121
|
+
let mod;
|
|
122
|
+
if (!ignoreModule) {
|
|
123
|
+
mod = await requireProjectFile(filePath);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
mod,
|
|
127
|
+
exe: this.defaultExe,
|
|
128
|
+
filePath
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @param {Array<Agent> | function(): Array<Agent> | function(): Promise<Array<Agent>>} mod
|
|
134
|
+
* @param {Array<string>} acceptableTypes
|
|
135
|
+
* @returns {Promise<any>}
|
|
136
|
+
*/
|
|
137
|
+
async resolve(mod, acceptableTypes = ['async function', 'function', 'array', 'object', 'json object']) {
|
|
138
|
+
let result;
|
|
139
|
+
const entityType = checkVariableType(mod);
|
|
140
|
+
if (!acceptableTypes.includes(entityType)) {
|
|
141
|
+
throw new Error(`Invalid entity type (${entityType})`);
|
|
142
|
+
}
|
|
143
|
+
switch (entityType) {
|
|
144
|
+
case 'async function':
|
|
145
|
+
case 'function':
|
|
146
|
+
result = await mod();
|
|
147
|
+
break;
|
|
148
|
+
case 'array':
|
|
149
|
+
case 'object':
|
|
150
|
+
case 'json object':
|
|
151
|
+
result = mod;
|
|
152
|
+
break;
|
|
153
|
+
default:
|
|
154
|
+
throw new Error(`Invalid entity type (${entityType}) returned at "${path}"`);
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
get _filepath() {
|
|
160
|
+
const _filePath = `${this.src}/${this.relativeFileSourcePatternWithoutExe}`;
|
|
161
|
+
const pattern = formatGlobPattern(`${_filePath}.{ts,js,mjs,cjs}`);
|
|
162
|
+
const paths = globSync(
|
|
163
|
+
pattern,
|
|
164
|
+
{cwd: this.cwd, absolute: true}
|
|
165
|
+
);
|
|
166
|
+
if (paths.length === 0) {
|
|
167
|
+
throw new Error(`Not Found: Missing required file ${_filePath}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (paths.length > 1) {
|
|
171
|
+
throw new Error(`Input Error: Multiple files found - [${paths.join(', ')}], remove one and rerun "scout9 sync" to fix`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const [filePath] = paths;
|
|
175
|
+
|
|
176
|
+
if (!filePath) {
|
|
177
|
+
throw new Error(`Not Found: No filepath available in ${_filePath}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return filePath;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
get _defaultFilepath() {
|
|
184
|
+
return normalizeGlobPattern(`${this.src}/${this.relativeFileSourcePatternWithoutExe}${this.defaultExe}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Project Utility class - used to load and write project files
|
|
190
|
+
*/
|
|
191
|
+
export default class ProjectFiles {
|
|
192
|
+
|
|
193
|
+
constructor({
|
|
194
|
+
autoSave = true,
|
|
195
|
+
cwd = process.cwd(),
|
|
196
|
+
src = './src',
|
|
197
|
+
logger
|
|
198
|
+
} = {}) {
|
|
199
|
+
this.cwd = cwd;
|
|
200
|
+
this.src = src;
|
|
201
|
+
this.autoSave = autoSave;
|
|
202
|
+
|
|
203
|
+
// Determine default executable file from the src/index.{ts,js,mjs,cjs}
|
|
204
|
+
const rootFilePaths = [
|
|
205
|
+
path.resolve(cwd, src, 'index.ts'),
|
|
206
|
+
path.resolve(cwd, src, 'index.mjs'),
|
|
207
|
+
path.resolve(cwd, src, 'index.js'),
|
|
208
|
+
path.resolve(cwd, src, 'index.cjs')
|
|
209
|
+
];
|
|
210
|
+
let defaultExe = '.js';
|
|
211
|
+
for (const filepath of rootFilePaths) {
|
|
212
|
+
if (fs.existsSync(filepath)) {
|
|
213
|
+
defaultExe = path.extname(filepath);
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
this.defaultExe = defaultExe;
|
|
218
|
+
this.root = new ProjectModule({
|
|
219
|
+
autoSave,
|
|
220
|
+
cwd,
|
|
221
|
+
src,
|
|
222
|
+
defaultExe,
|
|
223
|
+
logger,
|
|
224
|
+
relativeFileSourcePatternWithoutExe: '{index,config}',
|
|
225
|
+
templateBuilder: projectTemplates.root
|
|
226
|
+
});
|
|
227
|
+
this.app = new ProjectModule({
|
|
228
|
+
autoSave,
|
|
229
|
+
cwd,
|
|
230
|
+
src,
|
|
231
|
+
defaultExe,
|
|
232
|
+
logger,
|
|
233
|
+
relativeFileSourcePatternWithoutExe: 'app',
|
|
234
|
+
templateBuilder: projectTemplates.app
|
|
235
|
+
});
|
|
236
|
+
this.agents = new ProjectModule({
|
|
237
|
+
autoSave,
|
|
238
|
+
cwd,
|
|
239
|
+
src,
|
|
240
|
+
defaultExe,
|
|
241
|
+
logger,
|
|
242
|
+
relativeFileSourcePatternWithoutExe: 'entities/agents/{index,config}',
|
|
243
|
+
templateBuilder: (config) => {
|
|
244
|
+
// console.log({config});
|
|
245
|
+
return projectTemplates.entities.agents(config.agents);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
this.customers = new ProjectModule({
|
|
249
|
+
autoSave,
|
|
250
|
+
cwd,
|
|
251
|
+
src,
|
|
252
|
+
defaultExe,
|
|
253
|
+
logger,
|
|
254
|
+
relativeFileSourcePatternWithoutExe: 'entities/customers/api',
|
|
255
|
+
templateBuilder: projectTemplates.entities.customers
|
|
256
|
+
});
|
|
257
|
+
this.customer = new ProjectModule({
|
|
258
|
+
autoSave,
|
|
259
|
+
cwd,
|
|
260
|
+
src,
|
|
261
|
+
defaultExe,
|
|
262
|
+
logger,
|
|
263
|
+
relativeFileSourcePatternWithoutExe: 'entities/customers/[customer]/api',
|
|
264
|
+
templateBuilder: projectTemplates.entities.customer
|
|
265
|
+
});
|
|
266
|
+
this.logger = logger;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Loads all the project configuration data
|
|
271
|
+
* @returns {Promise<Scout9ProjectBuildConfig>}
|
|
272
|
+
*/
|
|
273
|
+
async load() {
|
|
274
|
+
/**
|
|
275
|
+
* @type {Scout9ProjectBuildConfig}
|
|
276
|
+
*/
|
|
277
|
+
const projectConfig = {
|
|
278
|
+
...(await this._loadEnv()),
|
|
279
|
+
entities: [],
|
|
280
|
+
agents: [],
|
|
281
|
+
workflows: []
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Load entities, except for special entities such as ["agents"]
|
|
285
|
+
projectConfig.entities = await loadEntitiesConfig({cwd: this.cwd, src: this.src});
|
|
286
|
+
|
|
287
|
+
// Load agent persona configuration
|
|
288
|
+
const mod = await this.agents.load(projectConfig, false).then(({mod}) => mod.default);
|
|
289
|
+
projectConfig.agents = await this.agents.resolve(
|
|
290
|
+
mod,
|
|
291
|
+
['async function', 'function', 'array']
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Loads workflows
|
|
295
|
+
projectConfig.workflows = await loadWorkflowsConfig({cwd: this.cwd, src: this.src});
|
|
296
|
+
|
|
297
|
+
// Validate the config
|
|
298
|
+
const result = Scout9ProjectBuildConfigSchema.safeParse(projectConfig);
|
|
299
|
+
if (!result.success) {
|
|
300
|
+
result.error.source = `${this.src}/index.js`;
|
|
301
|
+
throw result.error;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Log
|
|
305
|
+
for (const entityConfig of projectConfig.entities) {
|
|
306
|
+
this.logger?.info?.(`Loaded entity ${entityConfig.entities.join('/')}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return projectConfig;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
*
|
|
314
|
+
* @param {Scout9ProjectBuildConfig | undefined} config - is required if autoSave is true
|
|
315
|
+
* @param {(message: string, type: 'info' | 'warn' | 'error' | 'log' | undefined) => void | undefined} cb
|
|
316
|
+
* @returns {Promise<void>}
|
|
317
|
+
*/
|
|
318
|
+
async sync(config, cb = (message, type) => {
|
|
319
|
+
}) {
|
|
320
|
+
await this.root.load(config, true);
|
|
321
|
+
await this.root.save(config, true);
|
|
322
|
+
|
|
323
|
+
await this.app.load(config, true);
|
|
324
|
+
|
|
325
|
+
await this.agents.load(config, true);
|
|
326
|
+
await this.agents.save(config, true);
|
|
327
|
+
cb('Synced agents');
|
|
328
|
+
|
|
329
|
+
await this.customers.load(config, true);
|
|
330
|
+
await this.customer.load(config, true);
|
|
331
|
+
|
|
332
|
+
// If entity file doesn't exist, create it
|
|
333
|
+
for (const entityConfig of config.entities) {
|
|
334
|
+
const {entities} = entityConfig;
|
|
335
|
+
if (entities.length === 0) {
|
|
336
|
+
cb(`Entity ${entityConfig.entity} has no entity path references - skipping check`, 'warn');
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
const paths = entities.join('/');
|
|
340
|
+
const filepath = path.resolve(this.cwd, this.src, `entities/${paths}/api${this.defaultExe}`);
|
|
341
|
+
if (!fs.existsSync(filepath)) {
|
|
342
|
+
await fsp.mkdir(path.dirname(filepath), {recursive: true});
|
|
343
|
+
await fsp.writeFile(filepath, projectTemplates.entities.entity(entityConfig));
|
|
344
|
+
cb(`Missing entity, created ${filepath}`, 'info');
|
|
345
|
+
} else {
|
|
346
|
+
cb(`Existing entity, ${filepath} skipped`, 'info');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Loads the users local package.json, if they provide a package-s9-test.json, it will load that instead (used for scout9 internal testing)
|
|
353
|
+
* @returns {Promise<Scout9ProjectConfig>}
|
|
354
|
+
*/
|
|
355
|
+
async _loadEnv() {
|
|
356
|
+
if (!!process.env.SCOUT9_API_KEY) {
|
|
357
|
+
if (process.env.SCOUT9_API_KEY.includes('insert-scout9-api-key')) {
|
|
358
|
+
throw new Error('Missing SCOUT9_API_KEY, please add your Scout9 API key to your .env file');
|
|
359
|
+
}
|
|
360
|
+
} else {
|
|
361
|
+
const configFilePath = path.resolve(this.cwd, './.env');
|
|
362
|
+
config({path: configFilePath});
|
|
363
|
+
if (!process.env.SCOUT9_API_KEY) {
|
|
364
|
+
const exists = fs.existsSync(configFilePath);
|
|
365
|
+
if (!exists) {
|
|
366
|
+
throw new Error(`Missing .env file with "SCOUT9_API_KEY".\n\n\tTo fix, create a .env file at the root of your project.\nAdd "SCOUT9_API_KEY=<your-scout9-api-key>" to the .env file.\n\n\t> You can get your API key at https://scout9.com\n\n`);
|
|
367
|
+
} else {
|
|
368
|
+
throw new Error(`Missing "SCOUT9_API_KEY" within .env file.\n\n\tTo fix, add "SCOUT9_API_KEY=<your-scout9-api-key>" to the .env file.\n\n\tYou can get your API key at https://scout9.com\n\n`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const {pkg} = await loadUserPackageJson({cwd: this.cwd});
|
|
374
|
+
const tag = `${pkg.name || 'scout9-app'}-v${pkg.version || '0.0.1'}`;
|
|
375
|
+
const {mod, filePath} = await this.root.load();
|
|
376
|
+
const root = mod?.default;
|
|
377
|
+
if (!root || checkVariableType(root) !== 'json object') {
|
|
378
|
+
throw new Error(`Invalid root project file ${filePath} - must be an object, example:\n\nexport default {/** ... */}`);
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
tag,
|
|
382
|
+
...root
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { fileTypeFromBuffer } from './file-type.js';
|
|
2
|
+
|
|
3
|
+
export const videoExtensions = new Set([
|
|
4
|
+
'mp4',
|
|
5
|
+
'm4v',
|
|
6
|
+
'mkv',
|
|
7
|
+
'webm',
|
|
8
|
+
'mov',
|
|
9
|
+
'avi',
|
|
10
|
+
'wmv',
|
|
11
|
+
'mpg',
|
|
12
|
+
'flv',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} VideoTypeResult
|
|
17
|
+
* @property {string} ext - One of the supported [file types](https://github.com/sindresorhus/image-type#supported-file-types).
|
|
18
|
+
* @property {string} mime - The detected [MIME type](https://en.wikipedia.org/wiki/Internet_media_type).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
*
|
|
23
|
+
* @param {Buffer | Uint8Array} input
|
|
24
|
+
* @returns {Promise<VideoTypeResult | undefined>}
|
|
25
|
+
*/
|
|
26
|
+
export default async function videoType(input) {
|
|
27
|
+
const result = await fileTypeFromBuffer(input);
|
|
28
|
+
return videoExtensions.has(result?.ext) && result;
|
|
29
|
+
}
|