@streamfox/create-streamfox-plugin 0.3.2 → 0.3.3
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 +2 -2
- package/dist/cli.cjs +4 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +4 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ create-streamfox-plugin my-plugin --yes
|
|
|
50
50
|
| `--preset <preset>` | enum | `meta` | Legacy/compat primary template hint. Interactive mode now asks for capabilities directly. |
|
|
51
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
|
-
| `--sdk-version <range>` | string | `^0.2.
|
|
53
|
+
| `--sdk-version <range>` | string | `^0.2.1` | Dependency range for `@streamfox/plugin-sdk`. |
|
|
54
54
|
| `--yes` | flag | `false` | Skip prompts and use provided/default values. |
|
|
55
55
|
| `-v, --version` | flag | no | Display the current CLI version. |
|
|
56
56
|
|
|
@@ -63,7 +63,7 @@ create-streamfox-plugin streamfox-opensubs \
|
|
|
63
63
|
--ts \
|
|
64
64
|
--capabilities subtitles,meta,stream \
|
|
65
65
|
--advanced \
|
|
66
|
-
--sdk-version ^0.2.
|
|
66
|
+
--sdk-version ^0.2.1 \
|
|
67
67
|
--yes
|
|
68
68
|
```
|
|
69
69
|
|
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.3",
|
|
35
35
|
description: "Standalone CLI to scaffold StreamFox plugin projects",
|
|
36
36
|
type: "module",
|
|
37
37
|
bin: {
|
|
@@ -87,7 +87,7 @@ var CAPABILITIES = [
|
|
|
87
87
|
"plugin_catalog"
|
|
88
88
|
];
|
|
89
89
|
var DEFAULT_PRESET = "meta";
|
|
90
|
-
var DEFAULT_SDK_VERSION = "^0.2.
|
|
90
|
+
var DEFAULT_SDK_VERSION = "^0.2.1";
|
|
91
91
|
function sortedCapabilities(values) {
|
|
92
92
|
const unique = Array.from(new Set(values));
|
|
93
93
|
return CAPABILITIES.filter((capability) => unique.includes(capability));
|
|
@@ -217,14 +217,14 @@ ${advanced ? ` {
|
|
|
217
217
|
mediaTypes: ["movie", "episode"],
|
|
218
218
|
defaultLanguages: ["en"],
|
|
219
219
|
handler: async (request, { settings }) => {
|
|
220
|
-
const configuredLanguages = Array.isArray(settings
|
|
220
|
+
const configuredLanguages = Array.isArray(settings?.languages)
|
|
221
221
|
? settings.languages
|
|
222
222
|
: [];
|
|
223
223
|
const languagePreferences =
|
|
224
224
|
configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);
|
|
225
225
|
|
|
226
226
|
void languagePreferences;
|
|
227
|
-
void settings
|
|
227
|
+
void settings?.includeHI;
|
|
228
228
|
|
|
229
229
|
return {
|
|
230
230
|
subtitles: [],
|
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 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"]}
|
|
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.3\",\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.1\";\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.3",
|
|
12
12
|
description: "Standalone CLI to scaffold StreamFox plugin projects",
|
|
13
13
|
type: "module",
|
|
14
14
|
bin: {
|
|
@@ -64,7 +64,7 @@ var CAPABILITIES = [
|
|
|
64
64
|
"plugin_catalog"
|
|
65
65
|
];
|
|
66
66
|
var DEFAULT_PRESET = "meta";
|
|
67
|
-
var DEFAULT_SDK_VERSION = "^0.2.
|
|
67
|
+
var DEFAULT_SDK_VERSION = "^0.2.1";
|
|
68
68
|
function sortedCapabilities(values) {
|
|
69
69
|
const unique = Array.from(new Set(values));
|
|
70
70
|
return CAPABILITIES.filter((capability) => unique.includes(capability));
|
|
@@ -194,14 +194,14 @@ ${advanced ? ` {
|
|
|
194
194
|
mediaTypes: ["movie", "episode"],
|
|
195
195
|
defaultLanguages: ["en"],
|
|
196
196
|
handler: async (request, { settings }) => {
|
|
197
|
-
const configuredLanguages = Array.isArray(settings
|
|
197
|
+
const configuredLanguages = Array.isArray(settings?.languages)
|
|
198
198
|
? settings.languages
|
|
199
199
|
: [];
|
|
200
200
|
const languagePreferences =
|
|
201
201
|
configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);
|
|
202
202
|
|
|
203
203
|
void languagePreferences;
|
|
204
|
-
void settings
|
|
204
|
+
void settings?.includeHI;
|
|
205
205
|
|
|
206
206
|
return {
|
|
207
207
|
subtitles: [],
|
package/dist/cli.js.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 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":["path","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.3\",\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.1\";\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":["path","path"]}
|
package/dist/index.cjs
CHANGED
|
@@ -49,7 +49,7 @@ var CAPABILITIES = [
|
|
|
49
49
|
"plugin_catalog"
|
|
50
50
|
];
|
|
51
51
|
var DEFAULT_PRESET = "meta";
|
|
52
|
-
var DEFAULT_SDK_VERSION = "^0.2.
|
|
52
|
+
var DEFAULT_SDK_VERSION = "^0.2.1";
|
|
53
53
|
function sortedCapabilities(values) {
|
|
54
54
|
const unique = Array.from(new Set(values));
|
|
55
55
|
return CAPABILITIES.filter((capability) => unique.includes(capability));
|
|
@@ -179,14 +179,14 @@ ${advanced ? ` {
|
|
|
179
179
|
mediaTypes: ["movie", "episode"],
|
|
180
180
|
defaultLanguages: ["en"],
|
|
181
181
|
handler: async (request, { settings }) => {
|
|
182
|
-
const configuredLanguages = Array.isArray(settings
|
|
182
|
+
const configuredLanguages = Array.isArray(settings?.languages)
|
|
183
183
|
? settings.languages
|
|
184
184
|
: [];
|
|
185
185
|
const languagePreferences =
|
|
186
186
|
configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);
|
|
187
187
|
|
|
188
188
|
void languagePreferences;
|
|
189
|
-
void settings
|
|
189
|
+
void settings?.includeHI;
|
|
190
190
|
|
|
191
191
|
return {
|
|
192
192
|
subtitles: [],
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/scaffold.ts"],"sourcesContent":["export * from \"./scaffold\";\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":["path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/scaffold.ts"],"sourcesContent":["export * from \"./scaffold\";\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.1\";\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":["path"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ type Capability = (typeof CAPABILITIES)[number];
|
|
|
3
3
|
type Preset = Capability;
|
|
4
4
|
type Language = "ts" | "js";
|
|
5
5
|
declare const DEFAULT_PRESET: Preset;
|
|
6
|
-
declare const DEFAULT_SDK_VERSION = "^0.2.
|
|
6
|
+
declare const DEFAULT_SDK_VERSION = "^0.2.1";
|
|
7
7
|
interface ScaffoldOptions {
|
|
8
8
|
targetDir: string;
|
|
9
9
|
projectName: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ type Capability = (typeof CAPABILITIES)[number];
|
|
|
3
3
|
type Preset = Capability;
|
|
4
4
|
type Language = "ts" | "js";
|
|
5
5
|
declare const DEFAULT_PRESET: Preset;
|
|
6
|
-
declare const DEFAULT_SDK_VERSION = "^0.2.
|
|
6
|
+
declare const DEFAULT_SDK_VERSION = "^0.2.1";
|
|
7
7
|
interface ScaffoldOptions {
|
|
8
8
|
targetDir: string;
|
|
9
9
|
projectName: string;
|
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ var CAPABILITIES = [
|
|
|
11
11
|
"plugin_catalog"
|
|
12
12
|
];
|
|
13
13
|
var DEFAULT_PRESET = "meta";
|
|
14
|
-
var DEFAULT_SDK_VERSION = "^0.2.
|
|
14
|
+
var DEFAULT_SDK_VERSION = "^0.2.1";
|
|
15
15
|
function sortedCapabilities(values) {
|
|
16
16
|
const unique = Array.from(new Set(values));
|
|
17
17
|
return CAPABILITIES.filter((capability) => unique.includes(capability));
|
|
@@ -141,14 +141,14 @@ ${advanced ? ` {
|
|
|
141
141
|
mediaTypes: ["movie", "episode"],
|
|
142
142
|
defaultLanguages: ["en"],
|
|
143
143
|
handler: async (request, { settings }) => {
|
|
144
|
-
const configuredLanguages = Array.isArray(settings
|
|
144
|
+
const configuredLanguages = Array.isArray(settings?.languages)
|
|
145
145
|
? settings.languages
|
|
146
146
|
: [];
|
|
147
147
|
const languagePreferences =
|
|
148
148
|
configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);
|
|
149
149
|
|
|
150
150
|
void languagePreferences;
|
|
151
|
-
void settings
|
|
151
|
+
void settings?.includeHI;
|
|
152
152
|
|
|
153
153
|
return {
|
|
154
154
|
subtitles: [],
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/scaffold.ts"],"sourcesContent":["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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/scaffold.ts"],"sourcesContent":["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.1\";\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":[]}
|