@streamfox/create-streamfox-plugin 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,13 +75,12 @@ create-streamfox-plugin streamfox-opensubs \
75
75
  - install deeplink URL
76
76
  - launch URL
77
77
  - `test/plugin.test.(ts|js)` smoke test (`/manifest`, `/studio-config`)
78
- - `README.md`, `package.json`, `.prettierrc.json`, `.prettierignore`, and (for TS) `tsconfig.json`
78
+ - `README.md`, `package.json`, `.gitignore`, and (for TS) `tsconfig.json`
79
79
 
80
80
  ## Development
81
81
 
82
82
  ```bash
83
83
  npm install
84
- npm run format
85
84
  npm run build
86
85
  npm test
87
86
  ```
package/dist/cli.cjs CHANGED
@@ -31,7 +31,7 @@ var import_prompts = __toESM(require("prompts"), 1);
31
31
  // package.json
32
32
  var package_default = {
33
33
  name: "@streamfox/create-streamfox-plugin",
34
- version: "0.3.0",
34
+ version: "0.3.1",
35
35
  description: "Standalone CLI to scaffold StreamFox plugin projects",
36
36
  type: "module",
37
37
  bin: {
@@ -107,22 +107,18 @@ function makePackageJson(name, language, sdkVersion) {
107
107
  const scripts = language === "ts" ? {
108
108
  dev: "tsx watch src/server.ts",
109
109
  build: "tsc -p tsconfig.json",
110
- format: "prettier --write .",
111
- "format:check": "prettier --check .",
112
110
  start: "node dist/server.js",
113
111
  test: "vitest run",
114
112
  typecheck: "tsc --noEmit"
115
113
  } : {
116
114
  dev: "node --watch src/server.js",
117
115
  build: 'echo "No build step for JavaScript template"',
118
- format: "prettier --write .",
119
- "format:check": "prettier --check .",
120
116
  start: "node src/server.js",
121
117
  test: "vitest run"
122
118
  };
123
119
  const normalizedScripts = {
124
120
  ...scripts,
125
- check: language === "ts" ? "npm run format:check && npm run typecheck && npm test && npm run build" : "npm run format:check && npm test && npm run build"
121
+ check: language === "ts" ? "npm run typecheck && npm test && npm run build" : "npm test && npm run build"
126
122
  };
127
123
  const packageJson = {
128
124
  name,
@@ -135,12 +131,10 @@ function makePackageJson(name, language, sdkVersion) {
135
131
  },
136
132
  devDependencies: language === "ts" ? {
137
133
  "@types/node": "^24.6.0",
138
- prettier: "^3.6.2",
139
134
  tsx: "^4.20.5",
140
135
  typescript: "^5.9.2",
141
136
  vitest: "^2.1.9"
142
137
  } : {
143
- prettier: "^3.6.2",
144
138
  vitest: "^2.1.9"
145
139
  }
146
140
  };
@@ -367,15 +361,12 @@ var tsConfig = `{
367
361
  "include": ["src"]
368
362
  }
369
363
  `;
370
- var prettierConfig = `{
371
- "semi": true,
372
- "singleQuote": false,
373
- "trailingComma": "all"
374
- }
375
- `;
376
- var prettierIgnore = `dist
364
+ var gitIgnore = `dist
377
365
  node_modules
378
366
  .DS_Store
367
+ .env
368
+ .env.*
369
+ coverage
379
370
  `;
380
371
  function makeReadme(projectName, capabilities, advanced) {
381
372
  const capabilitiesList = capabilities.map((capability) => `- ${capability}`).join("\n");
@@ -413,9 +404,9 @@ Advanced template: \`${advanced ? "enabled" : "disabled"}\`
413
404
 
414
405
  - npm run dev
415
406
  - npm run build
416
- - npm run format
417
407
  - npm run start
418
408
  - npm run test
409
+ - npm run check
419
410
 
420
411
  ## Implemented Capabilities
421
412
 
@@ -448,14 +439,7 @@ async function scaffoldProject(options) {
448
439
  import_node_path.default.join(options.targetDir, "package.json"),
449
440
  makePackageJson(options.projectName, options.language, sdkVersion)
450
441
  );
451
- await (0, import_promises.writeFile)(
452
- import_node_path.default.join(options.targetDir, ".prettierrc.json"),
453
- prettierConfig
454
- );
455
- await (0, import_promises.writeFile)(
456
- import_node_path.default.join(options.targetDir, ".prettierignore"),
457
- prettierIgnore
458
- );
442
+ await (0, import_promises.writeFile)(import_node_path.default.join(options.targetDir, ".gitignore"), gitIgnore);
459
443
  await (0, import_promises.writeFile)(
460
444
  import_node_path.default.join(options.targetDir, "README.md"),
461
445
  makeReadme(options.projectName, capabilities, advanced)
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.0\",\n \"description\": \"Standalone CLI to scaffold StreamFox plugin projects\",\n \"type\": \"module\",\n \"bin\": {\n \"create-streamfox-plugin\": \"dist/cli.cjs\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"test\": \"vitest run\",\n \"typecheck\": \"tsc --noEmit\",\n \"check\": \"npm run format:check && npm run typecheck && npm test && npm run build\"\n },\n \"dependencies\": {\n \"commander\": \"^12.1.0\",\n \"prompts\": \"^2.4.2\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.6.0\",\n \"@types/prompts\": \"^2.4.9\",\n \"prettier\": \"^3.6.2\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.2\",\n \"vitest\": \"^2.1.9\"\n },\n \"engines\": {\n \"node\": \">=20\"\n }\n}\n","import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const CAPABILITIES = [\n \"catalog\",\n \"meta\",\n \"stream\",\n \"subtitles\",\n \"plugin_catalog\",\n] as const;\nexport type Capability = (typeof CAPABILITIES)[number];\nexport type Preset = Capability;\nexport type Language = \"ts\" | \"js\";\nexport const DEFAULT_PRESET: Preset = \"meta\";\nexport const DEFAULT_SDK_VERSION = \"^0.2.0\";\n\nexport interface ScaffoldOptions {\n targetDir: string;\n projectName: string;\n language: Language;\n preset?: Preset;\n 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 format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node dist/server.js\",\n test: \"vitest run\",\n typecheck: \"tsc --noEmit\",\n }\n : {\n dev: \"node --watch src/server.js\",\n build: 'echo \"No build step for JavaScript template\"',\n format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node src/server.js\",\n test: \"vitest run\",\n };\n\n const normalizedScripts = {\n ...scripts,\n check:\n language === \"ts\"\n ? \"npm run format:check && npm run typecheck && npm test && npm run build\"\n : \"npm run format:check && npm test && npm run build\",\n };\n\n const packageJson = {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: normalizedScripts,\n dependencies: {\n \"@streamfox/plugin-sdk\": sdkVersion,\n },\n devDependencies:\n language === \"ts\"\n ? {\n \"@types/node\": \"^24.6.0\",\n prettier: \"^3.6.2\",\n tsx: \"^4.20.5\",\n typescript: \"^5.9.2\",\n vitest: \"^2.1.9\",\n }\n : {\n prettier: \"^3.6.2\",\n vitest: \"^2.1.9\",\n },\n };\n\n return `${JSON.stringify(packageJson, null, 2)}\\n`;\n}\n\nfunction resourceBlock(capability: Capability, advanced: boolean): string {\n switch (capability) {\n case \"catalog\":\n return `catalog: {\n endpoints: [\n {\n id: \"top\",\n name: \"Top\",\n mediaTypes: [\"movie\"],\n filters: [{ key: \"genre\", valueType: \"string\" }],\n },\n ],\n handler: async () => ({\n items: [],\n }),\n },`;\n case \"meta\":\n return `meta: {\n mediaTypes: [\"movie\"],\n includes: [\"videos\", \"links\"],\n handler: async () => ({\n item: ${\n advanced\n ? `{\n summary: {\n id: { namespace: \"imdb\", value: \"tt1254207\" },\n mediaType: \"movie\",\n title: \"Big Buck Bunny\",\n links: [],\n },\n defaultVideoID: \"main\",\n trailers: [{ transport: { kind: \"youtube\", id: \"aqz-KE-bpKQ\" } }],\n videos: [\n {\n id: \"main\",\n title: \"Main\",\n streams: [{ transport: { kind: \"http\", url: \"https://example.com/video.mp4\" } }],\n },\n ],\n }`\n : \"null\"\n },\n }),\n },`;\n case \"stream\":\n return `stream: {\n mediaTypes: [\"movie\"],\n supportedTransports: ${advanced ? `[\"http\", \"torrent\", \"usenet\", \"archive\", \"youtube\"]` : `[\"http\"]`},\n handler: async () => ({\n streams: [\n {\n transport: { kind: \"http\", url: \"https://example.com/video.mp4\", mode: \"stream\" },\n hints: {\n notWebReady: true,\n proxyHeaders: { request: { \"User-Agent\": \"StreamFox\" } },\n },\n },\n${\n advanced\n ? ` {\n transport: { kind: \"torrent\", infoHash: \"abcdef\", peerDiscovery: [\"tracker:udp://tracker.example.com:80\"] },\n selection: { fileIndex: 0 },\n },\n {\n transport: { kind: \"usenet\", nzbURL: \"https://example.com/file.nzb\", servers: [\"nntps://user:pass@news.example.com:563/4\"] },\n },\n {\n transport: {\n kind: \"archive\",\n format: \"zip\",\n files: [{ url: \"https://example.com/archive.zip\", bytes: 1024 }],\n },\n selection: { fileMustInclude: \"movie.mkv\" },\n },\n`\n : \"\"\n} ],\n }),\n },`;\n case \"subtitles\":\n return `subtitles: {\n mediaTypes: [\"movie\", \"episode\"],\n defaultLanguages: [\"en\"],\n handler: async (request, { settings }) => {\n const configuredLanguages = Array.isArray(settings.languages)\n ? settings.languages\n : [];\n const languagePreferences =\n configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);\n\n void languagePreferences;\n void settings.includeHI;\n\n return {\n subtitles: [],\n };\n },\n },`;\n case \"plugin_catalog\":\n return `pluginCatalog: {\n endpoints: [\n {\n id: \"featured\",\n name: \"Featured\",\n pluginKinds: [\"catalog\", \"meta\", \"stream\", \"subtitles\"],\n tags: [\"official\"],\n },\n ],\n handler: async () => ({\n plugins: [\n {\n id: \"com.example.recommended\",\n name: \"Recommended\",\n version: \"1.0.0\",\n pluginKinds: [\"catalog\", \"meta\"],\n distribution: {\n transport: \"https\",\n manifestURL: \"https://plugins.example.com/recommended/manifest\",\n },\n manifestSnapshot: {\n plugin: { id: \"com.example.recommended\" },\n },\n },\n ],\n }),\n },`;\n default:\n return \"\";\n }\n}\n\nfunction makeInstallBlock(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 prettierConfig = `{\n \"semi\": true,\n \"singleQuote\": false,\n \"trailingComma\": \"all\"\n}\n`;\n\nconst prettierIgnore = `dist\nnode_modules\n.DS_Store\n`;\n\nfunction makeReadme(\n projectName: string,\n 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 format\n- npm run start\n- npm run test\n\n## Implemented Capabilities\n\n${capabilitiesList}\n\n## Stream Model\n\n- Unified transport model via \\`stream.transport\\`\n- Capability declaration via \\`resources.stream.supportedTransports\\`\n- Optional selection controls via \\`stream.selection\\`\n\n## Endpoints\n\n${endpointLines}\n`;\n}\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<void> {\n const capabilities = 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(\n path.join(options.targetDir, \".prettierrc.json\"),\n prettierConfig,\n );\n await writeFile(\n path.join(options.targetDir, \".prettierignore\"),\n prettierIgnore,\n );\n await writeFile(\n path.join(options.targetDir, \"README.md\"),\n makeReadme(options.projectName, 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":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,oBAAiB;AACjB,uBAA8C;AAC9C,qBAAoB;;;ACFpB;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,MAAQ;AAAA,EACR,KAAO;AAAA,IACL,2BAA2B;AAAA,EAC7B;AAAA,EACA,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,OAAS;AAAA,EACT,SAAW;AAAA,IACT,KAAK;AAAA,MACH,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,OAAS;AAAA,IACP;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,QAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,MAAQ;AAAA,IACR,WAAa;AAAA,IACb,OAAS;AAAA,EACX;AAAA,EACA,cAAgB;AAAA,IACd,WAAa;AAAA,IACb,SAAW;AAAA,EACb;AAAA,EACA,iBAAmB;AAAA,IACjB,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,UAAY;AAAA,IACZ,MAAQ;AAAA,IACR,YAAc;AAAA,IACd,QAAU;AAAA,EACZ;AAAA,EACA,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AACF;;;AC7CA,sBAAyC;AACzC,uBAAiB;AAEV,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,iBAAyB;AAC/B,IAAM,sBAAsB;AAanC,SAAS,mBAAmB,QAAoC;AAC9D,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACzC,SAAO,aAAa,OAAO,CAAC,eAAe,OAAO,SAAS,UAAU,CAAC;AACxE;AAEA,eAAe,yBAAyB,WAAkC;AACxE,MAAI;AACF,cAAM,wBAAO,SAAS;AACtB,UAAM,IAAI,MAAM,oCAAoC,SAAS,EAAE;AAAA,EACjE,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,gBACP,MACA,UACA,YACQ;AACR,QAAM,UACJ,aAAa,OACT;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb,IACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAEN,QAAM,oBAAoB;AAAA,IACxB,GAAG;AAAA,IACH,OACE,aAAa,OACT,2EACA;AAAA,EACR;AAEA,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,MACZ,yBAAyB;AAAA,IAC3B;AAAA,IACA,iBACE,aAAa,OACT;AAAA,MACE,eAAe;AAAA,MACf,UAAU;AAAA,MACV,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV,IACA;AAAA,MACE,UAAU;AAAA,MACV,QAAQ;AAAA,IACV;AAAA,EACR;AAEA,SAAO,GAAG,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;AAAA;AAChD;AAEA,SAAS,cAAc,YAAwB,UAA2B;AACxE,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA,gBAKH,WACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAiBA,MACN;AAAA;AAAA;AAAA,IAGJ,KAAK;AACH,aAAO;AAAA;AAAA,6BAEgB,WAAW,wDAAwD,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxG,WACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBA,EACN;AAAA;AAAA;AAAA,IAGI,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,iBAAiB,cAAoC;AAC5D,MAAI,CAAC,aAAa,SAAS,WAAW,GAAG;AACvC,WAAO;AAAA,EACT;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBT;AAEA,SAAS,eACP,MACA,cACA,UACQ;AACR,QAAM,YAAY,aACf,IAAI,CAAC,eAAe,cAAc,YAAY,QAAQ,CAAC,EACvD,KAAK,QAAQ;AAChB,QAAM,UAAU,iBAAiB,YAAY;AAC7C,QAAM,aACJ,QAAQ,SAAS,IAAI,2BAA2B;AAClD,QAAM,eAAe,QAAQ,SAAS,IAAI,GAAG,OAAO,KAAK;AAEzD,SAAO,YAAY,UAAU;AAAA;AAAA;AAAA;AAAA,uBAIR,IAAI;AAAA,aACd,IAAI;AAAA;AAAA;AAAA;AAAA,EAIf,YAAY;AAAA,MACR,SAAS;AAAA;AAAA;AAAA;AAIf;AAEA,SAAS,eAAe,UAA4B;AAClD,QAAM,eAAe,aAAa,OAAO,aAAa;AACtD,SAAO;AAAA,0BACiB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAetC;AAEA,SAAS,eAAe,UAA4B;AAClD,QAAM,eAAe,aAAa,OAAO,kBAAkB;AAC3D,SAAO;AAAA;AAAA,0BAEiB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AActC;AAEA,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAejB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOvB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAKvB,SAAS,WACP,aACA,cACA,UACQ;AACR,QAAM,mBAAmB,aACtB,IAAI,CAAC,eAAe,KAAK,UAAU,EAAE,EACrC,KAAK,IAAI;AAEZ,QAAM,wBAAwB,CAAC,eAAmC;AAChE,YAAQ,YAAY;AAAA,MAClB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO,IAAI,UAAU;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA,GAAG,aAAa;AAAA,MACd,CAAC,eAAe,SAAS,sBAAsB,UAAU,CAAC;AAAA,IAC5D;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,SAAO,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA,kBAIP,aAAa,KAAK,IAAI,CAAC;AAAA,uBAClB,WAAW,YAAY,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYtD,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUhB,aAAa;AAAA;AAEf;AAEA,eAAsB,gBAAgB,SAAyC;AAC7E,QAAM,eAAe,QAAQ,eACzB,mBAAmB,QAAQ,YAAY,IACvC,mBAAmB;AAAA,IACjB,QAAQ,UAAU;AAAA,IAClB,GAAI,QAAQ,qBAAqB,CAAC;AAAA,EACpC,CAAC;AACL,QAAM,cACH,QAAQ,cAAc,qBAAqB,KAAK,KAAK;AACxD,QAAM,WAAW,QAAQ,YAAY;AAErC,QAAM,yBAAyB,QAAQ,SAAS;AAEhD,QAAM,SAAS,iBAAAC,QAAK,KAAK,QAAQ,WAAW,KAAK;AACjD,QAAM,UAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,MAAM;AAEnD,YAAM,uBAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,YAAM,uBAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAExC,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,cAAc;AAAA,IAC3C,gBAAgB,QAAQ,aAAa,QAAQ,UAAU,UAAU;AAAA,EACnE;AACA,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,kBAAkB;AAAA,IAC/C;AAAA,EACF;AACA,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,iBAAiB;AAAA,IAC9C;AAAA,EACF;AACA,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,WAAW;AAAA,IACxC,WAAW,QAAQ,aAAa,cAAc,QAAQ;AAAA,EACxD;AAEA,MAAI,QAAQ,aAAa,MAAM;AAC7B,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,eAAe,GAAG,QAAQ;AACvE,cAAM;AAAA,MACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,GAAG,eAAe,IAAI,CAAC;AACpE,cAAM,2BAAU,iBAAAA,QAAK,KAAK,SAAS,gBAAgB,GAAG,eAAe,IAAI,CAAC;AAAA,EAC5E,OAAO;AACL,cAAM;AAAA,MACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,GAAG,eAAe,IAAI,CAAC;AACpE,cAAM,2BAAU,iBAAAA,QAAK,KAAK,SAAS,gBAAgB,GAAG,eAAe,IAAI,CAAC;AAAA,EAC5E;AACF;;;AFtdA,SAAS,oBAA4B;AACnC,SAAO,aAAa,KAAK,IAAI;AAC/B;AAEA,SAAS,YAAY,OAAuB;AAC1C,MAAI,aAAa,SAAS,KAAmB,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR,mBAAmB,KAAK,kBAAkB,kBAAkB,CAAC;AAAA,EAC/D;AACF;AAEA,SAAS,sBAAsB,OAA6B;AAC1D,QAAM,SAAS,MACZ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAErC,aAAW,cAAc,QAAQ;AAC/B,QAAI,CAAC,aAAa,SAAS,UAAU,GAAG;AACtC,YAAM,IAAI;AAAA,QACR,uBAAuB,UAAU,kBAAkB,kBAAkB,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACnC;AAEA,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QACG,KAAK,yBAAyB,EAC9B,QAAQ,gBAAY,SAAS,iBAAiB,iCAAiC,EAC/E,YAAY,8DAA8D,EAC1E,mBAAmB,EACnB,SAAS,eAAe,kBAAkB,EAC1C,OAAO,QAAQ,yBAAyB,EACxC,OAAO,QAAQ,yBAAyB,EACxC;AAAA,EACC;AAAA,EACA,kBAAkB,kBAAkB,CAAC;AAAA,EACrC;AACF,EACC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,cAAc,uCAAuC,EAC5D;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,SAAS,+BAA+B,EAC/C;AAAA,EACC,OACE,cACA,YASG;AACH,QAAI,QAAQ,MAAM,QAAQ,IAAI;AAC5B,YAAM,IAAI,sCAAqB,uCAAuC;AAAA,IACxE;AAEA,UAAM,iBAAiB;AAAA,MACrB,WAAW,gBAAgB;AAAA,MAC3B,UAAU,QAAQ,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,MAClD,QAAQ,QAAQ,UAAU;AAAA,MAC1B,cAAc,MAAM;AAAA,QAClB,oBAAI,IAAI;AAAA,UACN,QAAQ,UAAU;AAAA,UAClB,GAAI,QAAQ,gBAAgB,CAAC;AAAA,QAC/B,CAAC;AAAA,MACH;AAAA,MACA,UAAU,QAAQ,YAAY;AAAA,MAC9B,YAAY,QAAQ,cAAc;AAAA,IACpC;AAEA,UAAM,eAAe,CAAC,QAAQ;AAE9B,QAAI,YAAY,eAAe;AAC/B,QAAI,WAAW,eAAe;AAC9B,QAAI,SAAS,eAAe;AAC5B,QAAI,eAAe,eAAe;AAClC,QAAI,WAAW,eAAe;AAC9B,QAAI,aAAa,eAAe;AAEhC,QAAI,cAAc;AAChB,YAAM,UAAU,UAAM,eAAAC;AAAA,QACpB;AAAA,UACE;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,cACP,EAAE,OAAO,cAAc,OAAO,KAAK;AAAA,cACnC,EAAE,OAAO,cAAc,OAAO,KAAK;AAAA,YACrC;AAAA,YACA,SAAS,aAAa,OAAO,IAAI;AAAA,UACnC;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS,aAAa,IAAI,CAAC,gBAAgB;AAAA,cACzC,OAAO;AAAA,cACP,OAAO;AAAA,cACP,UAAU,aAAa,SAAS,UAAU;AAAA,YAC5C,EAAE;AAAA,YACF,cAAc;AAAA,YACd,KAAK;AAAA,YACL,MAAM;AAAA,UACR;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,QACF;AAAA,QACA;AAAA,UACE,UAAU,MAAM;AACd,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAEA,kBAAY,QAAQ;AACpB,iBAAW,QAAQ;AACnB,qBAAgB,QAAQ,gBACtB,eAAe;AACjB,eACE,aAAa,CAAC,KAAK,eAAe;AACpC,iBAAW,QAAQ,QAAQ,YAAY,eAAe,QAAQ;AAC9D,mBAAa,QAAQ,cAAc,eAAe;AAAA,IACpD;AAEA,UAAM,YAAY,kBAAAC,QAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AACvD,UAAM,cAAc,kBAAAA,QAAK,SAAS,SAAS;AAE3C,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,WAAW,WAAW,OAAO,SAAS,EAAE;AACpD,YAAQ,IAAI,aAAa;AACzB,YAAQ,IAAI,QAAQ,SAAS,EAAE;AAC/B,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,eAAe;AAAA,EAC7B;AACF;AAEF,KAAK,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,UAAmB;AAC9D,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAQ,MAAM,UAAU,OAAO,EAAE;AACjC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["import_node_path","path","prompts","path"]}
1
+ {"version":3,"sources":["../src/cli.ts","../package.json","../src/scaffold.ts"],"sourcesContent":["import path from \"node:path\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport prompts from \"prompts\";\nimport packageJSON from \"../package.json\";\nimport {\n CAPABILITIES,\n DEFAULT_PRESET,\n DEFAULT_SDK_VERSION,\n scaffoldProject,\n type Capability,\n type Language,\n type Preset,\n} from \"./scaffold\";\n\nfunction capabilitiesLabel(): string {\n return CAPABILITIES.join(\", \");\n}\n\nfunction parsePreset(input: string): Preset {\n if (CAPABILITIES.includes(input as Capability)) {\n return input as Preset;\n }\n throw new InvalidArgumentError(\n `Invalid preset '${input}'. Use one of: ${capabilitiesLabel()}`,\n );\n}\n\nfunction parseCapabilitiesList(input: string): Capability[] {\n const parsed = input\n .split(\",\")\n .map((value) => value.trim())\n .filter((value) => value.length > 0) as Capability[];\n\n for (const capability of parsed) {\n if (!CAPABILITIES.includes(capability)) {\n throw new InvalidArgumentError(\n `Invalid capability '${capability}'. Use one of: ${capabilitiesLabel()}`,\n );\n }\n }\n\n return Array.from(new Set(parsed));\n}\n\nconst program = new Command();\n\nprogram\n .name(\"create-streamfox-plugin\")\n .version(packageJSON.version, \"-v, --version\", \"display the current CLI version\")\n .description(\"Scaffold StreamFox plugin projects with modern JS/TS presets\")\n .showHelpAfterError()\n .argument(\"[directory]\", \"output directory\")\n .option(\"--ts\", \"use TypeScript template\")\n .option(\"--js\", \"use JavaScript template\")\n .option(\n \"--preset <preset>\",\n `plugin preset: ${capabilitiesLabel()}`,\n parsePreset,\n )\n .option(\n \"--capabilities <capabilities>\",\n \"extra capabilities as comma-separated list\",\n parseCapabilitiesList,\n )\n .option(\"--advanced\", \"generate advanced capability examples\")\n .option(\n \"--sdk-version <range>\",\n \"@streamfox/plugin-sdk version/range\",\n DEFAULT_SDK_VERSION,\n )\n .option(\"--yes\", \"skip prompts and use defaults\")\n .action(\n async (\n directoryArg: string | undefined,\n options: {\n ts?: boolean;\n js?: boolean;\n yes?: boolean;\n preset?: Preset;\n capabilities?: Capability[];\n advanced?: boolean;\n sdkVersion?: string;\n },\n ) => {\n if (options.ts && options.js) {\n throw new InvalidArgumentError(\"Choose either --ts or --js, not both.\");\n }\n\n const promptDefaults = {\n directory: directoryArg ?? \"my-media-plugin\",\n language: options.ts ? \"ts\" : options.js ? \"js\" : \"ts\",\n preset: options.preset ?? DEFAULT_PRESET,\n capabilities: Array.from(\n new Set([\n options.preset ?? DEFAULT_PRESET,\n ...(options.capabilities ?? []),\n ]),\n ) as Capability[],\n advanced: options.advanced ?? false,\n sdkVersion: options.sdkVersion ?? DEFAULT_SDK_VERSION,\n };\n\n const shouldPrompt = !options.yes;\n\n let directory = promptDefaults.directory;\n let language = promptDefaults.language as Language;\n let preset = promptDefaults.preset;\n let capabilities = promptDefaults.capabilities;\n let advanced = promptDefaults.advanced;\n let sdkVersion = promptDefaults.sdkVersion;\n\n if (shouldPrompt) {\n const answers = await prompts(\n [\n {\n type: \"text\",\n name: \"directory\",\n message: \"Project directory\",\n initial: directory,\n },\n {\n type: \"select\",\n name: \"language\",\n message: \"Template language\",\n choices: [\n { title: \"TypeScript\", value: \"ts\" },\n { title: \"JavaScript\", value: \"js\" },\n ],\n initial: language === \"ts\" ? 0 : 1,\n },\n {\n type: \"multiselect\",\n name: \"capabilities\",\n message: \"Plugin capabilities\",\n choices: CAPABILITIES.map((capability) => ({\n title: capability,\n value: capability,\n selected: capabilities.includes(capability),\n })),\n instructions: false,\n min: 1,\n hint: \"Space to select\",\n },\n {\n type: \"confirm\",\n name: \"advanced\",\n message: \"Generate advanced capability examples?\",\n initial: advanced,\n },\n {\n type: \"text\",\n name: \"sdkVersion\",\n message: \"SDK dependency version/range\",\n initial: sdkVersion,\n },\n ],\n {\n onCancel: () => {\n process.exit(1);\n },\n },\n );\n\n directory = answers.directory;\n language = answers.language;\n capabilities = (answers.capabilities ??\n promptDefaults.capabilities) as Capability[];\n preset =\n capabilities[0] ?? promptDefaults.preset;\n advanced = Boolean(answers.advanced ?? promptDefaults.advanced);\n sdkVersion = answers.sdkVersion ?? promptDefaults.sdkVersion;\n }\n\n const targetDir = path.resolve(process.cwd(), directory);\n const projectName = path.basename(targetDir);\n\n await scaffoldProject({\n targetDir,\n projectName,\n language,\n capabilities,\n preset,\n advanced,\n sdkVersion,\n });\n\n console.log(`Created ${projectName} at ${targetDir}`);\n console.log(\"Next steps:\");\n console.log(` cd ${directory}`);\n console.log(\" npm install\");\n console.log(\" npm run dev\");\n },\n );\n\nvoid program.parseAsync(process.argv).catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`Error: ${message}`);\n process.exit(1);\n});\n","{\n \"name\": \"@streamfox/create-streamfox-plugin\",\n \"version\": \"0.3.1\",\n \"description\": \"Standalone CLI to scaffold StreamFox plugin projects\",\n \"type\": \"module\",\n \"bin\": {\n \"create-streamfox-plugin\": \"dist/cli.cjs\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"test\": \"vitest run\",\n \"typecheck\": \"tsc --noEmit\",\n \"check\": \"npm run format:check && npm run typecheck && npm test && npm run build\"\n },\n \"dependencies\": {\n \"commander\": \"^12.1.0\",\n \"prompts\": \"^2.4.2\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.6.0\",\n \"@types/prompts\": \"^2.4.9\",\n \"prettier\": \"^3.6.2\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.2\",\n \"vitest\": \"^2.1.9\"\n },\n \"engines\": {\n \"node\": \">=20\"\n }\n}\n","import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const CAPABILITIES = [\n \"catalog\",\n \"meta\",\n \"stream\",\n \"subtitles\",\n \"plugin_catalog\",\n] as const;\nexport type Capability = (typeof CAPABILITIES)[number];\nexport type Preset = Capability;\nexport type Language = \"ts\" | \"js\";\nexport const DEFAULT_PRESET: Preset = \"meta\";\nexport const DEFAULT_SDK_VERSION = \"^0.2.0\";\n\nexport interface ScaffoldOptions {\n targetDir: string;\n projectName: string;\n language: Language;\n preset?: Preset;\n capabilities?: Capability[];\n advanced?: boolean;\n extraCapabilities?: Capability[];\n sdkVersion?: string;\n}\n\nfunction sortedCapabilities(values: Capability[]): Capability[] {\n const unique = Array.from(new Set(values));\n return CAPABILITIES.filter((capability) => unique.includes(capability));\n}\n\nasync function ensureTargetDoesNotExist(targetDir: string): Promise<void> {\n try {\n await access(targetDir);\n throw new Error(`Target directory already exists: ${targetDir}`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return;\n }\n throw error;\n }\n}\n\nfunction makePackageJson(\n name: string,\n language: Language,\n sdkVersion: string,\n): string {\n const scripts =\n language === \"ts\"\n ? {\n dev: \"tsx watch src/server.ts\",\n build: \"tsc -p tsconfig.json\",\n start: \"node dist/server.js\",\n test: \"vitest run\",\n typecheck: \"tsc --noEmit\",\n }\n : {\n dev: \"node --watch src/server.js\",\n build: 'echo \"No build step for JavaScript template\"',\n start: \"node src/server.js\",\n test: \"vitest run\",\n };\n\n const normalizedScripts = {\n ...scripts,\n check:\n language === \"ts\"\n ? \"npm run typecheck && npm test && npm run build\"\n : \"npm test && npm run build\",\n };\n\n const packageJson = {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: normalizedScripts,\n dependencies: {\n \"@streamfox/plugin-sdk\": sdkVersion,\n },\n devDependencies:\n language === \"ts\"\n ? {\n \"@types/node\": \"^24.6.0\",\n tsx: \"^4.20.5\",\n typescript: \"^5.9.2\",\n vitest: \"^2.1.9\",\n }\n : {\n vitest: \"^2.1.9\",\n },\n };\n\n return `${JSON.stringify(packageJson, null, 2)}\\n`;\n}\n\nfunction resourceBlock(capability: Capability, advanced: boolean): string {\n switch (capability) {\n case \"catalog\":\n return `catalog: {\n endpoints: [\n {\n id: \"top\",\n name: \"Top\",\n mediaTypes: [\"movie\"],\n filters: [{ key: \"genre\", valueType: \"string\" }],\n },\n ],\n handler: async () => ({\n items: [],\n }),\n },`;\n case \"meta\":\n return `meta: {\n mediaTypes: [\"movie\"],\n includes: [\"videos\", \"links\"],\n handler: async () => ({\n item: ${\n advanced\n ? `{\n summary: {\n id: { namespace: \"imdb\", value: \"tt1254207\" },\n mediaType: \"movie\",\n title: \"Big Buck Bunny\",\n links: [],\n },\n defaultVideoID: \"main\",\n trailers: [{ transport: { kind: \"youtube\", id: \"aqz-KE-bpKQ\" } }],\n videos: [\n {\n id: \"main\",\n title: \"Main\",\n streams: [{ transport: { kind: \"http\", url: \"https://example.com/video.mp4\" } }],\n },\n ],\n }`\n : \"null\"\n },\n }),\n },`;\n case \"stream\":\n return `stream: {\n mediaTypes: [\"movie\"],\n supportedTransports: ${advanced ? `[\"http\", \"torrent\", \"usenet\", \"archive\", \"youtube\"]` : `[\"http\"]`},\n handler: async () => ({\n streams: [\n {\n transport: { kind: \"http\", url: \"https://example.com/video.mp4\", mode: \"stream\" },\n hints: {\n notWebReady: true,\n proxyHeaders: { request: { \"User-Agent\": \"StreamFox\" } },\n },\n },\n${\n advanced\n ? ` {\n transport: { kind: \"torrent\", infoHash: \"abcdef\", peerDiscovery: [\"tracker:udp://tracker.example.com:80\"] },\n selection: { fileIndex: 0 },\n },\n {\n transport: { kind: \"usenet\", nzbURL: \"https://example.com/file.nzb\", servers: [\"nntps://user:pass@news.example.com:563/4\"] },\n },\n {\n transport: {\n kind: \"archive\",\n format: \"zip\",\n files: [{ url: \"https://example.com/archive.zip\", bytes: 1024 }],\n },\n selection: { fileMustInclude: \"movie.mkv\" },\n },\n`\n : \"\"\n} ],\n }),\n },`;\n case \"subtitles\":\n return `subtitles: {\n mediaTypes: [\"movie\", \"episode\"],\n defaultLanguages: [\"en\"],\n handler: async (request, { settings }) => {\n const configuredLanguages = Array.isArray(settings.languages)\n ? settings.languages\n : [];\n const languagePreferences =\n configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);\n\n void languagePreferences;\n void settings.includeHI;\n\n return {\n subtitles: [],\n };\n },\n },`;\n case \"plugin_catalog\":\n return `pluginCatalog: {\n endpoints: [\n {\n id: \"featured\",\n name: \"Featured\",\n pluginKinds: [\"catalog\", \"meta\", \"stream\", \"subtitles\"],\n tags: [\"official\"],\n },\n ],\n handler: async () => ({\n plugins: [\n {\n id: \"com.example.recommended\",\n name: \"Recommended\",\n version: \"1.0.0\",\n pluginKinds: [\"catalog\", \"meta\"],\n distribution: {\n transport: \"https\",\n manifestURL: \"https://plugins.example.com/recommended/manifest\",\n },\n manifestSnapshot: {\n plugin: { id: \"com.example.recommended\" },\n },\n },\n ],\n }),\n },`;\n default:\n return \"\";\n }\n}\n\nfunction makeInstallBlock(capabilities: Capability[]): string {\n if (!capabilities.includes(\"subtitles\")) {\n return \"\";\n }\n\n return ` install: {\n configurationRequired: true,\n title: \"Subtitle Settings\",\n description: \"Configure subtitle defaults before installing this plugin.\",\n fields: [\n settings.multiSelect(\"languages\", {\n label: \"Languages\",\n options: [\n { label: \"English\", value: \"en\" },\n { label: \"Greek\", value: \"el\" },\n { label: \"Spanish\", value: \"es\" },\n ],\n defaultValue: [\"en\"],\n }),\n settings.checkbox(\"includeHI\", {\n label: \"Include hearing impaired\",\n defaultValue: true,\n }),\n ],\n },\n`;\n}\n\nfunction makePluginFile(\n name: string,\n capabilities: Capability[],\n advanced: boolean,\n): string {\n const resources = capabilities\n .map((capability) => resourceBlock(capability, advanced))\n .join(\"\\n \");\n const install = makeInstallBlock(capabilities);\n const importSpec =\n install.length > 0 ? \"definePlugin, settings\" : \"definePlugin\";\n const installBlock = install.length > 0 ? `${install}` : \"\";\n\n return `import { ${importSpec} } from \"@streamfox/plugin-sdk\";\n\nexport const plugin = definePlugin({\n plugin: {\n id: \"com.example.${name}\",\n name: \"${name}\",\n version: \"0.1.0\",\n description: \"Generated StreamFox plugin scaffold\",\n },\n${installBlock} resources: {\n ${resources}\n },\n});\n`;\n}\n\nfunction makeServerFile(language: Language): string {\n const pluginImport = language === \"ts\" ? \"./plugin\" : \"./plugin.js\";\n return `import { serve } from \"@streamfox/plugin-sdk\";\nimport { plugin } from \"${pluginImport}\";\n\nconst { url, installURL, launchURL } = await serve(plugin, {\n port: Number(process.env.PORT ?? 7000),\n integration: {\n installScheme: \"streamfox\",\n launchBaseURL: \"https://streamfox.app/#\",\n autoOpen: \"none\",\n },\n});\n\nconsole.log(\"Plugin manifest:\", url);\nconsole.log(\"Plugin installer deeplink:\", installURL);\nconsole.log(\"Plugin launch URL:\", launchURL);\n`;\n}\n\nfunction makeVitestFile(language: Language): string {\n const pluginImport = language === \"ts\" ? \"../src/plugin\" : \"../src/plugin.js\";\n return `import { describe, expect, it } from \"vitest\";\nimport { createServer } from \"@streamfox/plugin-sdk\";\nimport { plugin } from \"${pluginImport}\";\n\ndescribe(\"scaffold smoke\", () => {\n it(\"serves manifest and studio config\", async () => {\n const app = createServer(plugin, { frontend: false });\n\n const manifestResponse = await app.request(\"/manifest\");\n expect(manifestResponse.status).toBe(200);\n\n const studioResponse = await app.request(\"/studio-config\");\n expect(studioResponse.status).toBe(200);\n });\n});\n`;\n}\n\nconst tsConfig = `{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"ESNext\",\n \"moduleResolution\": \"Bundler\",\n \"strict\": true,\n \"declaration\": true,\n \"outDir\": \"dist\",\n \"rootDir\": \"src\",\n \"types\": [\"node\"]\n },\n \"include\": [\"src\"]\n}\n`;\n\nconst gitIgnore = `dist\nnode_modules\n.DS_Store\n.env\n.env.*\ncoverage\n`;\n\nfunction makeReadme(\n projectName: string,\n capabilities: Capability[],\n advanced: boolean,\n): string {\n const capabilitiesList = capabilities\n .map((capability) => `- ${capability}`)\n .join(\"\\n\");\n\n const endpointForCapability = (capability: Capability): string => {\n switch (capability) {\n case \"catalog\":\n return \"/catalog/:mediaType/:catalogID\";\n case \"meta\":\n return \"/meta/:mediaType/:itemID\";\n case \"stream\":\n return \"/stream/:mediaType/:itemID\";\n case \"subtitles\":\n return \"/subtitles/:mediaType/:itemID\";\n case \"plugin_catalog\":\n return \"/plugin_catalog/:catalogID/:pluginKind\";\n default:\n return `/${capability}`;\n }\n };\n\n const endpointLines = [\n \"- GET /manifest\",\n \"- GET /studio-config\",\n ...capabilities.map(\n (capability) => `- GET ${endpointForCapability(capability)}`,\n ),\n ].join(\"\\n\");\n\n return `# ${projectName}\n\nGenerated with create-streamfox-plugin.\n\nCapabilities: \\`${capabilities.join(\", \")}\\`\nAdvanced template: \\`${advanced ? \"enabled\" : \"disabled\"}\\`\n\n## Scripts\n\n- npm run dev\n- npm run build\n- npm run start\n- npm run test\n- npm run check\n\n## Implemented Capabilities\n\n${capabilitiesList}\n\n## Stream Model\n\n- Unified transport model via \\`stream.transport\\`\n- Capability declaration via \\`resources.stream.supportedTransports\\`\n- Optional selection controls via \\`stream.selection\\`\n\n## Endpoints\n\n${endpointLines}\n`;\n}\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<void> {\n const capabilities = options.capabilities\n ? sortedCapabilities(options.capabilities)\n : sortedCapabilities([\n options.preset ?? DEFAULT_PRESET,\n ...(options.extraCapabilities ?? []),\n ]);\n const sdkVersion =\n (options.sdkVersion ?? DEFAULT_SDK_VERSION).trim() || DEFAULT_SDK_VERSION;\n const advanced = options.advanced ?? false;\n\n await ensureTargetDoesNotExist(options.targetDir);\n\n const srcDir = path.join(options.targetDir, \"src\");\n const testDir = path.join(options.targetDir, \"test\");\n\n await mkdir(srcDir, { recursive: true });\n await mkdir(testDir, { recursive: true });\n\n await writeFile(\n path.join(options.targetDir, \"package.json\"),\n makePackageJson(options.projectName, options.language, sdkVersion),\n );\n await writeFile(path.join(options.targetDir, \".gitignore\"), gitIgnore);\n await writeFile(\n path.join(options.targetDir, \"README.md\"),\n makeReadme(options.projectName, capabilities, advanced),\n );\n\n if (options.language === \"ts\") {\n await writeFile(path.join(options.targetDir, \"tsconfig.json\"), tsConfig);\n await writeFile(\n path.join(srcDir, \"plugin.ts\"),\n makePluginFile(\n options.projectName,\n capabilities,\n advanced,\n ),\n );\n await writeFile(path.join(srcDir, \"server.ts\"), makeServerFile(\"ts\"));\n await writeFile(path.join(testDir, \"plugin.test.ts\"), makeVitestFile(\"ts\"));\n } else {\n await writeFile(\n path.join(srcDir, \"plugin.js\"),\n makePluginFile(\n options.projectName,\n capabilities,\n advanced,\n ),\n );\n await writeFile(path.join(srcDir, \"server.js\"), makeServerFile(\"js\"));\n await writeFile(path.join(testDir, \"plugin.test.js\"), makeVitestFile(\"js\"));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,oBAAiB;AACjB,uBAA8C;AAC9C,qBAAoB;;;ACFpB;AAAA,EACE,MAAQ;AAAA,EACR,SAAW;AAAA,EACX,aAAe;AAAA,EACf,MAAQ;AAAA,EACR,KAAO;AAAA,IACL,2BAA2B;AAAA,EAC7B;AAAA,EACA,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,OAAS;AAAA,EACT,SAAW;AAAA,IACT,KAAK;AAAA,MACH,OAAS;AAAA,MACT,QAAU;AAAA,MACV,SAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,OAAS;AAAA,IACP;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAW;AAAA,IACT,OAAS;AAAA,IACT,QAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,MAAQ;AAAA,IACR,WAAa;AAAA,IACb,OAAS;AAAA,EACX;AAAA,EACA,cAAgB;AAAA,IACd,WAAa;AAAA,IACb,SAAW;AAAA,EACb;AAAA,EACA,iBAAmB;AAAA,IACjB,eAAe;AAAA,IACf,kBAAkB;AAAA,IAClB,UAAY;AAAA,IACZ,MAAQ;AAAA,IACR,YAAc;AAAA,IACd,QAAU;AAAA,EACZ;AAAA,EACA,SAAW;AAAA,IACT,MAAQ;AAAA,EACV;AACF;;;AC7CA,sBAAyC;AACzC,uBAAiB;AAEV,IAAM,eAAe;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,iBAAyB;AAC/B,IAAM,sBAAsB;AAanC,SAAS,mBAAmB,QAAoC;AAC9D,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACzC,SAAO,aAAa,OAAO,CAAC,eAAe,OAAO,SAAS,UAAU,CAAC;AACxE;AAEA,eAAe,yBAAyB,WAAkC;AACxE,MAAI;AACF,cAAM,wBAAO,SAAS;AACtB,UAAM,IAAI,MAAM,oCAAoC,SAAS,EAAE;AAAA,EACjE,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD;AAAA,IACF;AACA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,gBACP,MACA,UACA,YACQ;AACR,QAAM,UACJ,aAAa,OACT;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb,IACA;AAAA,IACE,KAAK;AAAA,IACL,OAAO;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAEN,QAAM,oBAAoB;AAAA,IACxB,GAAG;AAAA,IACH,OACE,aAAa,OACT,mDACA;AAAA,EACR;AAEA,QAAM,cAAc;AAAA,IAClB;AAAA,IACA,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,MACZ,yBAAyB;AAAA,IAC3B;AAAA,IACA,iBACE,aAAa,OACT;AAAA,MACE,eAAe;AAAA,MACf,KAAK;AAAA,MACL,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV,IACA;AAAA,MACE,QAAQ;AAAA,IACV;AAAA,EACR;AAEA,SAAO,GAAG,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;AAAA;AAChD;AAEA,SAAS,cAAc,YAAwB,UAA2B;AACxE,UAAQ,YAAY;AAAA,IAClB,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAaT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA,gBAKH,WACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,aAiBA,MACN;AAAA;AAAA;AAAA,IAGJ,KAAK;AACH,aAAO;AAAA;AAAA,6BAEgB,WAAW,wDAAwD,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWxG,WACI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBA,EACN;AAAA;AAAA;AAAA,IAGI,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBT,KAAK;AACH,aAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BT;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,iBAAiB,cAAoC;AAC5D,MAAI,CAAC,aAAa,SAAS,WAAW,GAAG;AACvC,WAAO;AAAA,EACT;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBT;AAEA,SAAS,eACP,MACA,cACA,UACQ;AACR,QAAM,YAAY,aACf,IAAI,CAAC,eAAe,cAAc,YAAY,QAAQ,CAAC,EACvD,KAAK,QAAQ;AAChB,QAAM,UAAU,iBAAiB,YAAY;AAC7C,QAAM,aACJ,QAAQ,SAAS,IAAI,2BAA2B;AAClD,QAAM,eAAe,QAAQ,SAAS,IAAI,GAAG,OAAO,KAAK;AAEzD,SAAO,YAAY,UAAU;AAAA;AAAA;AAAA;AAAA,uBAIR,IAAI;AAAA,aACd,IAAI;AAAA;AAAA;AAAA;AAAA,EAIf,YAAY;AAAA,MACR,SAAS;AAAA;AAAA;AAAA;AAIf;AAEA,SAAS,eAAe,UAA4B;AAClD,QAAM,eAAe,aAAa,OAAO,aAAa;AACtD,SAAO;AAAA,0BACiB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAetC;AAEA,SAAS,eAAe,UAA4B;AAClD,QAAM,eAAe,aAAa,OAAO,kBAAkB;AAC3D,SAAO;AAAA;AAAA,0BAEiB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AActC;AAEA,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAejB,IAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQlB,SAAS,WACP,aACA,cACA,UACQ;AACR,QAAM,mBAAmB,aACtB,IAAI,CAAC,eAAe,KAAK,UAAU,EAAE,EACrC,KAAK,IAAI;AAEZ,QAAM,wBAAwB,CAAC,eAAmC;AAChE,YAAQ,YAAY;AAAA,MAClB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO,IAAI,UAAU;AAAA,IACzB;AAAA,EACF;AAEA,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA,GAAG,aAAa;AAAA,MACd,CAAC,eAAe,SAAS,sBAAsB,UAAU,CAAC;AAAA,IAC5D;AAAA,EACF,EAAE,KAAK,IAAI;AAEX,SAAO,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA,kBAIP,aAAa,KAAK,IAAI,CAAC;AAAA,uBAClB,WAAW,YAAY,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYtD,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUhB,aAAa;AAAA;AAEf;AAEA,eAAsB,gBAAgB,SAAyC;AAC7E,QAAM,eAAe,QAAQ,eACzB,mBAAmB,QAAQ,YAAY,IACvC,mBAAmB;AAAA,IACjB,QAAQ,UAAU;AAAA,IAClB,GAAI,QAAQ,qBAAqB,CAAC;AAAA,EACpC,CAAC;AACL,QAAM,cACH,QAAQ,cAAc,qBAAqB,KAAK,KAAK;AACxD,QAAM,WAAW,QAAQ,YAAY;AAErC,QAAM,yBAAyB,QAAQ,SAAS;AAEhD,QAAM,SAAS,iBAAAC,QAAK,KAAK,QAAQ,WAAW,KAAK;AACjD,QAAM,UAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,MAAM;AAEnD,YAAM,uBAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;AACvC,YAAM,uBAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAExC,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,cAAc;AAAA,IAC3C,gBAAgB,QAAQ,aAAa,QAAQ,UAAU,UAAU;AAAA,EACnE;AACA,YAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,YAAY,GAAG,SAAS;AACrE,YAAM;AAAA,IACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW,WAAW;AAAA,IACxC,WAAW,QAAQ,aAAa,cAAc,QAAQ;AAAA,EACxD;AAEA,MAAI,QAAQ,aAAa,MAAM;AAC7B,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,eAAe,GAAG,QAAQ;AACvE,cAAM;AAAA,MACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,GAAG,eAAe,IAAI,CAAC;AACpE,cAAM,2BAAU,iBAAAA,QAAK,KAAK,SAAS,gBAAgB,GAAG,eAAe,IAAI,CAAC;AAAA,EAC5E,OAAO;AACL,cAAM;AAAA,MACJ,iBAAAA,QAAK,KAAK,QAAQ,WAAW;AAAA,MAC7B;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,cAAM,2BAAU,iBAAAA,QAAK,KAAK,QAAQ,WAAW,GAAG,eAAe,IAAI,CAAC;AACpE,cAAM,2BAAU,iBAAAA,QAAK,KAAK,SAAS,gBAAgB,GAAG,eAAe,IAAI,CAAC;AAAA,EAC5E;AACF;;;AFrcA,SAAS,oBAA4B;AACnC,SAAO,aAAa,KAAK,IAAI;AAC/B;AAEA,SAAS,YAAY,OAAuB;AAC1C,MAAI,aAAa,SAAS,KAAmB,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR,mBAAmB,KAAK,kBAAkB,kBAAkB,CAAC;AAAA,EAC/D;AACF;AAEA,SAAS,sBAAsB,OAA6B;AAC1D,QAAM,SAAS,MACZ,MAAM,GAAG,EACT,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,EAC3B,OAAO,CAAC,UAAU,MAAM,SAAS,CAAC;AAErC,aAAW,cAAc,QAAQ;AAC/B,QAAI,CAAC,aAAa,SAAS,UAAU,GAAG;AACtC,YAAM,IAAI;AAAA,QACR,uBAAuB,UAAU,kBAAkB,kBAAkB,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC;AACnC;AAEA,IAAM,UAAU,IAAI,yBAAQ;AAE5B,QACG,KAAK,yBAAyB,EAC9B,QAAQ,gBAAY,SAAS,iBAAiB,iCAAiC,EAC/E,YAAY,8DAA8D,EAC1E,mBAAmB,EACnB,SAAS,eAAe,kBAAkB,EAC1C,OAAO,QAAQ,yBAAyB,EACxC,OAAO,QAAQ,yBAAyB,EACxC;AAAA,EACC;AAAA,EACA,kBAAkB,kBAAkB,CAAC;AAAA,EACrC;AACF,EACC;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,cAAc,uCAAuC,EAC5D;AAAA,EACC;AAAA,EACA;AAAA,EACA;AACF,EACC,OAAO,SAAS,+BAA+B,EAC/C;AAAA,EACC,OACE,cACA,YASG;AACH,QAAI,QAAQ,MAAM,QAAQ,IAAI;AAC5B,YAAM,IAAI,sCAAqB,uCAAuC;AAAA,IACxE;AAEA,UAAM,iBAAiB;AAAA,MACrB,WAAW,gBAAgB;AAAA,MAC3B,UAAU,QAAQ,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,MAClD,QAAQ,QAAQ,UAAU;AAAA,MAC1B,cAAc,MAAM;AAAA,QAClB,oBAAI,IAAI;AAAA,UACN,QAAQ,UAAU;AAAA,UAClB,GAAI,QAAQ,gBAAgB,CAAC;AAAA,QAC/B,CAAC;AAAA,MACH;AAAA,MACA,UAAU,QAAQ,YAAY;AAAA,MAC9B,YAAY,QAAQ,cAAc;AAAA,IACpC;AAEA,UAAM,eAAe,CAAC,QAAQ;AAE9B,QAAI,YAAY,eAAe;AAC/B,QAAI,WAAW,eAAe;AAC9B,QAAI,SAAS,eAAe;AAC5B,QAAI,eAAe,eAAe;AAClC,QAAI,WAAW,eAAe;AAC9B,QAAI,aAAa,eAAe;AAEhC,QAAI,cAAc;AAChB,YAAM,UAAU,UAAM,eAAAC;AAAA,QACpB;AAAA,UACE;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,cACP,EAAE,OAAO,cAAc,OAAO,KAAK;AAAA,cACnC,EAAE,OAAO,cAAc,OAAO,KAAK;AAAA,YACrC;AAAA,YACA,SAAS,aAAa,OAAO,IAAI;AAAA,UACnC;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS,aAAa,IAAI,CAAC,gBAAgB;AAAA,cACzC,OAAO;AAAA,cACP,OAAO;AAAA,cACP,UAAU,aAAa,SAAS,UAAU;AAAA,YAC5C,EAAE;AAAA,YACF,cAAc;AAAA,YACd,KAAK;AAAA,YACL,MAAM;AAAA,UACR;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,UACA;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AAAA,QACF;AAAA,QACA;AAAA,UACE,UAAU,MAAM;AACd,oBAAQ,KAAK,CAAC;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAEA,kBAAY,QAAQ;AACpB,iBAAW,QAAQ;AACnB,qBAAgB,QAAQ,gBACtB,eAAe;AACjB,eACE,aAAa,CAAC,KAAK,eAAe;AACpC,iBAAW,QAAQ,QAAQ,YAAY,eAAe,QAAQ;AAC9D,mBAAa,QAAQ,cAAc,eAAe;AAAA,IACpD;AAEA,UAAM,YAAY,kBAAAC,QAAK,QAAQ,QAAQ,IAAI,GAAG,SAAS;AACvD,UAAM,cAAc,kBAAAA,QAAK,SAAS,SAAS;AAE3C,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,WAAW,WAAW,OAAO,SAAS,EAAE;AACpD,YAAQ,IAAI,aAAa;AACzB,YAAQ,IAAI,QAAQ,SAAS,EAAE;AAC/B,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,eAAe;AAAA,EAC7B;AACF;AAEF,KAAK,QAAQ,WAAW,QAAQ,IAAI,EAAE,MAAM,CAAC,UAAmB;AAC9D,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAQ,MAAM,UAAU,OAAO,EAAE;AACjC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["import_node_path","path","prompts","path"]}
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.0",
11
+ version: "0.3.1",
12
12
  description: "Standalone CLI to scaffold StreamFox plugin projects",
13
13
  type: "module",
14
14
  bin: {
@@ -84,22 +84,18 @@ function makePackageJson(name, language, sdkVersion) {
84
84
  const scripts = language === "ts" ? {
85
85
  dev: "tsx watch src/server.ts",
86
86
  build: "tsc -p tsconfig.json",
87
- format: "prettier --write .",
88
- "format:check": "prettier --check .",
89
87
  start: "node dist/server.js",
90
88
  test: "vitest run",
91
89
  typecheck: "tsc --noEmit"
92
90
  } : {
93
91
  dev: "node --watch src/server.js",
94
92
  build: 'echo "No build step for JavaScript template"',
95
- format: "prettier --write .",
96
- "format:check": "prettier --check .",
97
93
  start: "node src/server.js",
98
94
  test: "vitest run"
99
95
  };
100
96
  const normalizedScripts = {
101
97
  ...scripts,
102
- check: language === "ts" ? "npm run format:check && npm run typecheck && npm test && npm run build" : "npm run format:check && npm test && npm run build"
98
+ check: language === "ts" ? "npm run typecheck && npm test && npm run build" : "npm test && npm run build"
103
99
  };
104
100
  const packageJson = {
105
101
  name,
@@ -112,12 +108,10 @@ function makePackageJson(name, language, sdkVersion) {
112
108
  },
113
109
  devDependencies: language === "ts" ? {
114
110
  "@types/node": "^24.6.0",
115
- prettier: "^3.6.2",
116
111
  tsx: "^4.20.5",
117
112
  typescript: "^5.9.2",
118
113
  vitest: "^2.1.9"
119
114
  } : {
120
- prettier: "^3.6.2",
121
115
  vitest: "^2.1.9"
122
116
  }
123
117
  };
@@ -344,15 +338,12 @@ var tsConfig = `{
344
338
  "include": ["src"]
345
339
  }
346
340
  `;
347
- var prettierConfig = `{
348
- "semi": true,
349
- "singleQuote": false,
350
- "trailingComma": "all"
351
- }
352
- `;
353
- var prettierIgnore = `dist
341
+ var gitIgnore = `dist
354
342
  node_modules
355
343
  .DS_Store
344
+ .env
345
+ .env.*
346
+ coverage
356
347
  `;
357
348
  function makeReadme(projectName, capabilities, advanced) {
358
349
  const capabilitiesList = capabilities.map((capability) => `- ${capability}`).join("\n");
@@ -390,9 +381,9 @@ Advanced template: \`${advanced ? "enabled" : "disabled"}\`
390
381
 
391
382
  - npm run dev
392
383
  - npm run build
393
- - npm run format
394
384
  - npm run start
395
385
  - npm run test
386
+ - npm run check
396
387
 
397
388
  ## Implemented Capabilities
398
389
 
@@ -425,14 +416,7 @@ async function scaffoldProject(options) {
425
416
  path.join(options.targetDir, "package.json"),
426
417
  makePackageJson(options.projectName, options.language, sdkVersion)
427
418
  );
428
- await writeFile(
429
- path.join(options.targetDir, ".prettierrc.json"),
430
- prettierConfig
431
- );
432
- await writeFile(
433
- path.join(options.targetDir, ".prettierignore"),
434
- prettierIgnore
435
- );
419
+ await writeFile(path.join(options.targetDir, ".gitignore"), gitIgnore);
436
420
  await writeFile(
437
421
  path.join(options.targetDir, "README.md"),
438
422
  makeReadme(options.projectName, capabilities, advanced)
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.0\",\n \"description\": \"Standalone CLI to scaffold StreamFox plugin projects\",\n \"type\": \"module\",\n \"bin\": {\n \"create-streamfox-plugin\": \"dist/cli.cjs\"\n },\n \"main\": \"./dist/index.cjs\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\",\n \"require\": \"./dist/index.cjs\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"format\": \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n \"test\": \"vitest run\",\n \"typecheck\": \"tsc --noEmit\",\n \"check\": \"npm run format:check && npm run typecheck && npm test && npm run build\"\n },\n \"dependencies\": {\n \"commander\": \"^12.1.0\",\n \"prompts\": \"^2.4.2\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^24.6.0\",\n \"@types/prompts\": \"^2.4.9\",\n \"prettier\": \"^3.6.2\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.9.2\",\n \"vitest\": \"^2.1.9\"\n },\n \"engines\": {\n \"node\": \">=20\"\n }\n}\n","import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const CAPABILITIES = [\n \"catalog\",\n \"meta\",\n \"stream\",\n \"subtitles\",\n \"plugin_catalog\",\n] as const;\nexport type Capability = (typeof CAPABILITIES)[number];\nexport type Preset = Capability;\nexport type Language = \"ts\" | \"js\";\nexport const DEFAULT_PRESET: Preset = \"meta\";\nexport const DEFAULT_SDK_VERSION = \"^0.2.0\";\n\nexport interface ScaffoldOptions {\n targetDir: string;\n projectName: string;\n language: Language;\n preset?: Preset;\n 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 format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node dist/server.js\",\n test: \"vitest run\",\n typecheck: \"tsc --noEmit\",\n }\n : {\n dev: \"node --watch src/server.js\",\n build: 'echo \"No build step for JavaScript template\"',\n format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node src/server.js\",\n test: \"vitest run\",\n };\n\n const normalizedScripts = {\n ...scripts,\n check:\n language === \"ts\"\n ? \"npm run format:check && npm run typecheck && npm test && npm run build\"\n : \"npm run format:check && npm test && npm run build\",\n };\n\n const packageJson = {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: normalizedScripts,\n dependencies: {\n \"@streamfox/plugin-sdk\": sdkVersion,\n },\n devDependencies:\n language === \"ts\"\n ? {\n \"@types/node\": \"^24.6.0\",\n prettier: \"^3.6.2\",\n tsx: \"^4.20.5\",\n typescript: \"^5.9.2\",\n vitest: \"^2.1.9\",\n }\n : {\n prettier: \"^3.6.2\",\n vitest: \"^2.1.9\",\n },\n };\n\n return `${JSON.stringify(packageJson, null, 2)}\\n`;\n}\n\nfunction resourceBlock(capability: Capability, advanced: boolean): string {\n switch (capability) {\n case \"catalog\":\n return `catalog: {\n endpoints: [\n {\n id: \"top\",\n name: \"Top\",\n mediaTypes: [\"movie\"],\n filters: [{ key: \"genre\", valueType: \"string\" }],\n },\n ],\n handler: async () => ({\n items: [],\n }),\n },`;\n case \"meta\":\n return `meta: {\n mediaTypes: [\"movie\"],\n includes: [\"videos\", \"links\"],\n handler: async () => ({\n item: ${\n advanced\n ? `{\n summary: {\n id: { namespace: \"imdb\", value: \"tt1254207\" },\n mediaType: \"movie\",\n title: \"Big Buck Bunny\",\n links: [],\n },\n defaultVideoID: \"main\",\n trailers: [{ transport: { kind: \"youtube\", id: \"aqz-KE-bpKQ\" } }],\n videos: [\n {\n id: \"main\",\n title: \"Main\",\n streams: [{ transport: { kind: \"http\", url: \"https://example.com/video.mp4\" } }],\n },\n ],\n }`\n : \"null\"\n },\n }),\n },`;\n case \"stream\":\n return `stream: {\n mediaTypes: [\"movie\"],\n supportedTransports: ${advanced ? `[\"http\", \"torrent\", \"usenet\", \"archive\", \"youtube\"]` : `[\"http\"]`},\n handler: async () => ({\n streams: [\n {\n transport: { kind: \"http\", url: \"https://example.com/video.mp4\", mode: \"stream\" },\n hints: {\n notWebReady: true,\n proxyHeaders: { request: { \"User-Agent\": \"StreamFox\" } },\n },\n },\n${\n advanced\n ? ` {\n transport: { kind: \"torrent\", infoHash: \"abcdef\", peerDiscovery: [\"tracker:udp://tracker.example.com:80\"] },\n selection: { fileIndex: 0 },\n },\n {\n transport: { kind: \"usenet\", nzbURL: \"https://example.com/file.nzb\", servers: [\"nntps://user:pass@news.example.com:563/4\"] },\n },\n {\n transport: {\n kind: \"archive\",\n format: \"zip\",\n files: [{ url: \"https://example.com/archive.zip\", bytes: 1024 }],\n },\n selection: { fileMustInclude: \"movie.mkv\" },\n },\n`\n : \"\"\n} ],\n }),\n },`;\n case \"subtitles\":\n return `subtitles: {\n mediaTypes: [\"movie\", \"episode\"],\n defaultLanguages: [\"en\"],\n handler: async (request, { settings }) => {\n const configuredLanguages = Array.isArray(settings.languages)\n ? settings.languages\n : [];\n const languagePreferences =\n configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);\n\n void languagePreferences;\n void settings.includeHI;\n\n return {\n subtitles: [],\n };\n },\n },`;\n case \"plugin_catalog\":\n return `pluginCatalog: {\n endpoints: [\n {\n id: \"featured\",\n name: \"Featured\",\n pluginKinds: [\"catalog\", \"meta\", \"stream\", \"subtitles\"],\n tags: [\"official\"],\n },\n ],\n handler: async () => ({\n plugins: [\n {\n id: \"com.example.recommended\",\n name: \"Recommended\",\n version: \"1.0.0\",\n pluginKinds: [\"catalog\", \"meta\"],\n distribution: {\n transport: \"https\",\n manifestURL: \"https://plugins.example.com/recommended/manifest\",\n },\n manifestSnapshot: {\n plugin: { id: \"com.example.recommended\" },\n },\n },\n ],\n }),\n },`;\n default:\n return \"\";\n }\n}\n\nfunction makeInstallBlock(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 prettierConfig = `{\n \"semi\": true,\n \"singleQuote\": false,\n \"trailingComma\": \"all\"\n}\n`;\n\nconst prettierIgnore = `dist\nnode_modules\n.DS_Store\n`;\n\nfunction makeReadme(\n projectName: string,\n 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 format\n- npm run start\n- npm run test\n\n## Implemented Capabilities\n\n${capabilitiesList}\n\n## Stream Model\n\n- Unified transport model via \\`stream.transport\\`\n- Capability declaration via \\`resources.stream.supportedTransports\\`\n- Optional selection controls via \\`stream.selection\\`\n\n## Endpoints\n\n${endpointLines}\n`;\n}\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<void> {\n const capabilities = 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(\n path.join(options.targetDir, \".prettierrc.json\"),\n prettierConfig,\n );\n await writeFile(\n path.join(options.targetDir, \".prettierignore\"),\n prettierIgnore,\n );\n await writeFile(\n path.join(options.targetDir, \"README.md\"),\n makeReadme(options.projectName, 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.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"]}
package/dist/index.cjs CHANGED
@@ -69,22 +69,18 @@ function makePackageJson(name, language, sdkVersion) {
69
69
  const scripts = language === "ts" ? {
70
70
  dev: "tsx watch src/server.ts",
71
71
  build: "tsc -p tsconfig.json",
72
- format: "prettier --write .",
73
- "format:check": "prettier --check .",
74
72
  start: "node dist/server.js",
75
73
  test: "vitest run",
76
74
  typecheck: "tsc --noEmit"
77
75
  } : {
78
76
  dev: "node --watch src/server.js",
79
77
  build: 'echo "No build step for JavaScript template"',
80
- format: "prettier --write .",
81
- "format:check": "prettier --check .",
82
78
  start: "node src/server.js",
83
79
  test: "vitest run"
84
80
  };
85
81
  const normalizedScripts = {
86
82
  ...scripts,
87
- check: language === "ts" ? "npm run format:check && npm run typecheck && npm test && npm run build" : "npm run format:check && npm test && npm run build"
83
+ check: language === "ts" ? "npm run typecheck && npm test && npm run build" : "npm test && npm run build"
88
84
  };
89
85
  const packageJson = {
90
86
  name,
@@ -97,12 +93,10 @@ function makePackageJson(name, language, sdkVersion) {
97
93
  },
98
94
  devDependencies: language === "ts" ? {
99
95
  "@types/node": "^24.6.0",
100
- prettier: "^3.6.2",
101
96
  tsx: "^4.20.5",
102
97
  typescript: "^5.9.2",
103
98
  vitest: "^2.1.9"
104
99
  } : {
105
- prettier: "^3.6.2",
106
100
  vitest: "^2.1.9"
107
101
  }
108
102
  };
@@ -329,15 +323,12 @@ var tsConfig = `{
329
323
  "include": ["src"]
330
324
  }
331
325
  `;
332
- var prettierConfig = `{
333
- "semi": true,
334
- "singleQuote": false,
335
- "trailingComma": "all"
336
- }
337
- `;
338
- var prettierIgnore = `dist
326
+ var gitIgnore = `dist
339
327
  node_modules
340
328
  .DS_Store
329
+ .env
330
+ .env.*
331
+ coverage
341
332
  `;
342
333
  function makeReadme(projectName, capabilities, advanced) {
343
334
  const capabilitiesList = capabilities.map((capability) => `- ${capability}`).join("\n");
@@ -375,9 +366,9 @@ Advanced template: \`${advanced ? "enabled" : "disabled"}\`
375
366
 
376
367
  - npm run dev
377
368
  - npm run build
378
- - npm run format
379
369
  - npm run start
380
370
  - npm run test
371
+ - npm run check
381
372
 
382
373
  ## Implemented Capabilities
383
374
 
@@ -410,14 +401,7 @@ async function scaffoldProject(options) {
410
401
  import_node_path.default.join(options.targetDir, "package.json"),
411
402
  makePackageJson(options.projectName, options.language, sdkVersion)
412
403
  );
413
- await (0, import_promises.writeFile)(
414
- import_node_path.default.join(options.targetDir, ".prettierrc.json"),
415
- prettierConfig
416
- );
417
- await (0, import_promises.writeFile)(
418
- import_node_path.default.join(options.targetDir, ".prettierignore"),
419
- prettierIgnore
420
- );
404
+ await (0, import_promises.writeFile)(import_node_path.default.join(options.targetDir, ".gitignore"), gitIgnore);
421
405
  await (0, import_promises.writeFile)(
422
406
  import_node_path.default.join(options.targetDir, "README.md"),
423
407
  makeReadme(options.projectName, capabilities, advanced)
@@ -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 format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node dist/server.js\",\n test: \"vitest run\",\n typecheck: \"tsc --noEmit\",\n }\n : {\n dev: \"node --watch src/server.js\",\n build: 'echo \"No build step for JavaScript template\"',\n format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node src/server.js\",\n test: \"vitest run\",\n };\n\n const normalizedScripts = {\n ...scripts,\n check:\n language === \"ts\"\n ? \"npm run format:check && npm run typecheck && npm test && npm run build\"\n : \"npm run format:check && npm test && npm run build\",\n };\n\n const packageJson = {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: normalizedScripts,\n dependencies: {\n \"@streamfox/plugin-sdk\": sdkVersion,\n },\n devDependencies:\n language === \"ts\"\n ? {\n \"@types/node\": \"^24.6.0\",\n prettier: \"^3.6.2\",\n tsx: \"^4.20.5\",\n typescript: \"^5.9.2\",\n vitest: \"^2.1.9\",\n }\n : {\n prettier: \"^3.6.2\",\n vitest: \"^2.1.9\",\n },\n };\n\n return `${JSON.stringify(packageJson, null, 2)}\\n`;\n}\n\nfunction resourceBlock(capability: Capability, advanced: boolean): string {\n switch (capability) {\n case \"catalog\":\n return `catalog: {\n endpoints: [\n {\n id: \"top\",\n name: \"Top\",\n mediaTypes: [\"movie\"],\n filters: [{ key: \"genre\", valueType: \"string\" }],\n },\n ],\n handler: async () => ({\n items: [],\n }),\n },`;\n case \"meta\":\n return `meta: {\n mediaTypes: [\"movie\"],\n includes: [\"videos\", \"links\"],\n handler: async () => ({\n item: ${\n advanced\n ? `{\n summary: {\n id: { namespace: \"imdb\", value: \"tt1254207\" },\n mediaType: \"movie\",\n title: \"Big Buck Bunny\",\n links: [],\n },\n defaultVideoID: \"main\",\n trailers: [{ transport: { kind: \"youtube\", id: \"aqz-KE-bpKQ\" } }],\n videos: [\n {\n id: \"main\",\n title: \"Main\",\n streams: [{ transport: { kind: \"http\", url: \"https://example.com/video.mp4\" } }],\n },\n ],\n }`\n : \"null\"\n },\n }),\n },`;\n case \"stream\":\n return `stream: {\n mediaTypes: [\"movie\"],\n supportedTransports: ${advanced ? `[\"http\", \"torrent\", \"usenet\", \"archive\", \"youtube\"]` : `[\"http\"]`},\n handler: async () => ({\n streams: [\n {\n transport: { kind: \"http\", url: \"https://example.com/video.mp4\", mode: \"stream\" },\n hints: {\n notWebReady: true,\n proxyHeaders: { request: { \"User-Agent\": \"StreamFox\" } },\n },\n },\n${\n advanced\n ? ` {\n transport: { kind: \"torrent\", infoHash: \"abcdef\", peerDiscovery: [\"tracker:udp://tracker.example.com:80\"] },\n selection: { fileIndex: 0 },\n },\n {\n transport: { kind: \"usenet\", nzbURL: \"https://example.com/file.nzb\", servers: [\"nntps://user:pass@news.example.com:563/4\"] },\n },\n {\n transport: {\n kind: \"archive\",\n format: \"zip\",\n files: [{ url: \"https://example.com/archive.zip\", bytes: 1024 }],\n },\n selection: { fileMustInclude: \"movie.mkv\" },\n },\n`\n : \"\"\n} ],\n }),\n },`;\n case \"subtitles\":\n return `subtitles: {\n mediaTypes: [\"movie\", \"episode\"],\n defaultLanguages: [\"en\"],\n handler: async (request, { settings }) => {\n const configuredLanguages = Array.isArray(settings.languages)\n ? settings.languages\n : [];\n const languagePreferences =\n configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);\n\n void languagePreferences;\n void settings.includeHI;\n\n return {\n subtitles: [],\n };\n },\n },`;\n case \"plugin_catalog\":\n return `pluginCatalog: {\n endpoints: [\n {\n id: \"featured\",\n name: \"Featured\",\n pluginKinds: [\"catalog\", \"meta\", \"stream\", \"subtitles\"],\n tags: [\"official\"],\n },\n ],\n handler: async () => ({\n plugins: [\n {\n id: \"com.example.recommended\",\n name: \"Recommended\",\n version: \"1.0.0\",\n pluginKinds: [\"catalog\", \"meta\"],\n distribution: {\n transport: \"https\",\n manifestURL: \"https://plugins.example.com/recommended/manifest\",\n },\n manifestSnapshot: {\n plugin: { id: \"com.example.recommended\" },\n },\n },\n ],\n }),\n },`;\n default:\n return \"\";\n }\n}\n\nfunction makeInstallBlock(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 prettierConfig = `{\n \"semi\": true,\n \"singleQuote\": false,\n \"trailingComma\": \"all\"\n}\n`;\n\nconst prettierIgnore = `dist\nnode_modules\n.DS_Store\n`;\n\nfunction makeReadme(\n projectName: string,\n 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 format\n- npm run start\n- npm run test\n\n## Implemented Capabilities\n\n${capabilitiesList}\n\n## Stream Model\n\n- Unified transport model via \\`stream.transport\\`\n- Capability declaration via \\`resources.stream.supportedTransports\\`\n- Optional selection controls via \\`stream.selection\\`\n\n## Endpoints\n\n${endpointLines}\n`;\n}\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<void> {\n const capabilities = 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(\n path.join(options.targetDir, \".prettierrc.json\"),\n prettierConfig,\n );\n await writeFile(\n path.join(options.targetDir, \".prettierignore\"),\n prettierIgnore,\n );\n await writeFile(\n path.join(options.targetDir, \"README.md\"),\n makeReadme(options.projectName, 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.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"]}
package/dist/index.js CHANGED
@@ -31,22 +31,18 @@ function makePackageJson(name, language, sdkVersion) {
31
31
  const scripts = language === "ts" ? {
32
32
  dev: "tsx watch src/server.ts",
33
33
  build: "tsc -p tsconfig.json",
34
- format: "prettier --write .",
35
- "format:check": "prettier --check .",
36
34
  start: "node dist/server.js",
37
35
  test: "vitest run",
38
36
  typecheck: "tsc --noEmit"
39
37
  } : {
40
38
  dev: "node --watch src/server.js",
41
39
  build: 'echo "No build step for JavaScript template"',
42
- format: "prettier --write .",
43
- "format:check": "prettier --check .",
44
40
  start: "node src/server.js",
45
41
  test: "vitest run"
46
42
  };
47
43
  const normalizedScripts = {
48
44
  ...scripts,
49
- check: language === "ts" ? "npm run format:check && npm run typecheck && npm test && npm run build" : "npm run format:check && npm test && npm run build"
45
+ check: language === "ts" ? "npm run typecheck && npm test && npm run build" : "npm test && npm run build"
50
46
  };
51
47
  const packageJson = {
52
48
  name,
@@ -59,12 +55,10 @@ function makePackageJson(name, language, sdkVersion) {
59
55
  },
60
56
  devDependencies: language === "ts" ? {
61
57
  "@types/node": "^24.6.0",
62
- prettier: "^3.6.2",
63
58
  tsx: "^4.20.5",
64
59
  typescript: "^5.9.2",
65
60
  vitest: "^2.1.9"
66
61
  } : {
67
- prettier: "^3.6.2",
68
62
  vitest: "^2.1.9"
69
63
  }
70
64
  };
@@ -291,15 +285,12 @@ var tsConfig = `{
291
285
  "include": ["src"]
292
286
  }
293
287
  `;
294
- var prettierConfig = `{
295
- "semi": true,
296
- "singleQuote": false,
297
- "trailingComma": "all"
298
- }
299
- `;
300
- var prettierIgnore = `dist
288
+ var gitIgnore = `dist
301
289
  node_modules
302
290
  .DS_Store
291
+ .env
292
+ .env.*
293
+ coverage
303
294
  `;
304
295
  function makeReadme(projectName, capabilities, advanced) {
305
296
  const capabilitiesList = capabilities.map((capability) => `- ${capability}`).join("\n");
@@ -337,9 +328,9 @@ Advanced template: \`${advanced ? "enabled" : "disabled"}\`
337
328
 
338
329
  - npm run dev
339
330
  - npm run build
340
- - npm run format
341
331
  - npm run start
342
332
  - npm run test
333
+ - npm run check
343
334
 
344
335
  ## Implemented Capabilities
345
336
 
@@ -372,14 +363,7 @@ async function scaffoldProject(options) {
372
363
  path.join(options.targetDir, "package.json"),
373
364
  makePackageJson(options.projectName, options.language, sdkVersion)
374
365
  );
375
- await writeFile(
376
- path.join(options.targetDir, ".prettierrc.json"),
377
- prettierConfig
378
- );
379
- await writeFile(
380
- path.join(options.targetDir, ".prettierignore"),
381
- prettierIgnore
382
- );
366
+ await writeFile(path.join(options.targetDir, ".gitignore"), gitIgnore);
383
367
  await writeFile(
384
368
  path.join(options.targetDir, "README.md"),
385
369
  makeReadme(options.projectName, capabilities, advanced)
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 format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node dist/server.js\",\n test: \"vitest run\",\n typecheck: \"tsc --noEmit\",\n }\n : {\n dev: \"node --watch src/server.js\",\n build: 'echo \"No build step for JavaScript template\"',\n format: \"prettier --write .\",\n \"format:check\": \"prettier --check .\",\n start: \"node src/server.js\",\n test: \"vitest run\",\n };\n\n const normalizedScripts = {\n ...scripts,\n check:\n language === \"ts\"\n ? \"npm run format:check && npm run typecheck && npm test && npm run build\"\n : \"npm run format:check && npm test && npm run build\",\n };\n\n const packageJson = {\n name,\n version: \"0.1.0\",\n private: true,\n type: \"module\",\n scripts: normalizedScripts,\n dependencies: {\n \"@streamfox/plugin-sdk\": sdkVersion,\n },\n devDependencies:\n language === \"ts\"\n ? {\n \"@types/node\": \"^24.6.0\",\n prettier: \"^3.6.2\",\n tsx: \"^4.20.5\",\n typescript: \"^5.9.2\",\n vitest: \"^2.1.9\",\n }\n : {\n prettier: \"^3.6.2\",\n vitest: \"^2.1.9\",\n },\n };\n\n return `${JSON.stringify(packageJson, null, 2)}\\n`;\n}\n\nfunction resourceBlock(capability: Capability, advanced: boolean): string {\n switch (capability) {\n case \"catalog\":\n return `catalog: {\n endpoints: [\n {\n id: \"top\",\n name: \"Top\",\n mediaTypes: [\"movie\"],\n filters: [{ key: \"genre\", valueType: \"string\" }],\n },\n ],\n handler: async () => ({\n items: [],\n }),\n },`;\n case \"meta\":\n return `meta: {\n mediaTypes: [\"movie\"],\n includes: [\"videos\", \"links\"],\n handler: async () => ({\n item: ${\n advanced\n ? `{\n summary: {\n id: { namespace: \"imdb\", value: \"tt1254207\" },\n mediaType: \"movie\",\n title: \"Big Buck Bunny\",\n links: [],\n },\n defaultVideoID: \"main\",\n trailers: [{ transport: { kind: \"youtube\", id: \"aqz-KE-bpKQ\" } }],\n videos: [\n {\n id: \"main\",\n title: \"Main\",\n streams: [{ transport: { kind: \"http\", url: \"https://example.com/video.mp4\" } }],\n },\n ],\n }`\n : \"null\"\n },\n }),\n },`;\n case \"stream\":\n return `stream: {\n mediaTypes: [\"movie\"],\n supportedTransports: ${advanced ? `[\"http\", \"torrent\", \"usenet\", \"archive\", \"youtube\"]` : `[\"http\"]`},\n handler: async () => ({\n streams: [\n {\n transport: { kind: \"http\", url: \"https://example.com/video.mp4\", mode: \"stream\" },\n hints: {\n notWebReady: true,\n proxyHeaders: { request: { \"User-Agent\": \"StreamFox\" } },\n },\n },\n${\n advanced\n ? ` {\n transport: { kind: \"torrent\", infoHash: \"abcdef\", peerDiscovery: [\"tracker:udp://tracker.example.com:80\"] },\n selection: { fileIndex: 0 },\n },\n {\n transport: { kind: \"usenet\", nzbURL: \"https://example.com/file.nzb\", servers: [\"nntps://user:pass@news.example.com:563/4\"] },\n },\n {\n transport: {\n kind: \"archive\",\n format: \"zip\",\n files: [{ url: \"https://example.com/archive.zip\", bytes: 1024 }],\n },\n selection: { fileMustInclude: \"movie.mkv\" },\n },\n`\n : \"\"\n} ],\n }),\n },`;\n case \"subtitles\":\n return `subtitles: {\n mediaTypes: [\"movie\", \"episode\"],\n defaultLanguages: [\"en\"],\n handler: async (request, { settings }) => {\n const configuredLanguages = Array.isArray(settings.languages)\n ? settings.languages\n : [];\n const languagePreferences =\n configuredLanguages.length > 0 ? configuredLanguages : (request.languagePreferences ?? []);\n\n void languagePreferences;\n void settings.includeHI;\n\n return {\n subtitles: [],\n };\n },\n },`;\n case \"plugin_catalog\":\n return `pluginCatalog: {\n endpoints: [\n {\n id: \"featured\",\n name: \"Featured\",\n pluginKinds: [\"catalog\", \"meta\", \"stream\", \"subtitles\"],\n tags: [\"official\"],\n },\n ],\n handler: async () => ({\n plugins: [\n {\n id: \"com.example.recommended\",\n name: \"Recommended\",\n version: \"1.0.0\",\n pluginKinds: [\"catalog\", \"meta\"],\n distribution: {\n transport: \"https\",\n manifestURL: \"https://plugins.example.com/recommended/manifest\",\n },\n manifestSnapshot: {\n plugin: { id: \"com.example.recommended\" },\n },\n },\n ],\n }),\n },`;\n default:\n return \"\";\n }\n}\n\nfunction makeInstallBlock(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 prettierConfig = `{\n \"semi\": true,\n \"singleQuote\": false,\n \"trailingComma\": \"all\"\n}\n`;\n\nconst prettierIgnore = `dist\nnode_modules\n.DS_Store\n`;\n\nfunction makeReadme(\n projectName: string,\n 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 format\n- npm run start\n- npm run test\n\n## Implemented Capabilities\n\n${capabilitiesList}\n\n## Stream Model\n\n- Unified transport model via \\`stream.transport\\`\n- Capability declaration via \\`resources.stream.supportedTransports\\`\n- Optional selection controls via \\`stream.selection\\`\n\n## Endpoints\n\n${endpointLines}\n`;\n}\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<void> {\n const capabilities = 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(\n path.join(options.targetDir, \".prettierrc.json\"),\n prettierConfig,\n );\n await writeFile(\n path.join(options.targetDir, \".prettierignore\"),\n prettierIgnore,\n );\n await writeFile(\n path.join(options.targetDir, \"README.md\"),\n makeReadme(options.projectName, 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.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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamfox/create-streamfox-plugin",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Standalone CLI to scaffold StreamFox plugin projects",
5
5
  "type": "module",
6
6
  "bin": {