@streamfox/create-streamfox-plugin 0.3.0 → 0.3.2
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 +5 -7
- package/dist/cli.cjs +32 -54
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +32 -54
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +16 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +16 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,8 +47,8 @@ create-streamfox-plugin my-plugin --yes
|
|
|
47
47
|
| `[directory]` | positional | `my-media-plugin` | Output directory. |
|
|
48
48
|
| `--ts` | flag | `true` (unless `--js`) | Generate TypeScript template. |
|
|
49
49
|
| `--js` | flag | no | Generate JavaScript template. |
|
|
50
|
-
| `--preset <preset>` | enum | `meta` |
|
|
51
|
-
| `--capabilities <a,b,c>` | csv enum list |
|
|
50
|
+
| `--preset <preset>` | enum | `meta` | Legacy/compat primary template hint. Interactive mode now asks for capabilities directly. |
|
|
51
|
+
| `--capabilities <a,b,c>` | csv enum list | `meta` | Selected capabilities. One of: `catalog`, `meta`, `stream`, `subtitles`, `plugin_catalog`. |
|
|
52
52
|
| `--advanced` | flag | `false` | Generate richer examples (torrent/usenet/archive/trailers/distribution). |
|
|
53
53
|
| `--sdk-version <range>` | string | `^0.2.0` | Dependency range for `@streamfox/plugin-sdk`. |
|
|
54
54
|
| `--yes` | flag | `false` | Skip prompts and use provided/default values. |
|
|
@@ -56,13 +56,12 @@ create-streamfox-plugin my-plugin --yes
|
|
|
56
56
|
|
|
57
57
|
## Examples
|
|
58
58
|
|
|
59
|
-
Create subtitles
|
|
59
|
+
Create a subtitles + meta + stream plugin with advanced examples:
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
create-streamfox-plugin streamfox-opensubs \
|
|
63
63
|
--ts \
|
|
64
|
-
--
|
|
65
|
-
--capabilities meta,stream \
|
|
64
|
+
--capabilities subtitles,meta,stream \
|
|
66
65
|
--advanced \
|
|
67
66
|
--sdk-version ^0.2.0 \
|
|
68
67
|
--yes
|
|
@@ -76,13 +75,12 @@ create-streamfox-plugin streamfox-opensubs \
|
|
|
76
75
|
- install deeplink URL
|
|
77
76
|
- launch URL
|
|
78
77
|
- `test/plugin.test.(ts|js)` smoke test (`/manifest`, `/studio-config`)
|
|
79
|
-
- `README.md`, `package.json`, `.
|
|
78
|
+
- `README.md`, `package.json`, `.gitignore`, and (for TS) `tsconfig.json`
|
|
80
79
|
|
|
81
80
|
## Development
|
|
82
81
|
|
|
83
82
|
```bash
|
|
84
83
|
npm install
|
|
85
|
-
npm run format
|
|
86
84
|
npm run build
|
|
87
85
|
npm test
|
|
88
86
|
```
|
package/dist/cli.cjs
CHANGED
|
@@ -31,7 +31,7 @@ var import_prompts = __toESM(require("prompts"), 1);
|
|
|
31
31
|
// package.json
|
|
32
32
|
var package_default = {
|
|
33
33
|
name: "@streamfox/create-streamfox-plugin",
|
|
34
|
-
version: "0.3.
|
|
34
|
+
version: "0.3.1",
|
|
35
35
|
description: "Standalone CLI to scaffold StreamFox plugin projects",
|
|
36
36
|
type: "module",
|
|
37
37
|
bin: {
|
|
@@ -107,22 +107,18 @@ function makePackageJson(name, language, sdkVersion) {
|
|
|
107
107
|
const scripts = language === "ts" ? {
|
|
108
108
|
dev: "tsx watch src/server.ts",
|
|
109
109
|
build: "tsc -p tsconfig.json",
|
|
110
|
-
format: "prettier --write .",
|
|
111
|
-
"format:check": "prettier --check .",
|
|
112
110
|
start: "node dist/server.js",
|
|
113
111
|
test: "vitest run",
|
|
114
112
|
typecheck: "tsc --noEmit"
|
|
115
113
|
} : {
|
|
116
114
|
dev: "node --watch src/server.js",
|
|
117
115
|
build: 'echo "No build step for JavaScript template"',
|
|
118
|
-
format: "prettier --write .",
|
|
119
|
-
"format:check": "prettier --check .",
|
|
120
116
|
start: "node src/server.js",
|
|
121
117
|
test: "vitest run"
|
|
122
118
|
};
|
|
123
119
|
const normalizedScripts = {
|
|
124
120
|
...scripts,
|
|
125
|
-
check: language === "ts" ? "npm run
|
|
121
|
+
check: language === "ts" ? "npm run typecheck && npm test && npm run build" : "npm test && npm run build"
|
|
126
122
|
};
|
|
127
123
|
const packageJson = {
|
|
128
124
|
name,
|
|
@@ -135,12 +131,10 @@ function makePackageJson(name, language, sdkVersion) {
|
|
|
135
131
|
},
|
|
136
132
|
devDependencies: language === "ts" ? {
|
|
137
133
|
"@types/node": "^24.6.0",
|
|
138
|
-
prettier: "^3.6.2",
|
|
139
134
|
tsx: "^4.20.5",
|
|
140
135
|
typescript: "^5.9.2",
|
|
141
136
|
vitest: "^2.1.9"
|
|
142
137
|
} : {
|
|
143
|
-
prettier: "^3.6.2",
|
|
144
138
|
vitest: "^2.1.9"
|
|
145
139
|
}
|
|
146
140
|
};
|
|
@@ -269,8 +263,8 @@ ${advanced ? ` {
|
|
|
269
263
|
return "";
|
|
270
264
|
}
|
|
271
265
|
}
|
|
272
|
-
function makeInstallBlock(
|
|
273
|
-
if (
|
|
266
|
+
function makeInstallBlock(capabilities) {
|
|
267
|
+
if (!capabilities.includes("subtitles")) {
|
|
274
268
|
return "";
|
|
275
269
|
}
|
|
276
270
|
return ` install: {
|
|
@@ -295,9 +289,9 @@ function makeInstallBlock(preset) {
|
|
|
295
289
|
},
|
|
296
290
|
`;
|
|
297
291
|
}
|
|
298
|
-
function makePluginFile(name,
|
|
292
|
+
function makePluginFile(name, capabilities, advanced) {
|
|
299
293
|
const resources = capabilities.map((capability) => resourceBlock(capability, advanced)).join("\n ");
|
|
300
|
-
const install = makeInstallBlock(
|
|
294
|
+
const install = makeInstallBlock(capabilities);
|
|
301
295
|
const importSpec = install.length > 0 ? "definePlugin, settings" : "definePlugin";
|
|
302
296
|
const installBlock = install.length > 0 ? `${install}` : "";
|
|
303
297
|
return `import { ${importSpec} } from "@streamfox/plugin-sdk";
|
|
@@ -367,17 +361,14 @@ var tsConfig = `{
|
|
|
367
361
|
"include": ["src"]
|
|
368
362
|
}
|
|
369
363
|
`;
|
|
370
|
-
var
|
|
371
|
-
"semi": true,
|
|
372
|
-
"singleQuote": false,
|
|
373
|
-
"trailingComma": "all"
|
|
374
|
-
}
|
|
375
|
-
`;
|
|
376
|
-
var prettierIgnore = `dist
|
|
364
|
+
var gitIgnore = `dist
|
|
377
365
|
node_modules
|
|
378
366
|
.DS_Store
|
|
367
|
+
.env
|
|
368
|
+
.env.*
|
|
369
|
+
coverage
|
|
379
370
|
`;
|
|
380
|
-
function makeReadme(projectName,
|
|
371
|
+
function makeReadme(projectName, capabilities, advanced) {
|
|
381
372
|
const capabilitiesList = capabilities.map((capability) => `- ${capability}`).join("\n");
|
|
382
373
|
const endpointForCapability = (capability) => {
|
|
383
374
|
switch (capability) {
|
|
@@ -406,16 +397,16 @@ function makeReadme(projectName, preset, capabilities, advanced) {
|
|
|
406
397
|
|
|
407
398
|
Generated with create-streamfox-plugin.
|
|
408
399
|
|
|
409
|
-
|
|
400
|
+
Capabilities: \`${capabilities.join(", ")}\`
|
|
410
401
|
Advanced template: \`${advanced ? "enabled" : "disabled"}\`
|
|
411
402
|
|
|
412
403
|
## Scripts
|
|
413
404
|
|
|
414
405
|
- npm run dev
|
|
415
406
|
- npm run build
|
|
416
|
-
- npm run format
|
|
417
407
|
- npm run start
|
|
418
408
|
- npm run test
|
|
409
|
+
- npm run check
|
|
419
410
|
|
|
420
411
|
## Implemented Capabilities
|
|
421
412
|
|
|
@@ -433,8 +424,8 @@ ${endpointLines}
|
|
|
433
424
|
`;
|
|
434
425
|
}
|
|
435
426
|
async function scaffoldProject(options) {
|
|
436
|
-
const capabilities = sortedCapabilities([
|
|
437
|
-
options.preset,
|
|
427
|
+
const capabilities = options.capabilities ? sortedCapabilities(options.capabilities) : sortedCapabilities([
|
|
428
|
+
options.preset ?? DEFAULT_PRESET,
|
|
438
429
|
...options.extraCapabilities ?? []
|
|
439
430
|
]);
|
|
440
431
|
const sdkVersion = (options.sdkVersion ?? DEFAULT_SDK_VERSION).trim() || DEFAULT_SDK_VERSION;
|
|
@@ -448,17 +439,10 @@ async function scaffoldProject(options) {
|
|
|
448
439
|
import_node_path.default.join(options.targetDir, "package.json"),
|
|
449
440
|
makePackageJson(options.projectName, options.language, sdkVersion)
|
|
450
441
|
);
|
|
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
|
-
);
|
|
442
|
+
await (0, import_promises.writeFile)(import_node_path.default.join(options.targetDir, ".gitignore"), gitIgnore);
|
|
459
443
|
await (0, import_promises.writeFile)(
|
|
460
444
|
import_node_path.default.join(options.targetDir, "README.md"),
|
|
461
|
-
makeReadme(options.projectName,
|
|
445
|
+
makeReadme(options.projectName, capabilities, advanced)
|
|
462
446
|
);
|
|
463
447
|
if (options.language === "ts") {
|
|
464
448
|
await (0, import_promises.writeFile)(import_node_path.default.join(options.targetDir, "tsconfig.json"), tsConfig);
|
|
@@ -466,7 +450,6 @@ async function scaffoldProject(options) {
|
|
|
466
450
|
import_node_path.default.join(srcDir, "plugin.ts"),
|
|
467
451
|
makePluginFile(
|
|
468
452
|
options.projectName,
|
|
469
|
-
options.preset,
|
|
470
453
|
capabilities,
|
|
471
454
|
advanced
|
|
472
455
|
)
|
|
@@ -478,7 +461,6 @@ async function scaffoldProject(options) {
|
|
|
478
461
|
import_node_path.default.join(srcDir, "plugin.js"),
|
|
479
462
|
makePluginFile(
|
|
480
463
|
options.projectName,
|
|
481
|
-
options.preset,
|
|
482
464
|
capabilities,
|
|
483
465
|
advanced
|
|
484
466
|
)
|
|
@@ -533,7 +515,12 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
533
515
|
directory: directoryArg ?? "my-media-plugin",
|
|
534
516
|
language: options.ts ? "ts" : options.js ? "js" : "ts",
|
|
535
517
|
preset: options.preset ?? DEFAULT_PRESET,
|
|
536
|
-
|
|
518
|
+
capabilities: Array.from(
|
|
519
|
+
/* @__PURE__ */ new Set([
|
|
520
|
+
options.preset ?? DEFAULT_PRESET,
|
|
521
|
+
...options.capabilities ?? []
|
|
522
|
+
])
|
|
523
|
+
),
|
|
537
524
|
advanced: options.advanced ?? false,
|
|
538
525
|
sdkVersion: options.sdkVersion ?? DEFAULT_SDK_VERSION
|
|
539
526
|
};
|
|
@@ -541,7 +528,7 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
541
528
|
let directory = promptDefaults.directory;
|
|
542
529
|
let language = promptDefaults.language;
|
|
543
530
|
let preset = promptDefaults.preset;
|
|
544
|
-
let
|
|
531
|
+
let capabilities = promptDefaults.capabilities;
|
|
545
532
|
let advanced = promptDefaults.advanced;
|
|
546
533
|
let sdkVersion = promptDefaults.sdkVersion;
|
|
547
534
|
if (shouldPrompt) {
|
|
@@ -563,26 +550,17 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
563
550
|
],
|
|
564
551
|
initial: language === "ts" ? 0 : 1
|
|
565
552
|
},
|
|
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
553
|
{
|
|
577
554
|
type: "multiselect",
|
|
578
|
-
name: "
|
|
579
|
-
message: "
|
|
555
|
+
name: "capabilities",
|
|
556
|
+
message: "Plugin capabilities",
|
|
580
557
|
choices: CAPABILITIES.map((capability) => ({
|
|
581
558
|
title: capability,
|
|
582
|
-
value: capability
|
|
559
|
+
value: capability,
|
|
560
|
+
selected: capabilities.includes(capability)
|
|
583
561
|
})),
|
|
584
562
|
instructions: false,
|
|
585
|
-
min:
|
|
563
|
+
min: 1,
|
|
586
564
|
hint: "Space to select"
|
|
587
565
|
},
|
|
588
566
|
{
|
|
@@ -606,8 +584,8 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
606
584
|
);
|
|
607
585
|
directory = answers.directory;
|
|
608
586
|
language = answers.language;
|
|
609
|
-
|
|
610
|
-
|
|
587
|
+
capabilities = answers.capabilities ?? promptDefaults.capabilities;
|
|
588
|
+
preset = capabilities[0] ?? promptDefaults.preset;
|
|
611
589
|
advanced = Boolean(answers.advanced ?? promptDefaults.advanced);
|
|
612
590
|
sdkVersion = answers.sdkVersion ?? promptDefaults.sdkVersion;
|
|
613
591
|
}
|
|
@@ -617,8 +595,8 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
617
595
|
targetDir,
|
|
618
596
|
projectName,
|
|
619
597
|
language,
|
|
598
|
+
capabilities,
|
|
620
599
|
preset,
|
|
621
|
-
extraCapabilities,
|
|
622
600
|
advanced,
|
|
623
601
|
sdkVersion
|
|
624
602
|
});
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../package.json","../src/scaffold.ts"],"sourcesContent":["import path from \"node:path\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport prompts from \"prompts\";\nimport packageJSON from \"../package.json\";\nimport {\n CAPABILITIES,\n DEFAULT_PRESET,\n DEFAULT_SDK_VERSION,\n scaffoldProject,\n type Capability,\n type Language,\n type Preset,\n} from \"./scaffold\";\n\nfunction capabilitiesLabel(): string {\n return CAPABILITIES.join(\", \");\n}\n\nfunction parsePreset(input: string): Preset {\n if (CAPABILITIES.includes(input as Capability)) {\n return input as Preset;\n }\n throw new InvalidArgumentError(\n `Invalid preset '${input}'. Use one of: ${capabilitiesLabel()}`,\n );\n}\n\nfunction parseCapabilitiesList(input: string): Capability[] {\n const parsed = input\n .split(\",\")\n .map((value) => value.trim())\n .filter((value) => value.length > 0) as Capability[];\n\n for (const capability of parsed) {\n if (!CAPABILITIES.includes(capability)) {\n throw new InvalidArgumentError(\n `Invalid capability '${capability}'. Use one of: ${capabilitiesLabel()}`,\n );\n }\n }\n\n return Array.from(new Set(parsed));\n}\n\nconst program = new Command();\n\nprogram\n .name(\"create-streamfox-plugin\")\n .version(packageJSON.version, \"-v, --version\", \"display the current CLI version\")\n .description(\"Scaffold StreamFox plugin projects with modern JS/TS presets\")\n .showHelpAfterError()\n .argument(\"[directory]\", \"output directory\")\n .option(\"--ts\", \"use TypeScript template\")\n .option(\"--js\", \"use JavaScript template\")\n .option(\n \"--preset <preset>\",\n `plugin preset: ${capabilitiesLabel()}`,\n parsePreset,\n )\n .option(\n \"--capabilities <capabilities>\",\n \"extra capabilities as comma-separated list\",\n parseCapabilitiesList,\n )\n .option(\"--advanced\", \"generate advanced capability examples\")\n .option(\n \"--sdk-version <range>\",\n \"@streamfox/plugin-sdk version/range\",\n DEFAULT_SDK_VERSION,\n )\n .option(\"--yes\", \"skip prompts and use defaults\")\n .action(\n async (\n directoryArg: string | undefined,\n options: {\n ts?: boolean;\n js?: boolean;\n yes?: boolean;\n preset?: Preset;\n capabilities?: Capability[];\n advanced?: boolean;\n sdkVersion?: string;\n },\n ) => {\n if (options.ts && options.js) {\n throw new InvalidArgumentError(\"Choose either --ts or --js, not both.\");\n }\n\n const promptDefaults = {\n directory: directoryArg ?? \"my-media-plugin\",\n language: options.ts ? \"ts\" : options.js ? \"js\" : \"ts\",\n preset: options.preset ?? DEFAULT_PRESET,\n extraCapabilities: options.capabilities ?? [],\n advanced: options.advanced ?? false,\n sdkVersion: options.sdkVersion ?? DEFAULT_SDK_VERSION,\n };\n\n const shouldPrompt = !options.yes;\n\n let directory = promptDefaults.directory;\n let language = promptDefaults.language as Language;\n let preset = promptDefaults.preset;\n let extraCapabilities = promptDefaults.extraCapabilities;\n let advanced = promptDefaults.advanced;\n let sdkVersion = promptDefaults.sdkVersion;\n\n if (shouldPrompt) {\n const answers = await prompts(\n [\n {\n type: \"text\",\n name: \"directory\",\n message: \"Project directory\",\n initial: directory,\n },\n {\n type: \"select\",\n name: \"language\",\n message: \"Template language\",\n choices: [\n { title: \"TypeScript\", value: \"ts\" },\n { title: \"JavaScript\", value: \"js\" },\n ],\n initial: language === \"ts\" ? 0 : 1,\n },\n {\n type: \"select\",\n name: \"preset\",\n message: \"Plugin preset\",\n choices: CAPABILITIES.map((capability) => ({\n title: capability,\n value: capability,\n })),\n initial: CAPABILITIES.indexOf(preset),\n },\n {\n type: \"multiselect\",\n name: \"extraCapabilities\",\n message: \"Extra capabilities (optional)\",\n choices: CAPABILITIES.map((capability) => ({\n title: capability,\n value: capability,\n })),\n instructions: false,\n min: 0,\n hint: \"Space to select\",\n },\n {\n type: \"confirm\",\n name: \"advanced\",\n message: \"Generate advanced capability examples?\",\n initial: advanced,\n },\n {\n type: \"text\",\n name: \"sdkVersion\",\n message: \"SDK dependency version/range\",\n initial: sdkVersion,\n },\n ],\n {\n onCancel: () => {\n process.exit(1);\n },\n },\n );\n\n directory = answers.directory;\n language = answers.language;\n preset = (answers.preset ?? promptDefaults.preset) as Preset;\n extraCapabilities = (answers.extraCapabilities ?? []) as Capability[];\n advanced = Boolean(answers.advanced ?? promptDefaults.advanced);\n sdkVersion = answers.sdkVersion ?? promptDefaults.sdkVersion;\n }\n\n const targetDir = path.resolve(process.cwd(), directory);\n const projectName = path.basename(targetDir);\n\n await scaffoldProject({\n targetDir,\n projectName,\n language,\n preset,\n extraCapabilities,\n advanced,\n sdkVersion,\n });\n\n console.log(`Created ${projectName} at ${targetDir}`);\n console.log(\"Next steps:\");\n console.log(` cd ${directory}`);\n console.log(\" npm install\");\n console.log(\" npm run dev\");\n },\n );\n\nvoid program.parseAsync(process.argv).catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`Error: ${message}`);\n process.exit(1);\n});\n","{\n \"name\": \"@streamfox/create-streamfox-plugin\",\n \"version\": \"0.3.0\",\n \"description\": \"Standalone CLI to scaffold StreamFox plugin projects\",\n \"type\": \"module\",\n \"bin\": {\n \"create-streamfox-plugin\": \"dist/cli.cjs\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"test\": \"vitest run\",\n \"typecheck\": \"tsc --noEmit\",\n \"check\": \"npm run format:check && npm run typecheck && npm test && npm run build\"\n },\n \"dependencies\": {\n \"commander\": \"^12.1.0\",\n \"prompts\": \"^2.4.2\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.6.0\",\n \"@types/prompts\": \"^2.4.9\",\n \"prettier\": \"^3.6.2\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.2\",\n \"vitest\": \"^2.1.9\"\n },\n \"engines\": {\n \"node\": \">=20\"\n }\n}\n","import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const CAPABILITIES = [\n \"catalog\",\n \"meta\",\n \"stream\",\n \"subtitles\",\n \"plugin_catalog\",\n] as const;\nexport type Capability = (typeof CAPABILITIES)[number];\nexport type Preset = Capability;\nexport type Language = \"ts\" | \"js\";\nexport const DEFAULT_PRESET: Preset = \"meta\";\nexport const DEFAULT_SDK_VERSION = \"^0.2.0\";\n\nexport interface ScaffoldOptions {\n targetDir: string;\n projectName: string;\n language: Language;\n preset: Preset;\n advanced?: boolean;\n extraCapabilities?: Capability[];\n sdkVersion?: string;\n}\n\nfunction sortedCapabilities(values: Capability[]): Capability[] {\n const unique = Array.from(new Set(values));\n return CAPABILITIES.filter((capability) => unique.includes(capability));\n}\n\nasync function ensureTargetDoesNotExist(targetDir: string): Promise<void> {\n try {\n await access(targetDir);\n throw new Error(`Target directory already exists: ${targetDir}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return;\n }\n throw error;\n }\n}\n\nfunction makePackageJson(\n name: string,\n language: Language,\n sdkVersion: string,\n): string {\n const scripts =\n language === \"ts\"\n ? {\n dev: \"tsx watch src/server.ts\",\n build: \"tsc -p tsconfig.json\",\n format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node dist/server.js\",\n test: \"vitest run\",\n typecheck: \"tsc --noEmit\",\n }\n : {\n dev: \"node --watch src/server.js\",\n build: 'echo \"No build step for JavaScript template\"',\n format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node src/server.js\",\n test: \"vitest run\",\n };\n\n const normalizedScripts = {\n ...scripts,\n check:\n language === \"ts\"\n ? \"npm run format:check && npm run typecheck && npm test && npm run build\"\n : \"npm run format:check && npm test && npm run build\",\n };\n\n const packageJson = {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: normalizedScripts,\n dependencies: {\n \"@streamfox/plugin-sdk\": sdkVersion,\n },\n devDependencies:\n language === \"ts\"\n ? {\n \"@types/node\": \"^24.6.0\",\n prettier: \"^3.6.2\",\n tsx: \"^4.20.5\",\n typescript: \"^5.9.2\",\n vitest: \"^2.1.9\",\n }\n : {\n prettier: \"^3.6.2\",\n vitest: \"^2.1.9\",\n },\n };\n\n return `${JSON.stringify(packageJson, null, 2)}\\n`;\n}\n\nfunction resourceBlock(capability: Capability, advanced: boolean): string {\n switch (capability) {\n case \"catalog\":\n return `catalog: {\n endpoints: [\n {\n id: \"top\",\n name: \"Top\",\n mediaTypes: [\"movie\"],\n filters: [{ key: \"genre\", valueType: \"string\" }],\n },\n ],\n handler: async () => ({\n items: [],\n }),\n },`;\n case \"meta\":\n return `meta: {\n mediaTypes: [\"movie\"],\n includes: [\"videos\", \"links\"],\n handler: async () => ({\n item: ${\n advanced\n ? `{\n summary: {\n id: { namespace: \"imdb\", value: \"tt1254207\" },\n mediaType: \"movie\",\n title: \"Big Buck Bunny\",\n links: [],\n },\n defaultVideoID: \"main\",\n trailers: [{ transport: { kind: \"youtube\", id: \"aqz-KE-bpKQ\" } }],\n videos: [\n {\n id: \"main\",\n title: \"Main\",\n streams: [{ transport: { kind: \"http\", url: \"https://example.com/video.mp4\" } }],\n },\n ],\n }`\n : \"null\"\n },\n }),\n },`;\n case \"stream\":\n return `stream: {\n mediaTypes: [\"movie\"],\n supportedTransports: ${advanced ? `[\"http\", \"torrent\", \"usenet\", \"archive\", \"youtube\"]` : `[\"http\"]`},\n handler: async () => ({\n streams: [\n {\n transport: { kind: \"http\", url: \"https://example.com/video.mp4\", mode: \"stream\" },\n hints: {\n notWebReady: true,\n proxyHeaders: { request: { \"User-Agent\": \"StreamFox\" } },\n },\n },\n${\n advanced\n ? ` {\n transport: { kind: \"torrent\", infoHash: \"abcdef\", peerDiscovery: [\"tracker:udp://tracker.example.com:80\"] },\n selection: { fileIndex: 0 },\n },\n {\n transport: { kind: \"usenet\", nzbURL: \"https://example.com/file.nzb\", servers: [\"nntps://user:pass@news.example.com:563/4\"] },\n },\n {\n transport: {\n kind: \"archive\",\n format: \"zip\",\n files: [{ url: \"https://example.com/archive.zip\", bytes: 1024 }],\n },\n selection: { fileMustInclude: \"movie.mkv\" },\n },\n`\n : \"\"\n} ],\n }),\n },`;\n case \"subtitles\":\n return `subtitles: {\n mediaTypes: [\"movie\", \"episode\"],\n defaultLanguages: [\"en\"],\n handler: async (request, { settings }) => {\n const configuredLanguages = Array.isArray(settings.languages)\n ? settings.languages\n : [];\n const languagePreferences =\n configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);\n\n void languagePreferences;\n void settings.includeHI;\n\n return {\n subtitles: [],\n };\n },\n },`;\n case \"plugin_catalog\":\n return `pluginCatalog: {\n endpoints: [\n {\n id: \"featured\",\n name: \"Featured\",\n pluginKinds: [\"catalog\", \"meta\", \"stream\", \"subtitles\"],\n tags: [\"official\"],\n },\n ],\n handler: async () => ({\n plugins: [\n {\n id: \"com.example.recommended\",\n name: \"Recommended\",\n version: \"1.0.0\",\n pluginKinds: [\"catalog\", \"meta\"],\n distribution: {\n transport: \"https\",\n manifestURL: \"https://plugins.example.com/recommended/manifest\",\n },\n manifestSnapshot: {\n plugin: { id: \"com.example.recommended\" },\n },\n },\n ],\n }),\n },`;\n default:\n return \"\";\n }\n}\n\nfunction makeInstallBlock(preset: Preset): string {\n if (preset !== \"subtitles\") {\n return \"\";\n }\n\n return ` install: {\n configurationRequired: true,\n title: \"Subtitle Settings\",\n description: \"Configure subtitle defaults before installing this plugin.\",\n fields: [\n settings.multiSelect(\"languages\", {\n label: \"Languages\",\n options: [\n { label: \"English\", value: \"en\" },\n { label: \"Greek\", value: \"el\" },\n { label: \"Spanish\", value: \"es\" },\n ],\n defaultValue: [\"en\"],\n }),\n settings.checkbox(\"includeHI\", {\n label: \"Include hearing impaired\",\n defaultValue: true,\n }),\n ],\n },\n`;\n}\n\nfunction makePluginFile(\n name: string,\n preset: Preset,\n capabilities: Capability[],\n advanced: boolean,\n): string {\n const resources = capabilities\n .map((capability) => resourceBlock(capability, advanced))\n .join(\"\\n \");\n const install = makeInstallBlock(preset);\n const importSpec =\n install.length > 0 ? \"definePlugin, settings\" : \"definePlugin\";\n const installBlock = install.length > 0 ? `${install}` : \"\";\n\n return `import { ${importSpec} } from \"@streamfox/plugin-sdk\";\n\nexport const plugin = definePlugin({\n plugin: {\n id: \"com.example.${name}\",\n name: \"${name}\",\n version: \"0.1.0\",\n description: \"Generated StreamFox plugin scaffold\",\n },\n${installBlock} resources: {\n ${resources}\n },\n});\n`;\n}\n\nfunction makeServerFile(language: Language): string {\n const pluginImport = language === \"ts\" ? \"./plugin\" : \"./plugin.js\";\n return `import { serve } from \"@streamfox/plugin-sdk\";\nimport { plugin } from \"${pluginImport}\";\n\nconst { url, installURL, launchURL } = await serve(plugin, {\n port: Number(process.env.PORT ?? 7000),\n integration: {\n installScheme: \"streamfox\",\n launchBaseURL: \"https://streamfox.app/#\",\n autoOpen: \"none\",\n },\n});\n\nconsole.log(\"Plugin manifest:\", url);\nconsole.log(\"Plugin installer deeplink:\", installURL);\nconsole.log(\"Plugin launch URL:\", launchURL);\n`;\n}\n\nfunction makeVitestFile(language: Language): string {\n const pluginImport = language === \"ts\" ? \"../src/plugin\" : \"../src/plugin.js\";\n return `import { describe, expect, it } from \"vitest\";\nimport { createServer } from \"@streamfox/plugin-sdk\";\nimport { plugin } from \"${pluginImport}\";\n\ndescribe(\"scaffold smoke\", () => {\n it(\"serves manifest and studio config\", async () => {\n const app = createServer(plugin, { frontend: false });\n\n const manifestResponse = await app.request(\"/manifest\");\n expect(manifestResponse.status).toBe(200);\n\n const studioResponse = await app.request(\"/studio-config\");\n expect(studioResponse.status).toBe(200);\n });\n});\n`;\n}\n\nconst tsConfig = `{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Bundler\",\n \"strict\": true,\n \"declaration\": true,\n \"outDir\": \"dist\",\n \"rootDir\": \"src\",\n \"types\": [\"node\"]\n },\n \"include\": [\"src\"]\n}\n`;\n\nconst prettierConfig = `{\n \"semi\": true,\n \"singleQuote\": false,\n \"trailingComma\": \"all\"\n}\n`;\n\nconst prettierIgnore = `dist\nnode_modules\n.DS_Store\n`;\n\nfunction makeReadme(\n projectName: string,\n preset: Preset,\n capabilities: Capability[],\n advanced: boolean,\n): string {\n const capabilitiesList = capabilities\n .map((capability) => `- ${capability}`)\n .join(\"\\n\");\n\n const endpointForCapability = (capability: Capability): string => {\n switch (capability) {\n case \"catalog\":\n return \"/catalog/:mediaType/:catalogID\";\n case \"meta\":\n return \"/meta/:mediaType/:itemID\";\n case \"stream\":\n return \"/stream/:mediaType/:itemID\";\n case \"subtitles\":\n return \"/subtitles/:mediaType/:itemID\";\n case \"plugin_catalog\":\n return \"/plugin_catalog/:catalogID/:pluginKind\";\n default:\n return `/${capability}`;\n }\n };\n\n const endpointLines = [\n \"- GET /manifest\",\n \"- GET /studio-config\",\n ...capabilities.map(\n (capability) => `- GET ${endpointForCapability(capability)}`,\n ),\n ].join(\"\\n\");\n\n return `# ${projectName}\n\nGenerated with create-streamfox-plugin.\n\nPreset: \\`${preset}\\`\nAdvanced template: \\`${advanced ? \"enabled\" : \"disabled\"}\\`\n\n## Scripts\n\n- npm run dev\n- npm run build\n- npm run format\n- npm run start\n- npm run test\n\n## Implemented Capabilities\n\n${capabilitiesList}\n\n## Stream Model\n\n- Unified transport model via \\`stream.transport\\`\n- Capability declaration via \\`resources.stream.supportedTransports\\`\n- Optional selection controls via \\`stream.selection\\`\n\n## Endpoints\n\n${endpointLines}\n`;\n}\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<void> {\n const capabilities = sortedCapabilities([\n options.preset,\n ...(options.extraCapabilities ?? []),\n ]);\n const sdkVersion =\n (options.sdkVersion ?? DEFAULT_SDK_VERSION).trim() || DEFAULT_SDK_VERSION;\n const advanced = options.advanced ?? false;\n\n await ensureTargetDoesNotExist(options.targetDir);\n\n const srcDir = path.join(options.targetDir, \"src\");\n const testDir = path.join(options.targetDir, \"test\");\n\n await mkdir(srcDir, { recursive: true });\n await mkdir(testDir, { recursive: true });\n\n await writeFile(\n path.join(options.targetDir, \"package.json\"),\n makePackageJson(options.projectName, options.language, sdkVersion),\n );\n await writeFile(\n path.join(options.targetDir, \".prettierrc.json\"),\n prettierConfig,\n );\n await writeFile(\n path.join(options.targetDir, \".prettierignore\"),\n prettierIgnore,\n );\n await writeFile(\n path.join(options.targetDir, \"README.md\"),\n makeReadme(options.projectName, options.preset, capabilities, advanced),\n );\n\n if (options.language === \"ts\") {\n await writeFile(path.join(options.targetDir, \"tsconfig.json\"), tsConfig);\n await writeFile(\n path.join(srcDir, \"plugin.ts\"),\n makePluginFile(\n options.projectName,\n options.preset,\n capabilities,\n advanced,\n ),\n );\n await writeFile(path.join(srcDir, \"server.ts\"), makeServerFile(\"ts\"));\n await writeFile(path.join(testDir, \"plugin.test.ts\"), makeVitestFile(\"ts\"));\n } else {\n await writeFile(\n path.join(srcDir, \"plugin.js\"),\n makePluginFile(\n options.projectName,\n options.preset,\n capabilities,\n advanced,\n ),\n );\n await writeFile(path.join(srcDir, \"server.js\"), makeServerFile(\"js\"));\n await writeFile(path.join(testDir, \"plugin.test.js\"), makeVitestFile(\"js\"));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,oBAAiB;AACjB,uBAA8C;AAC9C,qBAAoB;;;ACFpB;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,MAAQ;AAAA,EACR,KAAO;AAAA,IACL,2BAA2B;AAAA,EAC7B;AAAA,EACA,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,OAAS;AAAA,EACT,SAAW;AAAA,IACT,KAAK;AAAA,MACH,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,OAAS;AAAA,IACP;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,QAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,MAAQ;AAAA,IACR,WAAa;AAAA,IACb,OAAS;AAAA,EACX;AAAA,EACA,cAAgB;AAAA,IACd,WAAa;AAAA,IACb,SAAW;AAAA,EACb;AAAA,EACA,iBAAmB;AAAA,IACjB,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,UAAY;AAAA,IACZ,MAAQ;AAAA,IACR,YAAc;AAAA,IACd,QAAU;AAAA,EACZ;AAAA,EACA,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AACF;;;AC7CA,sBAAyC;AACzC,uBAAiB;AAEV,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,iBAAyB;AAC/B,IAAM,sBAAsB;AAYnC,SAAS,mBAAmB,QAAoC;AAC9D,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACzC,SAAO,aAAa,OAAO,CAAC,eAAe,OAAO,SAAS,UAAU,CAAC;AACxE;AAEA,eAAe,yBAAyB,WAAkC;AACxE,MAAI;AACF,cAAM,wBAAO,SAAS;AACtB,UAAM,IAAI,MAAM,oCAAoC,SAAS,EAAE;AAAA,EACjE,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,gBACP,MACA,UACA,YACQ;AACR,QAAM,UACJ,aAAa,OACT;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb,IACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAEN,QAAM,oBAAoB;AAAA,IACxB,GAAG;AAAA,IACH,OACE,aAAa,OACT,2EACA;AAAA,EACR;AAEA,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,MACZ,yBAAyB;AAAA,IAC3B;AAAA,IACA,iBACE,aAAa,OACT;AAAA,MACE,eAAe;AAAA,MACf,UAAU;AAAA,MACV,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV,IACA;AAAA,MACE,UAAU;AAAA,MACV,QAAQ;AAAA,IACV;AAAA,EACR;AAEA,SAAO,GAAG,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;AAAA;AAChD;AAEA,SAAS,cAAc,YAAwB,UAA2B;AACxE,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA,gBAKH,WACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAiBA,MACN;AAAA;AAAA;AAAA,IAGJ,KAAK;AACH,aAAO;AAAA;AAAA,6BAEgB,WAAW,wDAAwD,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxG,WACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBA,EACN;AAAA;AAAA;AAAA,IAGI,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,iBAAiB,QAAwB;AAChD,MAAI,WAAW,aAAa;AAC1B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBT;AAEA,SAAS,eACP,MACA,QACA,cACA,UACQ;AACR,QAAM,YAAY,aACf,IAAI,CAAC,eAAe,cAAc,YAAY,QAAQ,CAAC,EACvD,KAAK,QAAQ;AAChB,QAAM,UAAU,iBAAiB,MAAM;AACvC,QAAM,aACJ,QAAQ,SAAS,IAAI,2BAA2B;AAClD,QAAM,eAAe,QAAQ,SAAS,IAAI,GAAG,OAAO,KAAK;AAEzD,SAAO,YAAY,UAAU;AAAA;AAAA;AAAA;AAAA,uBAIR,IAAI;AAAA,aACd,IAAI;AAAA;AAAA;AAAA;AAAA,EAIf,YAAY;AAAA,MACR,SAAS;AAAA;AAAA;AAAA;AAIf;AAEA,SAAS,eAAe,UAA4B;AAClD,QAAM,eAAe,aAAa,OAAO,aAAa;AACtD,SAAO;AAAA,0BACiB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAetC;AAEA,SAAS,eAAe,UAA4B;AAClD,QAAM,eAAe,aAAa,OAAO,kBAAkB;AAC3D,SAAO;AAAA;AAAA,0BAEiB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AActC;AAEA,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAejB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOvB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAKvB,SAAS,WACP,aACA,QACA,cACA,UACQ;AACR,QAAM,mBAAmB,aACtB,IAAI,CAAC,eAAe,KAAK,UAAU,EAAE,EACrC,KAAK,IAAI;AAEZ,QAAM,wBAAwB,CAAC,eAAmC;AAChE,YAAQ,YAAY;AAAA,MAClB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO,IAAI,UAAU;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA,GAAG,aAAa;AAAA,MACd,CAAC,eAAe,SAAS,sBAAsB,UAAU,CAAC;AAAA,IAC5D;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,SAAO,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA,YAIb,MAAM;AAAA,uBACK,WAAW,YAAY,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYtD,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUhB,aAAa;AAAA;AAEf;AAEA,eAAsB,gBAAgB,SAAyC;AAC7E,QAAM,eAAe,mBAAmB;AAAA,IACtC,QAAQ;AAAA,IACR,GAAI,QAAQ,qBAAqB,CAAC;AAAA,EACpC,CAAC;AACD,QAAM,cACH,QAAQ,cAAc,qBAAqB,KAAK,KAAK;AACxD,QAAM,WAAW,QAAQ,YAAY;AAErC,QAAM,yBAAyB,QAAQ,SAAS;AAEhD,QAAM,SAAS,iBAAAC,QAAK,KAAK,QAAQ,WAAW,KAAK;AACjD,QAAM,UAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,MAAM;AAEnD,YAAM,uBAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,YAAM,uBAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAExC,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,cAAc;AAAA,IAC3C,gBAAgB,QAAQ,aAAa,QAAQ,UAAU,UAAU;AAAA,EACnE;AACA,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,kBAAkB;AAAA,IAC/C;AAAA,EACF;AACA,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,iBAAiB;AAAA,IAC9C;AAAA,EACF;AACA,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,WAAW;AAAA,IACxC,WAAW,QAAQ,aAAa,QAAQ,QAAQ,cAAc,QAAQ;AAAA,EACxE;AAEA,MAAI,QAAQ,aAAa,MAAM;AAC7B,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,eAAe,GAAG,QAAQ;AACvE,cAAM;AAAA,MACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,GAAG,eAAe,IAAI,CAAC;AACpE,cAAM,2BAAU,iBAAAA,QAAK,KAAK,SAAS,gBAAgB,GAAG,eAAe,IAAI,CAAC;AAAA,EAC5E,OAAO;AACL,cAAM;AAAA,MACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,GAAG,eAAe,IAAI,CAAC;AACpE,cAAM,2BAAU,iBAAAA,QAAK,KAAK,SAAS,gBAAgB,GAAG,eAAe,IAAI,CAAC;AAAA,EAC5E;AACF;;;AFvdA,SAAS,oBAA4B;AACnC,SAAO,aAAa,KAAK,IAAI;AAC/B;AAEA,SAAS,YAAY,OAAuB;AAC1C,MAAI,aAAa,SAAS,KAAmB,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR,mBAAmB,KAAK,kBAAkB,kBAAkB,CAAC;AAAA,EAC/D;AACF;AAEA,SAAS,sBAAsB,OAA6B;AAC1D,QAAM,SAAS,MACZ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAErC,aAAW,cAAc,QAAQ;AAC/B,QAAI,CAAC,aAAa,SAAS,UAAU,GAAG;AACtC,YAAM,IAAI;AAAA,QACR,uBAAuB,UAAU,kBAAkB,kBAAkB,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACnC;AAEA,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QACG,KAAK,yBAAyB,EAC9B,QAAQ,gBAAY,SAAS,iBAAiB,iCAAiC,EAC/E,YAAY,8DAA8D,EAC1E,mBAAmB,EACnB,SAAS,eAAe,kBAAkB,EAC1C,OAAO,QAAQ,yBAAyB,EACxC,OAAO,QAAQ,yBAAyB,EACxC;AAAA,EACC;AAAA,EACA,kBAAkB,kBAAkB,CAAC;AAAA,EACrC;AACF,EACC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,cAAc,uCAAuC,EAC5D;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,SAAS,+BAA+B,EAC/C;AAAA,EACC,OACE,cACA,YASG;AACH,QAAI,QAAQ,MAAM,QAAQ,IAAI;AAC5B,YAAM,IAAI,sCAAqB,uCAAuC;AAAA,IACxE;AAEA,UAAM,iBAAiB;AAAA,MACrB,WAAW,gBAAgB;AAAA,MAC3B,UAAU,QAAQ,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,MAClD,QAAQ,QAAQ,UAAU;AAAA,MAC1B,mBAAmB,QAAQ,gBAAgB,CAAC;AAAA,MAC5C,UAAU,QAAQ,YAAY;AAAA,MAC9B,YAAY,QAAQ,cAAc;AAAA,IACpC;AAEA,UAAM,eAAe,CAAC,QAAQ;AAE9B,QAAI,YAAY,eAAe;AAC/B,QAAI,WAAW,eAAe;AAC9B,QAAI,SAAS,eAAe;AAC5B,QAAI,oBAAoB,eAAe;AACvC,QAAI,WAAW,eAAe;AAC9B,QAAI,aAAa,eAAe;AAEhC,QAAI,cAAc;AAChB,YAAM,UAAU,UAAM,eAAAC;AAAA,QACpB;AAAA,UACE;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,cACP,EAAE,OAAO,cAAc,OAAO,KAAK;AAAA,cACnC,EAAE,OAAO,cAAc,OAAO,KAAK;AAAA,YACrC;AAAA,YACA,SAAS,aAAa,OAAO,IAAI;AAAA,UACnC;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS,aAAa,IAAI,CAAC,gBAAgB;AAAA,cACzC,OAAO;AAAA,cACP,OAAO;AAAA,YACT,EAAE;AAAA,YACF,SAAS,aAAa,QAAQ,MAAM;AAAA,UACtC;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS,aAAa,IAAI,CAAC,gBAAgB;AAAA,cACzC,OAAO;AAAA,cACP,OAAO;AAAA,YACT,EAAE;AAAA,YACF,cAAc;AAAA,YACd,KAAK;AAAA,YACL,MAAM;AAAA,UACR;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,QACF;AAAA,QACA;AAAA,UACE,UAAU,MAAM;AACd,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAEA,kBAAY,QAAQ;AACpB,iBAAW,QAAQ;AACnB,eAAU,QAAQ,UAAU,eAAe;AAC3C,0BAAqB,QAAQ,qBAAqB,CAAC;AACnD,iBAAW,QAAQ,QAAQ,YAAY,eAAe,QAAQ;AAC9D,mBAAa,QAAQ,cAAc,eAAe;AAAA,IACpD;AAEA,UAAM,YAAY,kBAAAC,QAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AACvD,UAAM,cAAc,kBAAAA,QAAK,SAAS,SAAS;AAE3C,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,WAAW,WAAW,OAAO,SAAS,EAAE;AACpD,YAAQ,IAAI,aAAa;AACzB,YAAQ,IAAI,QAAQ,SAAS,EAAE;AAC/B,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,eAAe;AAAA,EAC7B;AACF;AAEF,KAAK,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,UAAmB;AAC9D,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAQ,MAAM,UAAU,OAAO,EAAE;AACjC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["import_node_path","path","prompts","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../package.json","../src/scaffold.ts"],"sourcesContent":["import path from \"node:path\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport prompts from \"prompts\";\nimport packageJSON from \"../package.json\";\nimport {\n CAPABILITIES,\n DEFAULT_PRESET,\n DEFAULT_SDK_VERSION,\n scaffoldProject,\n type Capability,\n type Language,\n type Preset,\n} from \"./scaffold\";\n\nfunction capabilitiesLabel(): string {\n return CAPABILITIES.join(\", \");\n}\n\nfunction parsePreset(input: string): Preset {\n if (CAPABILITIES.includes(input as Capability)) {\n return input as Preset;\n }\n throw new InvalidArgumentError(\n `Invalid preset '${input}'. Use one of: ${capabilitiesLabel()}`,\n );\n}\n\nfunction parseCapabilitiesList(input: string): Capability[] {\n const parsed = input\n .split(\",\")\n .map((value) => value.trim())\n .filter((value) => value.length > 0) as Capability[];\n\n for (const capability of parsed) {\n if (!CAPABILITIES.includes(capability)) {\n throw new InvalidArgumentError(\n `Invalid capability '${capability}'. Use one of: ${capabilitiesLabel()}`,\n );\n }\n }\n\n return Array.from(new Set(parsed));\n}\n\nconst program = new Command();\n\nprogram\n .name(\"create-streamfox-plugin\")\n .version(packageJSON.version, \"-v, --version\", \"display the current CLI version\")\n .description(\"Scaffold StreamFox plugin projects with modern JS/TS presets\")\n .showHelpAfterError()\n .argument(\"[directory]\", \"output directory\")\n .option(\"--ts\", \"use TypeScript template\")\n .option(\"--js\", \"use JavaScript template\")\n .option(\n \"--preset <preset>\",\n `plugin preset: ${capabilitiesLabel()}`,\n parsePreset,\n )\n .option(\n \"--capabilities <capabilities>\",\n \"extra capabilities as comma-separated list\",\n parseCapabilitiesList,\n )\n .option(\"--advanced\", \"generate advanced capability examples\")\n .option(\n \"--sdk-version <range>\",\n \"@streamfox/plugin-sdk version/range\",\n DEFAULT_SDK_VERSION,\n )\n .option(\"--yes\", \"skip prompts and use defaults\")\n .action(\n async (\n directoryArg: string | undefined,\n options: {\n ts?: boolean;\n js?: boolean;\n yes?: boolean;\n preset?: Preset;\n capabilities?: Capability[];\n advanced?: boolean;\n sdkVersion?: string;\n },\n ) => {\n if (options.ts && options.js) {\n throw new InvalidArgumentError(\"Choose either --ts or --js, not both.\");\n }\n\n const promptDefaults = {\n directory: directoryArg ?? \"my-media-plugin\",\n language: options.ts ? \"ts\" : options.js ? \"js\" : \"ts\",\n preset: options.preset ?? DEFAULT_PRESET,\n capabilities: Array.from(\n new Set([\n options.preset ?? DEFAULT_PRESET,\n ...(options.capabilities ?? []),\n ]),\n ) as Capability[],\n advanced: options.advanced ?? false,\n sdkVersion: options.sdkVersion ?? DEFAULT_SDK_VERSION,\n };\n\n const shouldPrompt = !options.yes;\n\n let directory = promptDefaults.directory;\n let language = promptDefaults.language as Language;\n let preset = promptDefaults.preset;\n let capabilities = promptDefaults.capabilities;\n let advanced = promptDefaults.advanced;\n let sdkVersion = promptDefaults.sdkVersion;\n\n if (shouldPrompt) {\n const answers = await prompts(\n [\n {\n type: \"text\",\n name: \"directory\",\n message: \"Project directory\",\n initial: directory,\n },\n {\n type: \"select\",\n name: \"language\",\n message: \"Template language\",\n choices: [\n { title: \"TypeScript\", value: \"ts\" },\n { title: \"JavaScript\", value: \"js\" },\n ],\n initial: language === \"ts\" ? 0 : 1,\n },\n {\n type: \"multiselect\",\n name: \"capabilities\",\n message: \"Plugin capabilities\",\n choices: CAPABILITIES.map((capability) => ({\n title: capability,\n value: capability,\n selected: capabilities.includes(capability),\n })),\n instructions: false,\n min: 1,\n hint: \"Space to select\",\n },\n {\n type: \"confirm\",\n name: \"advanced\",\n message: \"Generate advanced capability examples?\",\n initial: advanced,\n },\n {\n type: \"text\",\n name: \"sdkVersion\",\n message: \"SDK dependency version/range\",\n initial: sdkVersion,\n },\n ],\n {\n onCancel: () => {\n process.exit(1);\n },\n },\n );\n\n directory = answers.directory;\n language = answers.language;\n capabilities = (answers.capabilities ??\n promptDefaults.capabilities) as Capability[];\n preset =\n capabilities[0] ?? promptDefaults.preset;\n advanced = Boolean(answers.advanced ?? promptDefaults.advanced);\n sdkVersion = answers.sdkVersion ?? promptDefaults.sdkVersion;\n }\n\n const targetDir = path.resolve(process.cwd(), directory);\n const projectName = path.basename(targetDir);\n\n await scaffoldProject({\n targetDir,\n projectName,\n language,\n capabilities,\n preset,\n advanced,\n sdkVersion,\n });\n\n console.log(`Created ${projectName} at ${targetDir}`);\n console.log(\"Next steps:\");\n console.log(` cd ${directory}`);\n console.log(\" npm install\");\n console.log(\" npm run dev\");\n },\n );\n\nvoid program.parseAsync(process.argv).catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`Error: ${message}`);\n process.exit(1);\n});\n","{\n \"name\": \"@streamfox/create-streamfox-plugin\",\n \"version\": \"0.3.1\",\n \"description\": \"Standalone CLI to scaffold StreamFox plugin projects\",\n \"type\": \"module\",\n \"bin\": {\n \"create-streamfox-plugin\": \"dist/cli.cjs\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"test\": \"vitest run\",\n \"typecheck\": \"tsc --noEmit\",\n \"check\": \"npm run format:check && npm run typecheck && npm test && npm run build\"\n },\n \"dependencies\": {\n \"commander\": \"^12.1.0\",\n \"prompts\": \"^2.4.2\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.6.0\",\n \"@types/prompts\": \"^2.4.9\",\n \"prettier\": \"^3.6.2\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.2\",\n \"vitest\": \"^2.1.9\"\n },\n \"engines\": {\n \"node\": \">=20\"\n }\n}\n","import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const CAPABILITIES = [\n \"catalog\",\n \"meta\",\n \"stream\",\n \"subtitles\",\n \"plugin_catalog\",\n] as const;\nexport type Capability = (typeof CAPABILITIES)[number];\nexport type Preset = Capability;\nexport type Language = \"ts\" | \"js\";\nexport const DEFAULT_PRESET: Preset = \"meta\";\nexport const DEFAULT_SDK_VERSION = \"^0.2.0\";\n\nexport interface ScaffoldOptions {\n targetDir: string;\n projectName: string;\n language: Language;\n preset?: Preset;\n capabilities?: Capability[];\n advanced?: boolean;\n extraCapabilities?: Capability[];\n sdkVersion?: string;\n}\n\nfunction sortedCapabilities(values: Capability[]): Capability[] {\n const unique = Array.from(new Set(values));\n return CAPABILITIES.filter((capability) => unique.includes(capability));\n}\n\nasync function ensureTargetDoesNotExist(targetDir: string): Promise<void> {\n try {\n await access(targetDir);\n throw new Error(`Target directory already exists: ${targetDir}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return;\n }\n throw error;\n }\n}\n\nfunction makePackageJson(\n name: string,\n language: Language,\n sdkVersion: string,\n): string {\n const scripts =\n language === \"ts\"\n ? {\n dev: \"tsx watch src/server.ts\",\n build: \"tsc -p tsconfig.json\",\n start: \"node dist/server.js\",\n test: \"vitest run\",\n typecheck: \"tsc --noEmit\",\n }\n : {\n dev: \"node --watch src/server.js\",\n build: 'echo \"No build step for JavaScript template\"',\n start: \"node src/server.js\",\n test: \"vitest run\",\n };\n\n const normalizedScripts = {\n ...scripts,\n check:\n language === \"ts\"\n ? \"npm run typecheck && npm test && npm run build\"\n : \"npm test && npm run build\",\n };\n\n const packageJson = {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: normalizedScripts,\n dependencies: {\n \"@streamfox/plugin-sdk\": sdkVersion,\n },\n devDependencies:\n language === \"ts\"\n ? {\n \"@types/node\": \"^24.6.0\",\n tsx: \"^4.20.5\",\n typescript: \"^5.9.2\",\n vitest: \"^2.1.9\",\n }\n : {\n vitest: \"^2.1.9\",\n },\n };\n\n return `${JSON.stringify(packageJson, null, 2)}\\n`;\n}\n\nfunction resourceBlock(capability: Capability, advanced: boolean): string {\n switch (capability) {\n case \"catalog\":\n return `catalog: {\n endpoints: [\n {\n id: \"top\",\n name: \"Top\",\n mediaTypes: [\"movie\"],\n filters: [{ key: \"genre\", valueType: \"string\" }],\n },\n ],\n handler: async () => ({\n items: [],\n }),\n },`;\n case \"meta\":\n return `meta: {\n mediaTypes: [\"movie\"],\n includes: [\"videos\", \"links\"],\n handler: async () => ({\n item: ${\n advanced\n ? `{\n summary: {\n id: { namespace: \"imdb\", value: \"tt1254207\" },\n mediaType: \"movie\",\n title: \"Big Buck Bunny\",\n links: [],\n },\n defaultVideoID: \"main\",\n trailers: [{ transport: { kind: \"youtube\", id: \"aqz-KE-bpKQ\" } }],\n videos: [\n {\n id: \"main\",\n title: \"Main\",\n streams: [{ transport: { kind: \"http\", url: \"https://example.com/video.mp4\" } }],\n },\n ],\n }`\n : \"null\"\n },\n }),\n },`;\n case \"stream\":\n return `stream: {\n mediaTypes: [\"movie\"],\n supportedTransports: ${advanced ? `[\"http\", \"torrent\", \"usenet\", \"archive\", \"youtube\"]` : `[\"http\"]`},\n handler: async () => ({\n streams: [\n {\n transport: { kind: \"http\", url: \"https://example.com/video.mp4\", mode: \"stream\" },\n hints: {\n notWebReady: true,\n proxyHeaders: { request: { \"User-Agent\": \"StreamFox\" } },\n },\n },\n${\n advanced\n ? ` {\n transport: { kind: \"torrent\", infoHash: \"abcdef\", peerDiscovery: [\"tracker:udp://tracker.example.com:80\"] },\n selection: { fileIndex: 0 },\n },\n {\n transport: { kind: \"usenet\", nzbURL: \"https://example.com/file.nzb\", servers: [\"nntps://user:pass@news.example.com:563/4\"] },\n },\n {\n transport: {\n kind: \"archive\",\n format: \"zip\",\n files: [{ url: \"https://example.com/archive.zip\", bytes: 1024 }],\n },\n selection: { fileMustInclude: \"movie.mkv\" },\n },\n`\n : \"\"\n} ],\n }),\n },`;\n case \"subtitles\":\n return `subtitles: {\n mediaTypes: [\"movie\", \"episode\"],\n defaultLanguages: [\"en\"],\n handler: async (request, { settings }) => {\n const configuredLanguages = Array.isArray(settings.languages)\n ? settings.languages\n : [];\n const languagePreferences =\n configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);\n\n void languagePreferences;\n void settings.includeHI;\n\n return {\n subtitles: [],\n };\n },\n },`;\n case \"plugin_catalog\":\n return `pluginCatalog: {\n endpoints: [\n {\n id: \"featured\",\n name: \"Featured\",\n pluginKinds: [\"catalog\", \"meta\", \"stream\", \"subtitles\"],\n tags: [\"official\"],\n },\n ],\n handler: async () => ({\n plugins: [\n {\n id: \"com.example.recommended\",\n name: \"Recommended\",\n version: \"1.0.0\",\n pluginKinds: [\"catalog\", \"meta\"],\n distribution: {\n transport: \"https\",\n manifestURL: \"https://plugins.example.com/recommended/manifest\",\n },\n manifestSnapshot: {\n plugin: { id: \"com.example.recommended\" },\n },\n },\n ],\n }),\n },`;\n default:\n return \"\";\n }\n}\n\nfunction makeInstallBlock(capabilities: Capability[]): string {\n if (!capabilities.includes(\"subtitles\")) {\n return \"\";\n }\n\n return ` install: {\n configurationRequired: true,\n title: \"Subtitle Settings\",\n description: \"Configure subtitle defaults before installing this plugin.\",\n fields: [\n settings.multiSelect(\"languages\", {\n label: \"Languages\",\n options: [\n { label: \"English\", value: \"en\" },\n { label: \"Greek\", value: \"el\" },\n { label: \"Spanish\", value: \"es\" },\n ],\n defaultValue: [\"en\"],\n }),\n settings.checkbox(\"includeHI\", {\n label: \"Include hearing impaired\",\n defaultValue: true,\n }),\n ],\n },\n`;\n}\n\nfunction makePluginFile(\n name: string,\n capabilities: Capability[],\n advanced: boolean,\n): string {\n const resources = capabilities\n .map((capability) => resourceBlock(capability, advanced))\n .join(\"\\n \");\n const install = makeInstallBlock(capabilities);\n const importSpec =\n install.length > 0 ? \"definePlugin, settings\" : \"definePlugin\";\n const installBlock = install.length > 0 ? `${install}` : \"\";\n\n return `import { ${importSpec} } from \"@streamfox/plugin-sdk\";\n\nexport const plugin = definePlugin({\n plugin: {\n id: \"com.example.${name}\",\n name: \"${name}\",\n version: \"0.1.0\",\n description: \"Generated StreamFox plugin scaffold\",\n },\n${installBlock} resources: {\n ${resources}\n },\n});\n`;\n}\n\nfunction makeServerFile(language: Language): string {\n const pluginImport = language === \"ts\" ? \"./plugin\" : \"./plugin.js\";\n return `import { serve } from \"@streamfox/plugin-sdk\";\nimport { plugin } from \"${pluginImport}\";\n\nconst { url, installURL, launchURL } = await serve(plugin, {\n port: Number(process.env.PORT ?? 7000),\n integration: {\n installScheme: \"streamfox\",\n launchBaseURL: \"https://streamfox.app/#\",\n autoOpen: \"none\",\n },\n});\n\nconsole.log(\"Plugin manifest:\", url);\nconsole.log(\"Plugin installer deeplink:\", installURL);\nconsole.log(\"Plugin launch URL:\", launchURL);\n`;\n}\n\nfunction makeVitestFile(language: Language): string {\n const pluginImport = language === \"ts\" ? \"../src/plugin\" : \"../src/plugin.js\";\n return `import { describe, expect, it } from \"vitest\";\nimport { createServer } from \"@streamfox/plugin-sdk\";\nimport { plugin } from \"${pluginImport}\";\n\ndescribe(\"scaffold smoke\", () => {\n it(\"serves manifest and studio config\", async () => {\n const app = createServer(plugin, { frontend: false });\n\n const manifestResponse = await app.request(\"/manifest\");\n expect(manifestResponse.status).toBe(200);\n\n const studioResponse = await app.request(\"/studio-config\");\n expect(studioResponse.status).toBe(200);\n });\n});\n`;\n}\n\nconst tsConfig = `{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Bundler\",\n \"strict\": true,\n \"declaration\": true,\n \"outDir\": \"dist\",\n \"rootDir\": \"src\",\n \"types\": [\"node\"]\n },\n \"include\": [\"src\"]\n}\n`;\n\nconst gitIgnore = `dist\nnode_modules\n.DS_Store\n.env\n.env.*\ncoverage\n`;\n\nfunction makeReadme(\n projectName: string,\n capabilities: Capability[],\n advanced: boolean,\n): string {\n const capabilitiesList = capabilities\n .map((capability) => `- ${capability}`)\n .join(\"\\n\");\n\n const endpointForCapability = (capability: Capability): string => {\n switch (capability) {\n case \"catalog\":\n return \"/catalog/:mediaType/:catalogID\";\n case \"meta\":\n return \"/meta/:mediaType/:itemID\";\n case \"stream\":\n return \"/stream/:mediaType/:itemID\";\n case \"subtitles\":\n return \"/subtitles/:mediaType/:itemID\";\n case \"plugin_catalog\":\n return \"/plugin_catalog/:catalogID/:pluginKind\";\n default:\n return `/${capability}`;\n }\n };\n\n const endpointLines = [\n \"- GET /manifest\",\n \"- GET /studio-config\",\n ...capabilities.map(\n (capability) => `- GET ${endpointForCapability(capability)}`,\n ),\n ].join(\"\\n\");\n\n return `# ${projectName}\n\nGenerated with create-streamfox-plugin.\n\nCapabilities: \\`${capabilities.join(\", \")}\\`\nAdvanced template: \\`${advanced ? \"enabled\" : \"disabled\"}\\`\n\n## Scripts\n\n- npm run dev\n- npm run build\n- npm run start\n- npm run test\n- npm run check\n\n## Implemented Capabilities\n\n${capabilitiesList}\n\n## Stream Model\n\n- Unified transport model via \\`stream.transport\\`\n- Capability declaration via \\`resources.stream.supportedTransports\\`\n- Optional selection controls via \\`stream.selection\\`\n\n## Endpoints\n\n${endpointLines}\n`;\n}\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<void> {\n const capabilities = options.capabilities\n ? sortedCapabilities(options.capabilities)\n : sortedCapabilities([\n options.preset ?? DEFAULT_PRESET,\n ...(options.extraCapabilities ?? []),\n ]);\n const sdkVersion =\n (options.sdkVersion ?? DEFAULT_SDK_VERSION).trim() || DEFAULT_SDK_VERSION;\n const advanced = options.advanced ?? false;\n\n await ensureTargetDoesNotExist(options.targetDir);\n\n const srcDir = path.join(options.targetDir, \"src\");\n const testDir = path.join(options.targetDir, \"test\");\n\n await mkdir(srcDir, { recursive: true });\n await mkdir(testDir, { recursive: true });\n\n await writeFile(\n path.join(options.targetDir, \"package.json\"),\n makePackageJson(options.projectName, options.language, sdkVersion),\n );\n await writeFile(path.join(options.targetDir, \".gitignore\"), gitIgnore);\n await writeFile(\n path.join(options.targetDir, \"README.md\"),\n makeReadme(options.projectName, capabilities, advanced),\n );\n\n if (options.language === \"ts\") {\n await writeFile(path.join(options.targetDir, \"tsconfig.json\"), tsConfig);\n await writeFile(\n path.join(srcDir, \"plugin.ts\"),\n makePluginFile(\n options.projectName,\n capabilities,\n advanced,\n ),\n );\n await writeFile(path.join(srcDir, \"server.ts\"), makeServerFile(\"ts\"));\n await writeFile(path.join(testDir, \"plugin.test.ts\"), makeVitestFile(\"ts\"));\n } else {\n await writeFile(\n path.join(srcDir, \"plugin.js\"),\n makePluginFile(\n options.projectName,\n capabilities,\n advanced,\n ),\n );\n await writeFile(path.join(srcDir, \"server.js\"), makeServerFile(\"js\"));\n await writeFile(path.join(testDir, \"plugin.test.js\"), makeVitestFile(\"js\"));\n }\n}\n"],"mappings":"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","names":["import_node_path","path","prompts","path"]}
|
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import prompts from "prompts";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@streamfox/create-streamfox-plugin",
|
|
11
|
-
version: "0.3.
|
|
11
|
+
version: "0.3.1",
|
|
12
12
|
description: "Standalone CLI to scaffold StreamFox plugin projects",
|
|
13
13
|
type: "module",
|
|
14
14
|
bin: {
|
|
@@ -84,22 +84,18 @@ function makePackageJson(name, language, sdkVersion) {
|
|
|
84
84
|
const scripts = language === "ts" ? {
|
|
85
85
|
dev: "tsx watch src/server.ts",
|
|
86
86
|
build: "tsc -p tsconfig.json",
|
|
87
|
-
format: "prettier --write .",
|
|
88
|
-
"format:check": "prettier --check .",
|
|
89
87
|
start: "node dist/server.js",
|
|
90
88
|
test: "vitest run",
|
|
91
89
|
typecheck: "tsc --noEmit"
|
|
92
90
|
} : {
|
|
93
91
|
dev: "node --watch src/server.js",
|
|
94
92
|
build: 'echo "No build step for JavaScript template"',
|
|
95
|
-
format: "prettier --write .",
|
|
96
|
-
"format:check": "prettier --check .",
|
|
97
93
|
start: "node src/server.js",
|
|
98
94
|
test: "vitest run"
|
|
99
95
|
};
|
|
100
96
|
const normalizedScripts = {
|
|
101
97
|
...scripts,
|
|
102
|
-
check: language === "ts" ? "npm run
|
|
98
|
+
check: language === "ts" ? "npm run typecheck && npm test && npm run build" : "npm test && npm run build"
|
|
103
99
|
};
|
|
104
100
|
const packageJson = {
|
|
105
101
|
name,
|
|
@@ -112,12 +108,10 @@ function makePackageJson(name, language, sdkVersion) {
|
|
|
112
108
|
},
|
|
113
109
|
devDependencies: language === "ts" ? {
|
|
114
110
|
"@types/node": "^24.6.0",
|
|
115
|
-
prettier: "^3.6.2",
|
|
116
111
|
tsx: "^4.20.5",
|
|
117
112
|
typescript: "^5.9.2",
|
|
118
113
|
vitest: "^2.1.9"
|
|
119
114
|
} : {
|
|
120
|
-
prettier: "^3.6.2",
|
|
121
115
|
vitest: "^2.1.9"
|
|
122
116
|
}
|
|
123
117
|
};
|
|
@@ -246,8 +240,8 @@ ${advanced ? ` {
|
|
|
246
240
|
return "";
|
|
247
241
|
}
|
|
248
242
|
}
|
|
249
|
-
function makeInstallBlock(
|
|
250
|
-
if (
|
|
243
|
+
function makeInstallBlock(capabilities) {
|
|
244
|
+
if (!capabilities.includes("subtitles")) {
|
|
251
245
|
return "";
|
|
252
246
|
}
|
|
253
247
|
return ` install: {
|
|
@@ -272,9 +266,9 @@ function makeInstallBlock(preset) {
|
|
|
272
266
|
},
|
|
273
267
|
`;
|
|
274
268
|
}
|
|
275
|
-
function makePluginFile(name,
|
|
269
|
+
function makePluginFile(name, capabilities, advanced) {
|
|
276
270
|
const resources = capabilities.map((capability) => resourceBlock(capability, advanced)).join("\n ");
|
|
277
|
-
const install = makeInstallBlock(
|
|
271
|
+
const install = makeInstallBlock(capabilities);
|
|
278
272
|
const importSpec = install.length > 0 ? "definePlugin, settings" : "definePlugin";
|
|
279
273
|
const installBlock = install.length > 0 ? `${install}` : "";
|
|
280
274
|
return `import { ${importSpec} } from "@streamfox/plugin-sdk";
|
|
@@ -344,17 +338,14 @@ var tsConfig = `{
|
|
|
344
338
|
"include": ["src"]
|
|
345
339
|
}
|
|
346
340
|
`;
|
|
347
|
-
var
|
|
348
|
-
"semi": true,
|
|
349
|
-
"singleQuote": false,
|
|
350
|
-
"trailingComma": "all"
|
|
351
|
-
}
|
|
352
|
-
`;
|
|
353
|
-
var prettierIgnore = `dist
|
|
341
|
+
var gitIgnore = `dist
|
|
354
342
|
node_modules
|
|
355
343
|
.DS_Store
|
|
344
|
+
.env
|
|
345
|
+
.env.*
|
|
346
|
+
coverage
|
|
356
347
|
`;
|
|
357
|
-
function makeReadme(projectName,
|
|
348
|
+
function makeReadme(projectName, capabilities, advanced) {
|
|
358
349
|
const capabilitiesList = capabilities.map((capability) => `- ${capability}`).join("\n");
|
|
359
350
|
const endpointForCapability = (capability) => {
|
|
360
351
|
switch (capability) {
|
|
@@ -383,16 +374,16 @@ function makeReadme(projectName, preset, capabilities, advanced) {
|
|
|
383
374
|
|
|
384
375
|
Generated with create-streamfox-plugin.
|
|
385
376
|
|
|
386
|
-
|
|
377
|
+
Capabilities: \`${capabilities.join(", ")}\`
|
|
387
378
|
Advanced template: \`${advanced ? "enabled" : "disabled"}\`
|
|
388
379
|
|
|
389
380
|
## Scripts
|
|
390
381
|
|
|
391
382
|
- npm run dev
|
|
392
383
|
- npm run build
|
|
393
|
-
- npm run format
|
|
394
384
|
- npm run start
|
|
395
385
|
- npm run test
|
|
386
|
+
- npm run check
|
|
396
387
|
|
|
397
388
|
## Implemented Capabilities
|
|
398
389
|
|
|
@@ -410,8 +401,8 @@ ${endpointLines}
|
|
|
410
401
|
`;
|
|
411
402
|
}
|
|
412
403
|
async function scaffoldProject(options) {
|
|
413
|
-
const capabilities = sortedCapabilities([
|
|
414
|
-
options.preset,
|
|
404
|
+
const capabilities = options.capabilities ? sortedCapabilities(options.capabilities) : sortedCapabilities([
|
|
405
|
+
options.preset ?? DEFAULT_PRESET,
|
|
415
406
|
...options.extraCapabilities ?? []
|
|
416
407
|
]);
|
|
417
408
|
const sdkVersion = (options.sdkVersion ?? DEFAULT_SDK_VERSION).trim() || DEFAULT_SDK_VERSION;
|
|
@@ -425,17 +416,10 @@ async function scaffoldProject(options) {
|
|
|
425
416
|
path.join(options.targetDir, "package.json"),
|
|
426
417
|
makePackageJson(options.projectName, options.language, sdkVersion)
|
|
427
418
|
);
|
|
428
|
-
await writeFile(
|
|
429
|
-
path.join(options.targetDir, ".prettierrc.json"),
|
|
430
|
-
prettierConfig
|
|
431
|
-
);
|
|
432
|
-
await writeFile(
|
|
433
|
-
path.join(options.targetDir, ".prettierignore"),
|
|
434
|
-
prettierIgnore
|
|
435
|
-
);
|
|
419
|
+
await writeFile(path.join(options.targetDir, ".gitignore"), gitIgnore);
|
|
436
420
|
await writeFile(
|
|
437
421
|
path.join(options.targetDir, "README.md"),
|
|
438
|
-
makeReadme(options.projectName,
|
|
422
|
+
makeReadme(options.projectName, capabilities, advanced)
|
|
439
423
|
);
|
|
440
424
|
if (options.language === "ts") {
|
|
441
425
|
await writeFile(path.join(options.targetDir, "tsconfig.json"), tsConfig);
|
|
@@ -443,7 +427,6 @@ async function scaffoldProject(options) {
|
|
|
443
427
|
path.join(srcDir, "plugin.ts"),
|
|
444
428
|
makePluginFile(
|
|
445
429
|
options.projectName,
|
|
446
|
-
options.preset,
|
|
447
430
|
capabilities,
|
|
448
431
|
advanced
|
|
449
432
|
)
|
|
@@ -455,7 +438,6 @@ async function scaffoldProject(options) {
|
|
|
455
438
|
path.join(srcDir, "plugin.js"),
|
|
456
439
|
makePluginFile(
|
|
457
440
|
options.projectName,
|
|
458
|
-
options.preset,
|
|
459
441
|
capabilities,
|
|
460
442
|
advanced
|
|
461
443
|
)
|
|
@@ -510,7 +492,12 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
510
492
|
directory: directoryArg ?? "my-media-plugin",
|
|
511
493
|
language: options.ts ? "ts" : options.js ? "js" : "ts",
|
|
512
494
|
preset: options.preset ?? DEFAULT_PRESET,
|
|
513
|
-
|
|
495
|
+
capabilities: Array.from(
|
|
496
|
+
/* @__PURE__ */ new Set([
|
|
497
|
+
options.preset ?? DEFAULT_PRESET,
|
|
498
|
+
...options.capabilities ?? []
|
|
499
|
+
])
|
|
500
|
+
),
|
|
514
501
|
advanced: options.advanced ?? false,
|
|
515
502
|
sdkVersion: options.sdkVersion ?? DEFAULT_SDK_VERSION
|
|
516
503
|
};
|
|
@@ -518,7 +505,7 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
518
505
|
let directory = promptDefaults.directory;
|
|
519
506
|
let language = promptDefaults.language;
|
|
520
507
|
let preset = promptDefaults.preset;
|
|
521
|
-
let
|
|
508
|
+
let capabilities = promptDefaults.capabilities;
|
|
522
509
|
let advanced = promptDefaults.advanced;
|
|
523
510
|
let sdkVersion = promptDefaults.sdkVersion;
|
|
524
511
|
if (shouldPrompt) {
|
|
@@ -540,26 +527,17 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
540
527
|
],
|
|
541
528
|
initial: language === "ts" ? 0 : 1
|
|
542
529
|
},
|
|
543
|
-
{
|
|
544
|
-
type: "select",
|
|
545
|
-
name: "preset",
|
|
546
|
-
message: "Plugin preset",
|
|
547
|
-
choices: CAPABILITIES.map((capability) => ({
|
|
548
|
-
title: capability,
|
|
549
|
-
value: capability
|
|
550
|
-
})),
|
|
551
|
-
initial: CAPABILITIES.indexOf(preset)
|
|
552
|
-
},
|
|
553
530
|
{
|
|
554
531
|
type: "multiselect",
|
|
555
|
-
name: "
|
|
556
|
-
message: "
|
|
532
|
+
name: "capabilities",
|
|
533
|
+
message: "Plugin capabilities",
|
|
557
534
|
choices: CAPABILITIES.map((capability) => ({
|
|
558
535
|
title: capability,
|
|
559
|
-
value: capability
|
|
536
|
+
value: capability,
|
|
537
|
+
selected: capabilities.includes(capability)
|
|
560
538
|
})),
|
|
561
539
|
instructions: false,
|
|
562
|
-
min:
|
|
540
|
+
min: 1,
|
|
563
541
|
hint: "Space to select"
|
|
564
542
|
},
|
|
565
543
|
{
|
|
@@ -583,8 +561,8 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
583
561
|
);
|
|
584
562
|
directory = answers.directory;
|
|
585
563
|
language = answers.language;
|
|
586
|
-
|
|
587
|
-
|
|
564
|
+
capabilities = answers.capabilities ?? promptDefaults.capabilities;
|
|
565
|
+
preset = capabilities[0] ?? promptDefaults.preset;
|
|
588
566
|
advanced = Boolean(answers.advanced ?? promptDefaults.advanced);
|
|
589
567
|
sdkVersion = answers.sdkVersion ?? promptDefaults.sdkVersion;
|
|
590
568
|
}
|
|
@@ -594,8 +572,8 @@ program.name("create-streamfox-plugin").version(package_default.version, "-v, --
|
|
|
594
572
|
targetDir,
|
|
595
573
|
projectName,
|
|
596
574
|
language,
|
|
575
|
+
capabilities,
|
|
597
576
|
preset,
|
|
598
|
-
extraCapabilities,
|
|
599
577
|
advanced,
|
|
600
578
|
sdkVersion
|
|
601
579
|
});
|