@streamfox/create-streamfox-plugin 0.3.0
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/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/cli.cjs +637 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +614 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +459 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +420 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,637 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli.ts
|
|
27
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
28
|
+
var import_commander = require("commander");
|
|
29
|
+
var import_prompts = __toESM(require("prompts"), 1);
|
|
30
|
+
|
|
31
|
+
// package.json
|
|
32
|
+
var package_default = {
|
|
33
|
+
name: "@streamfox/create-streamfox-plugin",
|
|
34
|
+
version: "0.3.0",
|
|
35
|
+
description: "Standalone CLI to scaffold StreamFox plugin projects",
|
|
36
|
+
type: "module",
|
|
37
|
+
bin: {
|
|
38
|
+
"create-streamfox-plugin": "dist/cli.cjs"
|
|
39
|
+
},
|
|
40
|
+
main: "./dist/index.cjs",
|
|
41
|
+
module: "./dist/index.js",
|
|
42
|
+
types: "./dist/index.d.ts",
|
|
43
|
+
exports: {
|
|
44
|
+
".": {
|
|
45
|
+
types: "./dist/index.d.ts",
|
|
46
|
+
import: "./dist/index.js",
|
|
47
|
+
require: "./dist/index.cjs"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
files: [
|
|
51
|
+
"dist",
|
|
52
|
+
"README.md"
|
|
53
|
+
],
|
|
54
|
+
scripts: {
|
|
55
|
+
build: "tsup",
|
|
56
|
+
format: "prettier --write .",
|
|
57
|
+
"format:check": "prettier --check .",
|
|
58
|
+
test: "vitest run",
|
|
59
|
+
typecheck: "tsc --noEmit",
|
|
60
|
+
check: "npm run format:check && npm run typecheck && npm test && npm run build"
|
|
61
|
+
},
|
|
62
|
+
dependencies: {
|
|
63
|
+
commander: "^12.1.0",
|
|
64
|
+
prompts: "^2.4.2"
|
|
65
|
+
},
|
|
66
|
+
devDependencies: {
|
|
67
|
+
"@types/node": "^24.6.0",
|
|
68
|
+
"@types/prompts": "^2.4.9",
|
|
69
|
+
prettier: "^3.6.2",
|
|
70
|
+
tsup: "^8.5.0",
|
|
71
|
+
typescript: "^5.9.2",
|
|
72
|
+
vitest: "^2.1.9"
|
|
73
|
+
},
|
|
74
|
+
engines: {
|
|
75
|
+
node: ">=20"
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// src/scaffold.ts
|
|
80
|
+
var import_promises = require("fs/promises");
|
|
81
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
82
|
+
var CAPABILITIES = [
|
|
83
|
+
"catalog",
|
|
84
|
+
"meta",
|
|
85
|
+
"stream",
|
|
86
|
+
"subtitles",
|
|
87
|
+
"plugin_catalog"
|
|
88
|
+
];
|
|
89
|
+
var DEFAULT_PRESET = "meta";
|
|
90
|
+
var DEFAULT_SDK_VERSION = "^0.2.0";
|
|
91
|
+
function sortedCapabilities(values) {
|
|
92
|
+
const unique = Array.from(new Set(values));
|
|
93
|
+
return CAPABILITIES.filter((capability) => unique.includes(capability));
|
|
94
|
+
}
|
|
95
|
+
async function ensureTargetDoesNotExist(targetDir) {
|
|
96
|
+
try {
|
|
97
|
+
await (0, import_promises.access)(targetDir);
|
|
98
|
+
throw new Error(`Target directory already exists: ${targetDir}`);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error.code === "ENOENT") {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function makePackageJson(name, language, sdkVersion) {
|
|
107
|
+
const scripts = language === "ts" ? {
|
|
108
|
+
dev: "tsx watch src/server.ts",
|
|
109
|
+
build: "tsc -p tsconfig.json",
|
|
110
|
+
format: "prettier --write .",
|
|
111
|
+
"format:check": "prettier --check .",
|
|
112
|
+
start: "node dist/server.js",
|
|
113
|
+
test: "vitest run",
|
|
114
|
+
typecheck: "tsc --noEmit"
|
|
115
|
+
} : {
|
|
116
|
+
dev: "node --watch src/server.js",
|
|
117
|
+
build: 'echo "No build step for JavaScript template"',
|
|
118
|
+
format: "prettier --write .",
|
|
119
|
+
"format:check": "prettier --check .",
|
|
120
|
+
start: "node src/server.js",
|
|
121
|
+
test: "vitest run"
|
|
122
|
+
};
|
|
123
|
+
const normalizedScripts = {
|
|
124
|
+
...scripts,
|
|
125
|
+
check: language === "ts" ? "npm run format:check && npm run typecheck && npm test && npm run build" : "npm run format:check && npm test && npm run build"
|
|
126
|
+
};
|
|
127
|
+
const packageJson = {
|
|
128
|
+
name,
|
|
129
|
+
version: "0.1.0",
|
|
130
|
+
private: true,
|
|
131
|
+
type: "module",
|
|
132
|
+
scripts: normalizedScripts,
|
|
133
|
+
dependencies: {
|
|
134
|
+
"@streamfox/plugin-sdk": sdkVersion
|
|
135
|
+
},
|
|
136
|
+
devDependencies: language === "ts" ? {
|
|
137
|
+
"@types/node": "^24.6.0",
|
|
138
|
+
prettier: "^3.6.2",
|
|
139
|
+
tsx: "^4.20.5",
|
|
140
|
+
typescript: "^5.9.2",
|
|
141
|
+
vitest: "^2.1.9"
|
|
142
|
+
} : {
|
|
143
|
+
prettier: "^3.6.2",
|
|
144
|
+
vitest: "^2.1.9"
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
return `${JSON.stringify(packageJson, null, 2)}
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
function resourceBlock(capability, advanced) {
|
|
151
|
+
switch (capability) {
|
|
152
|
+
case "catalog":
|
|
153
|
+
return `catalog: {
|
|
154
|
+
endpoints: [
|
|
155
|
+
{
|
|
156
|
+
id: "top",
|
|
157
|
+
name: "Top",
|
|
158
|
+
mediaTypes: ["movie"],
|
|
159
|
+
filters: [{ key: "genre", valueType: "string" }],
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
handler: async () => ({
|
|
163
|
+
items: [],
|
|
164
|
+
}),
|
|
165
|
+
},`;
|
|
166
|
+
case "meta":
|
|
167
|
+
return `meta: {
|
|
168
|
+
mediaTypes: ["movie"],
|
|
169
|
+
includes: ["videos", "links"],
|
|
170
|
+
handler: async () => ({
|
|
171
|
+
item: ${advanced ? `{
|
|
172
|
+
summary: {
|
|
173
|
+
id: { namespace: "imdb", value: "tt1254207" },
|
|
174
|
+
mediaType: "movie",
|
|
175
|
+
title: "Big Buck Bunny",
|
|
176
|
+
links: [],
|
|
177
|
+
},
|
|
178
|
+
defaultVideoID: "main",
|
|
179
|
+
trailers: [{ transport: { kind: "youtube", id: "aqz-KE-bpKQ" } }],
|
|
180
|
+
videos: [
|
|
181
|
+
{
|
|
182
|
+
id: "main",
|
|
183
|
+
title: "Main",
|
|
184
|
+
streams: [{ transport: { kind: "http", url: "https://example.com/video.mp4" } }],
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
}` : "null"},
|
|
188
|
+
}),
|
|
189
|
+
},`;
|
|
190
|
+
case "stream":
|
|
191
|
+
return `stream: {
|
|
192
|
+
mediaTypes: ["movie"],
|
|
193
|
+
supportedTransports: ${advanced ? `["http", "torrent", "usenet", "archive", "youtube"]` : `["http"]`},
|
|
194
|
+
handler: async () => ({
|
|
195
|
+
streams: [
|
|
196
|
+
{
|
|
197
|
+
transport: { kind: "http", url: "https://example.com/video.mp4", mode: "stream" },
|
|
198
|
+
hints: {
|
|
199
|
+
notWebReady: true,
|
|
200
|
+
proxyHeaders: { request: { "User-Agent": "StreamFox" } },
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
${advanced ? ` {
|
|
204
|
+
transport: { kind: "torrent", infoHash: "abcdef", peerDiscovery: ["tracker:udp://tracker.example.com:80"] },
|
|
205
|
+
selection: { fileIndex: 0 },
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
transport: { kind: "usenet", nzbURL: "https://example.com/file.nzb", servers: ["nntps://user:pass@news.example.com:563/4"] },
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
transport: {
|
|
212
|
+
kind: "archive",
|
|
213
|
+
format: "zip",
|
|
214
|
+
files: [{ url: "https://example.com/archive.zip", bytes: 1024 }],
|
|
215
|
+
},
|
|
216
|
+
selection: { fileMustInclude: "movie.mkv" },
|
|
217
|
+
},
|
|
218
|
+
` : ""} ],
|
|
219
|
+
}),
|
|
220
|
+
},`;
|
|
221
|
+
case "subtitles":
|
|
222
|
+
return `subtitles: {
|
|
223
|
+
mediaTypes: ["movie", "episode"],
|
|
224
|
+
defaultLanguages: ["en"],
|
|
225
|
+
handler: async (request, { settings }) => {
|
|
226
|
+
const configuredLanguages = Array.isArray(settings.languages)
|
|
227
|
+
? settings.languages
|
|
228
|
+
: [];
|
|
229
|
+
const languagePreferences =
|
|
230
|
+
configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);
|
|
231
|
+
|
|
232
|
+
void languagePreferences;
|
|
233
|
+
void settings.includeHI;
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
subtitles: [],
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
},`;
|
|
240
|
+
case "plugin_catalog":
|
|
241
|
+
return `pluginCatalog: {
|
|
242
|
+
endpoints: [
|
|
243
|
+
{
|
|
244
|
+
id: "featured",
|
|
245
|
+
name: "Featured",
|
|
246
|
+
pluginKinds: ["catalog", "meta", "stream", "subtitles"],
|
|
247
|
+
tags: ["official"],
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
handler: async () => ({
|
|
251
|
+
plugins: [
|
|
252
|
+
{
|
|
253
|
+
id: "com.example.recommended",
|
|
254
|
+
name: "Recommended",
|
|
255
|
+
version: "1.0.0",
|
|
256
|
+
pluginKinds: ["catalog", "meta"],
|
|
257
|
+
distribution: {
|
|
258
|
+
transport: "https",
|
|
259
|
+
manifestURL: "https://plugins.example.com/recommended/manifest",
|
|
260
|
+
},
|
|
261
|
+
manifestSnapshot: {
|
|
262
|
+
plugin: { id: "com.example.recommended" },
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
],
|
|
266
|
+
}),
|
|
267
|
+
},`;
|
|
268
|
+
default:
|
|
269
|
+
return "";
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function makeInstallBlock(preset) {
|
|
273
|
+
if (preset !== "subtitles") {
|
|
274
|
+
return "";
|
|
275
|
+
}
|
|
276
|
+
return ` install: {
|
|
277
|
+
configurationRequired: true,
|
|
278
|
+
title: "Subtitle Settings",
|
|
279
|
+
description: "Configure subtitle defaults before installing this plugin.",
|
|
280
|
+
fields: [
|
|
281
|
+
settings.multiSelect("languages", {
|
|
282
|
+
label: "Languages",
|
|
283
|
+
options: [
|
|
284
|
+
{ label: "English", value: "en" },
|
|
285
|
+
{ label: "Greek", value: "el" },
|
|
286
|
+
{ label: "Spanish", value: "es" },
|
|
287
|
+
],
|
|
288
|
+
defaultValue: ["en"],
|
|
289
|
+
}),
|
|
290
|
+
settings.checkbox("includeHI", {
|
|
291
|
+
label: "Include hearing impaired",
|
|
292
|
+
defaultValue: true,
|
|
293
|
+
}),
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
`;
|
|
297
|
+
}
|
|
298
|
+
function makePluginFile(name, preset, capabilities, advanced) {
|
|
299
|
+
const resources = capabilities.map((capability) => resourceBlock(capability, advanced)).join("\n ");
|
|
300
|
+
const install = makeInstallBlock(preset);
|
|
301
|
+
const importSpec = install.length > 0 ? "definePlugin, settings" : "definePlugin";
|
|
302
|
+
const installBlock = install.length > 0 ? `${install}` : "";
|
|
303
|
+
return `import { ${importSpec} } from "@streamfox/plugin-sdk";
|
|
304
|
+
|
|
305
|
+
export const plugin = definePlugin({
|
|
306
|
+
plugin: {
|
|
307
|
+
id: "com.example.${name}",
|
|
308
|
+
name: "${name}",
|
|
309
|
+
version: "0.1.0",
|
|
310
|
+
description: "Generated StreamFox plugin scaffold",
|
|
311
|
+
},
|
|
312
|
+
${installBlock} resources: {
|
|
313
|
+
${resources}
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
`;
|
|
317
|
+
}
|
|
318
|
+
function makeServerFile(language) {
|
|
319
|
+
const pluginImport = language === "ts" ? "./plugin" : "./plugin.js";
|
|
320
|
+
return `import { serve } from "@streamfox/plugin-sdk";
|
|
321
|
+
import { plugin } from "${pluginImport}";
|
|
322
|
+
|
|
323
|
+
const { url, installURL, launchURL } = await serve(plugin, {
|
|
324
|
+
port: Number(process.env.PORT ?? 7000),
|
|
325
|
+
integration: {
|
|
326
|
+
installScheme: "streamfox",
|
|
327
|
+
launchBaseURL: "https://streamfox.app/#",
|
|
328
|
+
autoOpen: "none",
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
console.log("Plugin manifest:", url);
|
|
333
|
+
console.log("Plugin installer deeplink:", installURL);
|
|
334
|
+
console.log("Plugin launch URL:", launchURL);
|
|
335
|
+
`;
|
|
336
|
+
}
|
|
337
|
+
function makeVitestFile(language) {
|
|
338
|
+
const pluginImport = language === "ts" ? "../src/plugin" : "../src/plugin.js";
|
|
339
|
+
return `import { describe, expect, it } from "vitest";
|
|
340
|
+
import { createServer } from "@streamfox/plugin-sdk";
|
|
341
|
+
import { plugin } from "${pluginImport}";
|
|
342
|
+
|
|
343
|
+
describe("scaffold smoke", () => {
|
|
344
|
+
it("serves manifest and studio config", async () => {
|
|
345
|
+
const app = createServer(plugin, { frontend: false });
|
|
346
|
+
|
|
347
|
+
const manifestResponse = await app.request("/manifest");
|
|
348
|
+
expect(manifestResponse.status).toBe(200);
|
|
349
|
+
|
|
350
|
+
const studioResponse = await app.request("/studio-config");
|
|
351
|
+
expect(studioResponse.status).toBe(200);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
`;
|
|
355
|
+
}
|
|
356
|
+
var tsConfig = `{
|
|
357
|
+
"compilerOptions": {
|
|
358
|
+
"target": "ES2022",
|
|
359
|
+
"module": "ESNext",
|
|
360
|
+
"moduleResolution": "Bundler",
|
|
361
|
+
"strict": true,
|
|
362
|
+
"declaration": true,
|
|
363
|
+
"outDir": "dist",
|
|
364
|
+
"rootDir": "src",
|
|
365
|
+
"types": ["node"]
|
|
366
|
+
},
|
|
367
|
+
"include": ["src"]
|
|
368
|
+
}
|
|
369
|
+
`;
|
|
370
|
+
var prettierConfig = `{
|
|
371
|
+
"semi": true,
|
|
372
|
+
"singleQuote": false,
|
|
373
|
+
"trailingComma": "all"
|
|
374
|
+
}
|
|
375
|
+
`;
|
|
376
|
+
var prettierIgnore = `dist
|
|
377
|
+
node_modules
|
|
378
|
+
.DS_Store
|
|
379
|
+
`;
|
|
380
|
+
function makeReadme(projectName, preset, capabilities, advanced) {
|
|
381
|
+
const capabilitiesList = capabilities.map((capability) => `- ${capability}`).join("\n");
|
|
382
|
+
const endpointForCapability = (capability) => {
|
|
383
|
+
switch (capability) {
|
|
384
|
+
case "catalog":
|
|
385
|
+
return "/catalog/:mediaType/:catalogID";
|
|
386
|
+
case "meta":
|
|
387
|
+
return "/meta/:mediaType/:itemID";
|
|
388
|
+
case "stream":
|
|
389
|
+
return "/stream/:mediaType/:itemID";
|
|
390
|
+
case "subtitles":
|
|
391
|
+
return "/subtitles/:mediaType/:itemID";
|
|
392
|
+
case "plugin_catalog":
|
|
393
|
+
return "/plugin_catalog/:catalogID/:pluginKind";
|
|
394
|
+
default:
|
|
395
|
+
return `/${capability}`;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
const endpointLines = [
|
|
399
|
+
"- GET /manifest",
|
|
400
|
+
"- GET /studio-config",
|
|
401
|
+
...capabilities.map(
|
|
402
|
+
(capability) => `- GET ${endpointForCapability(capability)}`
|
|
403
|
+
)
|
|
404
|
+
].join("\n");
|
|
405
|
+
return `# ${projectName}
|
|
406
|
+
|
|
407
|
+
Generated with create-streamfox-plugin.
|
|
408
|
+
|
|
409
|
+
Preset: \`${preset}\`
|
|
410
|
+
Advanced template: \`${advanced ? "enabled" : "disabled"}\`
|
|
411
|
+
|
|
412
|
+
## Scripts
|
|
413
|
+
|
|
414
|
+
- npm run dev
|
|
415
|
+
- npm run build
|
|
416
|
+
- npm run format
|
|
417
|
+
- npm run start
|
|
418
|
+
- npm run test
|
|
419
|
+
|
|
420
|
+
## Implemented Capabilities
|
|
421
|
+
|
|
422
|
+
${capabilitiesList}
|
|
423
|
+
|
|
424
|
+
## Stream Model
|
|
425
|
+
|
|
426
|
+
- Unified transport model via \`stream.transport\`
|
|
427
|
+
- Capability declaration via \`resources.stream.supportedTransports\`
|
|
428
|
+
- Optional selection controls via \`stream.selection\`
|
|
429
|
+
|
|
430
|
+
## Endpoints
|
|
431
|
+
|
|
432
|
+
${endpointLines}
|
|
433
|
+
`;
|
|
434
|
+
}
|
|
435
|
+
async function scaffoldProject(options) {
|
|
436
|
+
const capabilities = sortedCapabilities([
|
|
437
|
+
options.preset,
|
|
438
|
+
...options.extraCapabilities ?? []
|
|
439
|
+
]);
|
|
440
|
+
const sdkVersion = (options.sdkVersion ?? DEFAULT_SDK_VERSION).trim() || DEFAULT_SDK_VERSION;
|
|
441
|
+
const advanced = options.advanced ?? false;
|
|
442
|
+
await ensureTargetDoesNotExist(options.targetDir);
|
|
443
|
+
const srcDir = import_node_path.default.join(options.targetDir, "src");
|
|
444
|
+
const testDir = import_node_path.default.join(options.targetDir, "test");
|
|
445
|
+
await (0, import_promises.mkdir)(srcDir, { recursive: true });
|
|
446
|
+
await (0, import_promises.mkdir)(testDir, { recursive: true });
|
|
447
|
+
await (0, import_promises.writeFile)(
|
|
448
|
+
import_node_path.default.join(options.targetDir, "package.json"),
|
|
449
|
+
makePackageJson(options.projectName, options.language, sdkVersion)
|
|
450
|
+
);
|
|
451
|
+
await (0, import_promises.writeFile)(
|
|
452
|
+
import_node_path.default.join(options.targetDir, ".prettierrc.json"),
|
|
453
|
+
prettierConfig
|
|
454
|
+
);
|
|
455
|
+
await (0, import_promises.writeFile)(
|
|
456
|
+
import_node_path.default.join(options.targetDir, ".prettierignore"),
|
|
457
|
+
prettierIgnore
|
|
458
|
+
);
|
|
459
|
+
await (0, import_promises.writeFile)(
|
|
460
|
+
import_node_path.default.join(options.targetDir, "README.md"),
|
|
461
|
+
makeReadme(options.projectName, options.preset, capabilities, advanced)
|
|
462
|
+
);
|
|
463
|
+
if (options.language === "ts") {
|
|
464
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(options.targetDir, "tsconfig.json"), tsConfig);
|
|
465
|
+
await (0, import_promises.writeFile)(
|
|
466
|
+
import_node_path.default.join(srcDir, "plugin.ts"),
|
|
467
|
+
makePluginFile(
|
|
468
|
+
options.projectName,
|
|
469
|
+
options.preset,
|
|
470
|
+
capabilities,
|
|
471
|
+
advanced
|
|
472
|
+
)
|
|
473
|
+
);
|
|
474
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(srcDir, "server.ts"), makeServerFile("ts"));
|
|
475
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(testDir, "plugin.test.ts"), makeVitestFile("ts"));
|
|
476
|
+
} else {
|
|
477
|
+
await (0, import_promises.writeFile)(
|
|
478
|
+
import_node_path.default.join(srcDir, "plugin.js"),
|
|
479
|
+
makePluginFile(
|
|
480
|
+
options.projectName,
|
|
481
|
+
options.preset,
|
|
482
|
+
capabilities,
|
|
483
|
+
advanced
|
|
484
|
+
)
|
|
485
|
+
);
|
|
486
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(srcDir, "server.js"), makeServerFile("js"));
|
|
487
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(testDir, "plugin.test.js"), makeVitestFile("js"));
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/cli.ts
|
|
492
|
+
function capabilitiesLabel() {
|
|
493
|
+
return CAPABILITIES.join(", ");
|
|
494
|
+
}
|
|
495
|
+
function parsePreset(input) {
|
|
496
|
+
if (CAPABILITIES.includes(input)) {
|
|
497
|
+
return input;
|
|
498
|
+
}
|
|
499
|
+
throw new import_commander.InvalidArgumentError(
|
|
500
|
+
`Invalid preset '${input}'. Use one of: ${capabilitiesLabel()}`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
function parseCapabilitiesList(input) {
|
|
504
|
+
const parsed = input.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
|
|
505
|
+
for (const capability of parsed) {
|
|
506
|
+
if (!CAPABILITIES.includes(capability)) {
|
|
507
|
+
throw new import_commander.InvalidArgumentError(
|
|
508
|
+
`Invalid capability '${capability}'. Use one of: ${capabilitiesLabel()}`
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return Array.from(new Set(parsed));
|
|
513
|
+
}
|
|
514
|
+
var program = new import_commander.Command();
|
|
515
|
+
program.name("create-streamfox-plugin").version(package_default.version, "-v, --version", "display the current CLI version").description("Scaffold StreamFox plugin projects with modern JS/TS presets").showHelpAfterError().argument("[directory]", "output directory").option("--ts", "use TypeScript template").option("--js", "use JavaScript template").option(
|
|
516
|
+
"--preset <preset>",
|
|
517
|
+
`plugin preset: ${capabilitiesLabel()}`,
|
|
518
|
+
parsePreset
|
|
519
|
+
).option(
|
|
520
|
+
"--capabilities <capabilities>",
|
|
521
|
+
"extra capabilities as comma-separated list",
|
|
522
|
+
parseCapabilitiesList
|
|
523
|
+
).option("--advanced", "generate advanced capability examples").option(
|
|
524
|
+
"--sdk-version <range>",
|
|
525
|
+
"@streamfox/plugin-sdk version/range",
|
|
526
|
+
DEFAULT_SDK_VERSION
|
|
527
|
+
).option("--yes", "skip prompts and use defaults").action(
|
|
528
|
+
async (directoryArg, options) => {
|
|
529
|
+
if (options.ts && options.js) {
|
|
530
|
+
throw new import_commander.InvalidArgumentError("Choose either --ts or --js, not both.");
|
|
531
|
+
}
|
|
532
|
+
const promptDefaults = {
|
|
533
|
+
directory: directoryArg ?? "my-media-plugin",
|
|
534
|
+
language: options.ts ? "ts" : options.js ? "js" : "ts",
|
|
535
|
+
preset: options.preset ?? DEFAULT_PRESET,
|
|
536
|
+
extraCapabilities: options.capabilities ?? [],
|
|
537
|
+
advanced: options.advanced ?? false,
|
|
538
|
+
sdkVersion: options.sdkVersion ?? DEFAULT_SDK_VERSION
|
|
539
|
+
};
|
|
540
|
+
const shouldPrompt = !options.yes;
|
|
541
|
+
let directory = promptDefaults.directory;
|
|
542
|
+
let language = promptDefaults.language;
|
|
543
|
+
let preset = promptDefaults.preset;
|
|
544
|
+
let extraCapabilities = promptDefaults.extraCapabilities;
|
|
545
|
+
let advanced = promptDefaults.advanced;
|
|
546
|
+
let sdkVersion = promptDefaults.sdkVersion;
|
|
547
|
+
if (shouldPrompt) {
|
|
548
|
+
const answers = await (0, import_prompts.default)(
|
|
549
|
+
[
|
|
550
|
+
{
|
|
551
|
+
type: "text",
|
|
552
|
+
name: "directory",
|
|
553
|
+
message: "Project directory",
|
|
554
|
+
initial: directory
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
type: "select",
|
|
558
|
+
name: "language",
|
|
559
|
+
message: "Template language",
|
|
560
|
+
choices: [
|
|
561
|
+
{ title: "TypeScript", value: "ts" },
|
|
562
|
+
{ title: "JavaScript", value: "js" }
|
|
563
|
+
],
|
|
564
|
+
initial: language === "ts" ? 0 : 1
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
type: "select",
|
|
568
|
+
name: "preset",
|
|
569
|
+
message: "Plugin preset",
|
|
570
|
+
choices: CAPABILITIES.map((capability) => ({
|
|
571
|
+
title: capability,
|
|
572
|
+
value: capability
|
|
573
|
+
})),
|
|
574
|
+
initial: CAPABILITIES.indexOf(preset)
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
type: "multiselect",
|
|
578
|
+
name: "extraCapabilities",
|
|
579
|
+
message: "Extra capabilities (optional)",
|
|
580
|
+
choices: CAPABILITIES.map((capability) => ({
|
|
581
|
+
title: capability,
|
|
582
|
+
value: capability
|
|
583
|
+
})),
|
|
584
|
+
instructions: false,
|
|
585
|
+
min: 0,
|
|
586
|
+
hint: "Space to select"
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
type: "confirm",
|
|
590
|
+
name: "advanced",
|
|
591
|
+
message: "Generate advanced capability examples?",
|
|
592
|
+
initial: advanced
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
type: "text",
|
|
596
|
+
name: "sdkVersion",
|
|
597
|
+
message: "SDK dependency version/range",
|
|
598
|
+
initial: sdkVersion
|
|
599
|
+
}
|
|
600
|
+
],
|
|
601
|
+
{
|
|
602
|
+
onCancel: () => {
|
|
603
|
+
process.exit(1);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
);
|
|
607
|
+
directory = answers.directory;
|
|
608
|
+
language = answers.language;
|
|
609
|
+
preset = answers.preset ?? promptDefaults.preset;
|
|
610
|
+
extraCapabilities = answers.extraCapabilities ?? [];
|
|
611
|
+
advanced = Boolean(answers.advanced ?? promptDefaults.advanced);
|
|
612
|
+
sdkVersion = answers.sdkVersion ?? promptDefaults.sdkVersion;
|
|
613
|
+
}
|
|
614
|
+
const targetDir = import_node_path2.default.resolve(process.cwd(), directory);
|
|
615
|
+
const projectName = import_node_path2.default.basename(targetDir);
|
|
616
|
+
await scaffoldProject({
|
|
617
|
+
targetDir,
|
|
618
|
+
projectName,
|
|
619
|
+
language,
|
|
620
|
+
preset,
|
|
621
|
+
extraCapabilities,
|
|
622
|
+
advanced,
|
|
623
|
+
sdkVersion
|
|
624
|
+
});
|
|
625
|
+
console.log(`Created ${projectName} at ${targetDir}`);
|
|
626
|
+
console.log("Next steps:");
|
|
627
|
+
console.log(` cd ${directory}`);
|
|
628
|
+
console.log(" npm install");
|
|
629
|
+
console.log(" npm run dev");
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
void program.parseAsync(process.argv).catch((error) => {
|
|
633
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
634
|
+
console.error(`Error: ${message}`);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
});
|
|
637
|
+
//# sourceMappingURL=cli.cjs.map
|