@remcostoeten/create-analytics 0.1.0 → 0.1.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.
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
+ "use strict";
2
3
 
3
4
  // src/cli.ts
4
- import { parseArgs } from "util";
5
- import { resolve } from "path";
6
- import { access } from "fs/promises";
5
+ var import_node_util = require("util");
6
+ var import_node_path2 = require("path");
7
+ var import_promises3 = require("fs/promises");
7
8
 
8
9
  // src/scaffold.ts
9
- import { mkdir, writeFile } from "fs/promises";
10
- import { dirname, join } from "path";
10
+ var import_promises = require("fs/promises");
11
+ var import_node_path = require("path");
11
12
 
12
13
  // src/templates.ts
13
14
  function webLayout(projectId) {
@@ -382,24 +383,24 @@ async function scaffoldProject(options) {
382
383
  const files = buildFiles(options);
383
384
  const written = [];
384
385
  for (const file of files) {
385
- const fullPath = join(options.targetDir, file.path);
386
- await mkdir(dirname(fullPath), { recursive: true });
387
- await writeFile(fullPath, file.content, "utf8");
386
+ const fullPath = (0, import_node_path.join)(options.targetDir, file.path);
387
+ await (0, import_promises.mkdir)((0, import_node_path.dirname)(fullPath), { recursive: true });
388
+ await (0, import_promises.writeFile)(fullPath, file.content, "utf8");
388
389
  written.push(file.path);
389
390
  }
390
391
  return written;
391
392
  }
392
393
 
393
394
  // src/prompt.ts
394
- import { createInterface } from "readline/promises";
395
- import { stdin as input, stdout as output } from "process";
395
+ var import_promises2 = require("readline/promises");
396
+ var import_node_process = require("process");
396
397
  var tierLabels = {
397
398
  separate: "Separate analytics-api project (recommended)",
398
399
  colocated: "API route in this app (larger server bundle)",
399
400
  "sdk-only": "SDK only \u2014 I already have an ingestion URL"
400
401
  };
401
402
  async function promptTier() {
402
- const rl = createInterface({ input, output });
403
+ const rl = (0, import_promises2.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
403
404
  console.log("\nIntegration tier:\n");
404
405
  const tiers = ["separate", "colocated", "sdk-only"];
405
406
  for (let i = 0; i < tiers.length; i++) {
@@ -413,7 +414,7 @@ async function promptTier() {
413
414
  return "separate";
414
415
  }
415
416
  async function promptProjectName(defaultName) {
416
- const rl = createInterface({ input, output });
417
+ const rl = (0, import_promises2.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
417
418
  const answer = await rl.question(`Project name [${defaultName}]: `);
418
419
  rl.close();
419
420
  const name = answer.trim() || defaultName;
@@ -447,7 +448,7 @@ Tiers:
447
448
  }
448
449
  async function directoryEmpty(dir) {
449
450
  try {
450
- await access(dir);
451
+ await (0, import_promises3.access)(dir);
451
452
  return false;
452
453
  } catch {
453
454
  return true;
@@ -466,7 +467,7 @@ async function resolveOptions(args, positionalName) {
466
467
  projectName = await promptProjectName(defaultName);
467
468
  }
468
469
  }
469
- const targetDir = resolve(process.cwd(), projectName);
470
+ const targetDir = (0, import_node_path2.resolve)(process.cwd(), projectName);
470
471
  if (!await directoryEmpty(targetDir)) {
471
472
  console.error(`Error: "${projectName}" already exists. Choose a different name or empty the folder.`);
472
473
  process.exit(1);
@@ -479,7 +480,7 @@ async function resolveOptions(args, positionalName) {
479
480
  };
480
481
  }
481
482
  async function main() {
482
- const { values, positionals } = parseArgs({
483
+ const { values, positionals } = (0, import_node_util.parseArgs)({
483
484
  args: process.argv.slice(2),
484
485
  options: {
485
486
  tier: { type: "string" },
@@ -523,4 +524,4 @@ main().catch(function(error) {
523
524
  console.error(error instanceof Error ? error.message : error);
524
525
  process.exit(1);
525
526
  });
526
- //# sourceMappingURL=cli.js.map
527
+ //# sourceMappingURL=cli.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts","../src/scaffold.ts","../src/templates.ts","../src/prompt.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { resolve } from \"node:path\";\nimport { access } from \"node:fs/promises\";\nimport { scaffoldProject } from \"./scaffold\";\nimport { parseTier, promptProjectName, promptTier } from \"./prompt\";\nimport { type Tier } from \"./types\";\n\ntype CliArgs = {\n\tprojectName?: string;\n\ttier?: string;\n\tyes?: boolean;\n};\n\nfunction printUsage(): void {\n\tconsole.log(`\ncreate-analytics — scaffold Remco Analytics\n\nUsage:\n npx create-analytics [project-name] [options]\n\nOptions:\n --tier <separate|colocated|sdk-only> Integration tier (default: separate)\n --yes Skip prompts when tier is set\n -h, --help Show help\n\nTiers:\n separate SDK in apps/web, ingestion in apps/analytics-api (recommended)\n colocated SDK + ingestion API route in one Next.js app\n sdk-only SDK only, existing ingestion URL\n`);\n}\n\nasync function directoryEmpty(dir: string): Promise<boolean> {\n\ttry {\n\t\tawait access(dir);\n\t\treturn false;\n\t} catch {\n\t\treturn true;\n\t}\n}\n\nasync function resolveOptions(args: CliArgs, positionalName?: string): Promise<{\n\tprojectName: string;\n\tprojectId: string;\n\ttier: Tier;\n\ttargetDir: string;\n}> {\n\tconst defaultName = positionalName?.trim() || \"my-analytics-app\";\n\tlet tier = parseTier(args.tier);\n\tlet projectName = defaultName;\n\n\tif (!tier && !args.yes) {\n\t\ttier = await promptTier();\n\t\tprojectName = await promptProjectName(defaultName);\n\t} else {\n\t\ttier = tier ?? \"separate\";\n\t\tif (!args.yes && !args.tier) {\n\t\t\tprojectName = await promptProjectName(defaultName);\n\t\t}\n\t}\n\n\tconst targetDir = resolve(process.cwd(), projectName);\n\n\tif (!(await directoryEmpty(targetDir))) {\n\t\tconsole.error(`Error: \"${projectName}\" already exists. Choose a different name or empty the folder.`);\n\t\tprocess.exit(1);\n\t}\n\n\treturn {\n\t\tprojectName,\n\t\tprojectId: projectName,\n\t\ttier,\n\t\ttargetDir,\n\t};\n}\n\nasync function main(): Promise<void> {\n\tconst { values, positionals } = parseArgs({\n\t\targs: process.argv.slice(2),\n\t\toptions: {\n\t\t\ttier: { type: \"string\" },\n\t\t\tyes: { type: \"boolean\", short: \"y\" },\n\t\t\thelp: { type: \"boolean\", short: \"h\" },\n\t\t},\n\t\tallowPositionals: true,\n\t});\n\n\tif (values.help) {\n\t\tprintUsage();\n\t\treturn;\n\t}\n\n\tconst options = await resolveOptions(\n\t\t{\n\t\t\ttier: values.tier,\n\t\t\tyes: values.yes,\n\t\t},\n\t\tpositionals[0],\n\t);\n\n\tconsole.log(`\\nScaffolding ${options.projectName} (${options.tier})...\\n`);\n\n\tconst written = await scaffoldProject(options);\n\n\tfor (const file of written) {\n\t\tconsole.log(` created ${file}`);\n\t}\n\n\tconsole.log(`\nDone. Next steps:\n\n cd ${options.projectName}\n cat README.md\n`);\n\n\tif (options.tier === \"separate\") {\n\t\tconsole.log(` Tier 1: deploy apps/analytics-api separately, then set NEXT_PUBLIC_ANALYTICS_URL in apps/web`);\n\t}\n\n\tif (options.tier === \"colocated\") {\n\t\tconsole.log(` Tier 2: adds ingestion deps to this deploy — see README for bundle tradeoff`);\n\t}\n}\n\nmain().catch(function (error) {\n\tconsole.error(error instanceof Error ? error.message : error);\n\tprocess.exit(1);\n});\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { buildFiles } from \"./templates\";\nimport { type ScaffoldOptions } from \"./types\";\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<string[]> {\n\tconst files = buildFiles(options);\n\tconst written: string[] = [];\n\n\tfor (const file of files) {\n\t\tconst fullPath = join(options.targetDir, file.path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, file.content, \"utf8\");\n\t\twritten.push(file.path);\n\t}\n\n\treturn written;\n}\n","import { type ScaffoldFile, type ScaffoldOptions } from \"./types\";\n\nfunction webLayout(projectId: string): string {\n\treturn `import { Analytics } from \"@remcostoeten/analytics\";\n\nexport default function RootLayout({\n\tchildren,\n}: Readonly<{\n\tchildren: React.ReactNode;\n}>) {\n\treturn (\n\t\t<html lang=\"en\">\n\t\t\t<body>\n\t\t\t\t{children}\n\t\t\t\t<Analytics projectId=\"${projectId}\" />\n\t\t\t</body>\n\t\t</html>\n\t);\n}\n`;\n}\n\nfunction webPage(): string {\n\treturn `export default function Home() {\n\treturn (\n\t\t<main>\n\t\t\t<h1>Analytics wired</h1>\n\t\t\t<p>Pageviews track automatically via the Analytics component in layout.tsx.</p>\n\t\t</main>\n\t);\n}\n`;\n}\n\nfunction webEnvExample(ingestUrl: string): string {\n\treturn `# Browser SDK (public)\nNEXT_PUBLIC_ANALYTICS_URL=${ingestUrl}\n\n# Server tracking (private — never use NEXT_PUBLIC_)\nANALYTICS_URL=${ingestUrl}\nINGEST_SECRET=replace-with-a-long-random-secret\n`;\n}\n\nfunction webPackageJson(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tname: \"web\",\n\t\t\tprivate: true,\n\t\t\tscripts: {\n\t\t\t\tdev: \"next dev\",\n\t\t\t\tbuild: \"next build\",\n\t\t\t\tstart: \"next start\",\n\t\t\t},\n\t\t\tdependencies: {\n\t\t\t\t\"@remcostoeten/analytics\": \"^1.5.0\",\n\t\t\t\tnext: \"^15.0.0\",\n\t\t\t\treact: \"^19.0.0\",\n\t\t\t\t\"react-dom\": \"^19.0.0\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"@types/node\": \"^20.0.0\",\n\t\t\t\t\"@types/react\": \"^19.0.0\",\n\t\t\t\ttypescript: \"^5.6.0\",\n\t\t\t},\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction webTsConfig(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tcompilerOptions: {\n\t\t\t\ttarget: \"ES2022\",\n\t\t\t\tlib: [\"dom\", \"dom.iterable\", \"esnext\"],\n\t\t\t\tallowJs: true,\n\t\t\t\tskipLibCheck: true,\n\t\t\t\tstrict: true,\n\t\t\t\tnoEmit: true,\n\t\t\t\tmodule: \"esnext\",\n\t\t\t\tmoduleResolution: \"bundler\",\n\t\t\t\tisolatedModules: true,\n\t\t\t\tjsx: \"preserve\",\n\t\t\t\tincremental: true,\n\t\t\t\tplugins: [{ name: \"next\" }],\n\t\t\t\tpaths: { \"@/*\": [\"./*\"] },\n\t\t\t},\n\t\t\tinclude: [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n\t\t\texclude: [\"node_modules\"],\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction apiPackageJson(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tname: \"analytics-api\",\n\t\t\tprivate: true,\n\t\t\ttype: \"module\",\n\t\t\tdependencies: {\n\t\t\t\t\"@remcostoeten/ingestion\": \"^0.1.0\",\n\t\t\t\t\"@neondatabase/serverless\": \"^0.10.0\",\n\t\t\t\t\"drizzle-orm\": \"^0.36.0\",\n\t\t\t\thono: \"^4.6.0\",\n\t\t\t\t\"ua-parser-js\": \"^2.0.0\",\n\t\t\t\tzod: \"^3.22.0\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"drizzle-kit\": \"^0.31.0\",\n\t\t\t\ttypescript: \"^5.6.0\",\n\t\t\t},\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction apiEnvExample(): string {\n\treturn `DATABASE_URL=postgres://user:password@host/db\nIP_HASH_SECRET=replace-with-at-least-32-characters\nORIGIN_ALLOWLIST=https://your-app.vercel.app\nINGEST_SECRET=replace-with-a-long-random-secret\n`;\n}\n\nfunction apiHandler(): string {\n\treturn `export { default } from \"@remcostoeten/ingestion/vercel\";\n`;\n}\n\nfunction apiVercelJson(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\trewrites: [{ source: \"/(.*)\", destination: \"/api\" }],\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction ingestRoute(): string {\n\treturn `import { app } from \"@remcostoeten/ingestion\";\n\nasync function handle(request: Request) {\n\treturn app.fetch(request);\n}\n\nexport const GET = handle;\nexport const POST = handle;\n`;\n}\n\nfunction serverTrackingExample(projectId: string): string {\n\treturn `import { trackServerEvent } from \"@remcostoeten/analytics/server\";\n\nexport async function POST() {\n\t// your server logic here\n\n\tawait trackServerEvent(\"example_action\", { projectId: \"${projectId}\", path: \"/api/example\" });\n\n\treturn Response.json({ ok: true });\n}\n`;\n}\n\nfunction colocatedPackageJson(projectName: string): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tname: projectName,\n\t\t\tprivate: true,\n\t\t\tscripts: {\n\t\t\t\tdev: \"next dev\",\n\t\t\t\tbuild: \"next build\",\n\t\t\t\tstart: \"next start\",\n\t\t\t},\n\t\t\tdependencies: {\n\t\t\t\t\"@remcostoeten/analytics\": \"^1.5.0\",\n\t\t\t\t\"@remcostoeten/ingestion\": \"^0.1.0\",\n\t\t\t\t\"@neondatabase/serverless\": \"^0.10.0\",\n\t\t\t\t\"drizzle-orm\": \"^0.36.0\",\n\t\t\t\thono: \"^4.6.0\",\n\t\t\t\tnext: \"^15.0.0\",\n\t\t\t\treact: \"^19.0.0\",\n\t\t\t\t\"react-dom\": \"^19.0.0\",\n\t\t\t\t\"ua-parser-js\": \"^2.0.0\",\n\t\t\t\tzod: \"^3.22.0\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"@types/node\": \"^20.0.0\",\n\t\t\t\t\"@types/react\": \"^19.0.0\",\n\t\t\t\t\"drizzle-kit\": \"^0.31.0\",\n\t\t\t\ttypescript: \"^5.6.0\",\n\t\t\t},\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction colocatedEnvExample(): string {\n\treturn `NEXT_PUBLIC_ANALYTICS_URL=http://localhost:3000\nANALYTICS_URL=http://localhost:3000\nINGEST_SECRET=replace-with-a-long-random-secret\nORIGIN_ALLOWLIST=http://localhost:3000\nDATABASE_URL=postgres://user:password@host/db\nIP_HASH_SECRET=replace-with-at-least-32-characters\n`;\n}\n\nfunction readmeSeparate(projectName: string, projectId: string): string {\n\treturn `# ${projectName}\n\nTier 1 setup: SDK in \\`apps/web\\`, ingestion in \\`apps/analytics-api\\` (separate deploy).\n\n## 1. Web app\n\n\\`\\`\\`bash\ncd apps/web\nnpm install\ncp .env.example .env.local\nnpm run dev\n\\`\\`\\`\n\n## 2. Analytics API\n\n\\`\\`\\`bash\ncd apps/analytics-api\nnpm install\ncp .env.example .env\n\\`\\`\\`\n\nCreate a Neon database, set \\`DATABASE_URL\\` and \\`IP_HASH_SECRET\\` (min 32 chars).\n\nRun migrations:\n\n\\`\\`\\`bash\nnpx drizzle-kit up:pg --config node_modules/@remcostoeten/ingestion/drizzle.config.ts\n\\`\\`\\`\n\nDeploy \\`apps/analytics-api\\` to Vercel, then set \\`NEXT_PUBLIC_ANALYTICS_URL\\` in the web app to that URL.\n\n## Server-side tracking\n\nFor events that happen on your backend (API routes, webhooks, cron jobs), use the server entry:\n\n\\`\\`\\`typescript\n// apps/web/app/api/example/route.ts\nimport { trackServerEvent } from \"@remcostoeten/analytics/server\";\n\nexport async function POST() {\n await trackServerEvent(\"signup_completed\", { projectId: \"${projectId}\", path: \"/api/signup\" });\n return Response.json({ ok: true });\n}\n\\`\\`\\`\n\nSet \\`ANALYTICS_URL\\` and \\`INGEST_SECRET\\` in \\`apps/web/.env.local\\` (server-only — never \\`NEXT_PUBLIC_\\`).\n\nOn the ingestion side, set the same \\`INGEST_SECRET\\` so it can authenticate server requests.\n\n## Project ID\n\nEvents use \\`projectId=\"${projectId}\"\\`. Change in \\`apps/web/app/layout.tsx\\` if needed.\n`;\n}\n\nfunction readmeColocated(projectName: string, projectId: string): string {\n\treturn `# ${projectName}\n\nTier 2 setup: SDK and ingestion in one Next.js app.\n\nWarning: this adds server-side ingestion dependencies to your app deploy (larger serverless bundle).\n\nIngestion routes: \\`app/e/route.ts\\` and \\`app/ingest/route.ts\\` (SDK posts to \\`/e\\`).\n\n## Setup\n\n\\`\\`\\`bash\nnpm install\ncp .env.example .env.local\n\\`\\`\\`\n\nSet \\`DATABASE_URL\\`, \\`IP_HASH_SECRET\\`, and \\`NEXT_PUBLIC_ANALYTICS_URL\\` (your app URL in production).\n\nRun migrations:\n\n\\`\\`\\`bash\nnpx drizzle-kit up:pg --config node_modules/@remcostoeten/ingestion/drizzle.config.ts\n\\`\\`\\`\n\n\\`\\`\\`bash\nnpm run dev\n\\`\\`\\`\n\n## Server-side tracking\n\nFor events that happen in API routes or server actions, use the server entry. See \\`app/api/example/route.ts\\`:\n\n\\`\\`\\`typescript\nimport { trackServerEvent } from \"@remcostoeten/analytics/server\";\n\nexport async function POST() {\n await trackServerEvent(\"signup_completed\", { projectId: \"${projectId}\", path: \"/api/signup\" });\n return Response.json({ ok: true });\n}\n\\`\\`\\`\n\n\\`ANALYTICS_URL\\` and \\`INGEST_SECRET\\` are already in \\`.env.example\\`. Never use \\`NEXT_PUBLIC_\\` for these.\n\n## Project ID\n\nEvents use \\`projectId=\"${projectId}\"\\`. Change in \\`app/layout.tsx\\` if needed.\n`;\n}\n\nfunction readmeSdkOnly(projectName: string, projectId: string): string {\n\treturn `# ${projectName}\n\nTier 3 setup: SDK only. Point at an existing ingestion URL.\n\n## Setup\n\n\\`\\`\\`bash\nnpm install\ncp .env.example .env.local\n\\`\\`\\`\n\nSet \\`NEXT_PUBLIC_ANALYTICS_URL\\` to your ingestion base URL (posts to \\`/e\\`).\n\n\\`\\`\\`bash\nnpm run dev\n\\`\\`\\`\n\n## Project ID\n\nEvents use \\`projectId=\"${projectId}\"\\`. Change in \\`app/layout.tsx\\` if needed.\n`;\n}\n\nexport function buildFiles(options: ScaffoldOptions): ScaffoldFile[] {\n\tconst ingestPlaceholder = \"https://your-analytics-api.vercel.app\";\n\n\tif (options.tier === \"separate\") {\n\t\treturn [\n\t\t\t{ path: \"README.md\", content: readmeSeparate(options.projectName, options.projectId) },\n\t\t\t{ path: \"apps/web/package.json\", content: webPackageJson() },\n\t\t\t{ path: \"apps/web/tsconfig.json\", content: webTsConfig() },\n\t\t\t{ path: \"apps/web/next.config.mjs\", content: \"export default {};\\n\" },\n\t\t\t{ path: \"apps/web/.env.example\", content: webEnvExample(ingestPlaceholder) },\n\t\t\t{ path: \"apps/web/app/layout.tsx\", content: webLayout(options.projectId) },\n\t\t\t{ path: \"apps/web/app/page.tsx\", content: webPage() },\n\t\t\t{ path: \"apps/web/app/api/example/route.ts\", content: serverTrackingExample(options.projectId) },\n\t\t\t{ path: \"apps/analytics-api/package.json\", content: apiPackageJson() },\n\t\t\t{ path: \"apps/analytics-api/api/index.ts\", content: apiHandler() },\n\t\t\t{ path: \"apps/analytics-api/vercel.json\", content: apiVercelJson() },\n\t\t\t{ path: \"apps/analytics-api/.env.example\", content: apiEnvExample() },\n\t\t];\n\t}\n\n\tif (options.tier === \"colocated\") {\n\t\treturn [\n\t\t\t{ path: \"README.md\", content: readmeColocated(options.projectName, options.projectId) },\n\t\t\t{ path: \"package.json\", content: colocatedPackageJson(options.projectName) },\n\t\t\t{ path: \"tsconfig.json\", content: webTsConfig() },\n\t\t\t{ path: \"next.config.mjs\", content: \"export default {};\\n\" },\n\t\t\t{ path: \".env.example\", content: colocatedEnvExample() },\n\t\t\t{ path: \"app/layout.tsx\", content: webLayout(options.projectId) },\n\t\t\t{ path: \"app/page.tsx\", content: webPage() },\n\t\t\t{ path: \"app/e/route.ts\", content: ingestRoute() },\n\t\t\t{ path: \"app/ingest/route.ts\", content: ingestRoute() },\n\t\t\t{ path: \"app/api/example/route.ts\", content: serverTrackingExample(options.projectId) },\n\t\t];\n\t}\n\n\treturn [\n\t\t{ path: \"README.md\", content: readmeSdkOnly(options.projectName, options.projectId) },\n\t\t{ path: \"package.json\", content: webPackageJson().replace('\"name\": \"web\"', `\"name\": \"${options.projectName}\"`) },\n\t\t{ path: \"tsconfig.json\", content: webTsConfig() },\n\t\t{ path: \"next.config.mjs\", content: \"export default {};\\n\" },\n\t\t{ path: \".env.example\", content: webEnvExample(ingestPlaceholder) },\n\t\t{ path: \"app/layout.tsx\", content: webLayout(options.projectId) },\n\t\t{ path: \"app/page.tsx\", content: webPage() },\n\t];\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { stdin as input, stdout as output } from \"node:process\";\nimport { type Tier } from \"./types\";\n\nconst tierLabels: Record<Tier, string> = {\n\tseparate: \"Separate analytics-api project (recommended)\",\n\tcolocated: \"API route in this app (larger server bundle)\",\n\t\"sdk-only\": \"SDK only — I already have an ingestion URL\",\n};\n\nexport async function promptTier(): Promise<Tier> {\n\tconst rl = createInterface({ input, output });\n\n\tconsole.log(\"\\nIntegration tier:\\n\");\n\tconst tiers: Tier[] = [\"separate\", \"colocated\", \"sdk-only\"];\n\tfor (let i = 0; i < tiers.length; i++) {\n\t\tconst marker = i === 0 ? \"→\" : \" \";\n\t\tconsole.log(` ${marker} ${i + 1}. ${tierLabels[tiers[i]]}`);\n\t}\n\n\tconst answer = await rl.question(\"\\nChoose [1]: \");\n\trl.close();\n\n\tconst index = answer.trim() === \"\" ? 0 : Number.parseInt(answer, 10) - 1;\n\tif (index >= 0 && index < tiers.length) return tiers[index];\n\n\treturn \"separate\";\n}\n\nexport async function promptProjectName(defaultName: string): Promise<string> {\n\tconst rl = createInterface({ input, output });\n\tconst answer = await rl.question(`Project name [${defaultName}]: `);\n\trl.close();\n\n\tconst name = answer.trim() || defaultName;\n\treturn name.replace(/[^a-zA-Z0-9-_]/g, \"-\").toLowerCase();\n}\n\nexport function parseTier(value: string | undefined): Tier | null {\n\tif (value === \"separate\" || value === \"1\") return \"separate\";\n\tif (value === \"colocated\" || value === \"2\") return \"colocated\";\n\tif (value === \"sdk-only\" || value === \"sdk\" || value === \"3\") return \"sdk-only\";\n\treturn null;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/cli.ts","../src/scaffold.ts","../src/templates.ts","../src/prompt.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { resolve } from \"node:path\";\nimport { access } from \"node:fs/promises\";\nimport { scaffoldProject } from \"./scaffold\";\nimport { parseTier, promptProjectName, promptTier } from \"./prompt\";\nimport { type Tier } from \"./types\";\n\ntype CliArgs = {\n\tprojectName?: string;\n\ttier?: string;\n\tyes?: boolean;\n};\n\nfunction printUsage(): void {\n\tconsole.log(`\ncreate-analytics — scaffold Remco Analytics\n\nUsage:\n npx create-analytics [project-name] [options]\n\nOptions:\n --tier <separate|colocated|sdk-only> Integration tier (default: separate)\n --yes Skip prompts when tier is set\n -h, --help Show help\n\nTiers:\n separate SDK in apps/web, ingestion in apps/analytics-api (recommended)\n colocated SDK + ingestion API route in one Next.js app\n sdk-only SDK only, existing ingestion URL\n`);\n}\n\nasync function directoryEmpty(dir: string): Promise<boolean> {\n\ttry {\n\t\tawait access(dir);\n\t\treturn false;\n\t} catch {\n\t\treturn true;\n\t}\n}\n\nasync function resolveOptions(args: CliArgs, positionalName?: string): Promise<{\n\tprojectName: string;\n\tprojectId: string;\n\ttier: Tier;\n\ttargetDir: string;\n}> {\n\tconst defaultName = positionalName?.trim() || \"my-analytics-app\";\n\tlet tier = parseTier(args.tier);\n\tlet projectName = defaultName;\n\n\tif (!tier && !args.yes) {\n\t\ttier = await promptTier();\n\t\tprojectName = await promptProjectName(defaultName);\n\t} else {\n\t\ttier = tier ?? \"separate\";\n\t\tif (!args.yes && !args.tier) {\n\t\t\tprojectName = await promptProjectName(defaultName);\n\t\t}\n\t}\n\n\tconst targetDir = resolve(process.cwd(), projectName);\n\n\tif (!(await directoryEmpty(targetDir))) {\n\t\tconsole.error(`Error: \"${projectName}\" already exists. Choose a different name or empty the folder.`);\n\t\tprocess.exit(1);\n\t}\n\n\treturn {\n\t\tprojectName,\n\t\tprojectId: projectName,\n\t\ttier,\n\t\ttargetDir,\n\t};\n}\n\nasync function main(): Promise<void> {\n\tconst { values, positionals } = parseArgs({\n\t\targs: process.argv.slice(2),\n\t\toptions: {\n\t\t\ttier: { type: \"string\" },\n\t\t\tyes: { type: \"boolean\", short: \"y\" },\n\t\t\thelp: { type: \"boolean\", short: \"h\" },\n\t\t},\n\t\tallowPositionals: true,\n\t});\n\n\tif (values.help) {\n\t\tprintUsage();\n\t\treturn;\n\t}\n\n\tconst options = await resolveOptions(\n\t\t{\n\t\t\ttier: values.tier,\n\t\t\tyes: values.yes,\n\t\t},\n\t\tpositionals[0],\n\t);\n\n\tconsole.log(`\\nScaffolding ${options.projectName} (${options.tier})...\\n`);\n\n\tconst written = await scaffoldProject(options);\n\n\tfor (const file of written) {\n\t\tconsole.log(` created ${file}`);\n\t}\n\n\tconsole.log(`\nDone. Next steps:\n\n cd ${options.projectName}\n cat README.md\n`);\n\n\tif (options.tier === \"separate\") {\n\t\tconsole.log(` Tier 1: deploy apps/analytics-api separately, then set NEXT_PUBLIC_ANALYTICS_URL in apps/web`);\n\t}\n\n\tif (options.tier === \"colocated\") {\n\t\tconsole.log(` Tier 2: adds ingestion deps to this deploy — see README for bundle tradeoff`);\n\t}\n}\n\nmain().catch(function (error) {\n\tconsole.error(error instanceof Error ? error.message : error);\n\tprocess.exit(1);\n});\n","import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { buildFiles } from \"./templates\";\nimport { type ScaffoldOptions } from \"./types\";\n\nexport async function scaffoldProject(options: ScaffoldOptions): Promise<string[]> {\n\tconst files = buildFiles(options);\n\tconst written: string[] = [];\n\n\tfor (const file of files) {\n\t\tconst fullPath = join(options.targetDir, file.path);\n\t\tawait mkdir(dirname(fullPath), { recursive: true });\n\t\tawait writeFile(fullPath, file.content, \"utf8\");\n\t\twritten.push(file.path);\n\t}\n\n\treturn written;\n}\n","import { type ScaffoldFile, type ScaffoldOptions } from \"./types\";\n\nfunction webLayout(projectId: string): string {\n\treturn `import { Analytics } from \"@remcostoeten/analytics\";\n\nexport default function RootLayout({\n\tchildren,\n}: Readonly<{\n\tchildren: React.ReactNode;\n}>) {\n\treturn (\n\t\t<html lang=\"en\">\n\t\t\t<body>\n\t\t\t\t{children}\n\t\t\t\t<Analytics projectId=\"${projectId}\" />\n\t\t\t</body>\n\t\t</html>\n\t);\n}\n`;\n}\n\nfunction webPage(): string {\n\treturn `export default function Home() {\n\treturn (\n\t\t<main>\n\t\t\t<h1>Analytics wired</h1>\n\t\t\t<p>Pageviews track automatically via the Analytics component in layout.tsx.</p>\n\t\t</main>\n\t);\n}\n`;\n}\n\nfunction webEnvExample(ingestUrl: string): string {\n\treturn `# Browser SDK (public)\nNEXT_PUBLIC_ANALYTICS_URL=${ingestUrl}\n\n# Server tracking (private — never use NEXT_PUBLIC_)\nANALYTICS_URL=${ingestUrl}\nINGEST_SECRET=replace-with-a-long-random-secret\n`;\n}\n\nfunction webPackageJson(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tname: \"web\",\n\t\t\tprivate: true,\n\t\t\tscripts: {\n\t\t\t\tdev: \"next dev\",\n\t\t\t\tbuild: \"next build\",\n\t\t\t\tstart: \"next start\",\n\t\t\t},\n\t\t\tdependencies: {\n\t\t\t\t\"@remcostoeten/analytics\": \"^1.5.0\",\n\t\t\t\tnext: \"^15.0.0\",\n\t\t\t\treact: \"^19.0.0\",\n\t\t\t\t\"react-dom\": \"^19.0.0\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"@types/node\": \"^20.0.0\",\n\t\t\t\t\"@types/react\": \"^19.0.0\",\n\t\t\t\ttypescript: \"^5.6.0\",\n\t\t\t},\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction webTsConfig(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tcompilerOptions: {\n\t\t\t\ttarget: \"ES2022\",\n\t\t\t\tlib: [\"dom\", \"dom.iterable\", \"esnext\"],\n\t\t\t\tallowJs: true,\n\t\t\t\tskipLibCheck: true,\n\t\t\t\tstrict: true,\n\t\t\t\tnoEmit: true,\n\t\t\t\tmodule: \"esnext\",\n\t\t\t\tmoduleResolution: \"bundler\",\n\t\t\t\tisolatedModules: true,\n\t\t\t\tjsx: \"preserve\",\n\t\t\t\tincremental: true,\n\t\t\t\tplugins: [{ name: \"next\" }],\n\t\t\t\tpaths: { \"@/*\": [\"./*\"] },\n\t\t\t},\n\t\t\tinclude: [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\"],\n\t\t\texclude: [\"node_modules\"],\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction apiPackageJson(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tname: \"analytics-api\",\n\t\t\tprivate: true,\n\t\t\ttype: \"module\",\n\t\t\tdependencies: {\n\t\t\t\t\"@remcostoeten/ingestion\": \"^0.1.0\",\n\t\t\t\t\"@neondatabase/serverless\": \"^0.10.0\",\n\t\t\t\t\"drizzle-orm\": \"^0.36.0\",\n\t\t\t\thono: \"^4.6.0\",\n\t\t\t\t\"ua-parser-js\": \"^2.0.0\",\n\t\t\t\tzod: \"^3.22.0\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"drizzle-kit\": \"^0.31.0\",\n\t\t\t\ttypescript: \"^5.6.0\",\n\t\t\t},\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction apiEnvExample(): string {\n\treturn `DATABASE_URL=postgres://user:password@host/db\nIP_HASH_SECRET=replace-with-at-least-32-characters\nORIGIN_ALLOWLIST=https://your-app.vercel.app\nINGEST_SECRET=replace-with-a-long-random-secret\n`;\n}\n\nfunction apiHandler(): string {\n\treturn `export { default } from \"@remcostoeten/ingestion/vercel\";\n`;\n}\n\nfunction apiVercelJson(): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\trewrites: [{ source: \"/(.*)\", destination: \"/api\" }],\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction ingestRoute(): string {\n\treturn `import { app } from \"@remcostoeten/ingestion\";\n\nasync function handle(request: Request) {\n\treturn app.fetch(request);\n}\n\nexport const GET = handle;\nexport const POST = handle;\n`;\n}\n\nfunction serverTrackingExample(projectId: string): string {\n\treturn `import { trackServerEvent } from \"@remcostoeten/analytics/server\";\n\nexport async function POST() {\n\t// your server logic here\n\n\tawait trackServerEvent(\"example_action\", { projectId: \"${projectId}\", path: \"/api/example\" });\n\n\treturn Response.json({ ok: true });\n}\n`;\n}\n\nfunction colocatedPackageJson(projectName: string): string {\n\treturn JSON.stringify(\n\t\t{\n\t\t\tname: projectName,\n\t\t\tprivate: true,\n\t\t\tscripts: {\n\t\t\t\tdev: \"next dev\",\n\t\t\t\tbuild: \"next build\",\n\t\t\t\tstart: \"next start\",\n\t\t\t},\n\t\t\tdependencies: {\n\t\t\t\t\"@remcostoeten/analytics\": \"^1.5.0\",\n\t\t\t\t\"@remcostoeten/ingestion\": \"^0.1.0\",\n\t\t\t\t\"@neondatabase/serverless\": \"^0.10.0\",\n\t\t\t\t\"drizzle-orm\": \"^0.36.0\",\n\t\t\t\thono: \"^4.6.0\",\n\t\t\t\tnext: \"^15.0.0\",\n\t\t\t\treact: \"^19.0.0\",\n\t\t\t\t\"react-dom\": \"^19.0.0\",\n\t\t\t\t\"ua-parser-js\": \"^2.0.0\",\n\t\t\t\tzod: \"^3.22.0\",\n\t\t\t},\n\t\t\tdevDependencies: {\n\t\t\t\t\"@types/node\": \"^20.0.0\",\n\t\t\t\t\"@types/react\": \"^19.0.0\",\n\t\t\t\t\"drizzle-kit\": \"^0.31.0\",\n\t\t\t\ttypescript: \"^5.6.0\",\n\t\t\t},\n\t\t},\n\t\tnull,\n\t\t\"\\t\",\n\t);\n}\n\nfunction colocatedEnvExample(): string {\n\treturn `NEXT_PUBLIC_ANALYTICS_URL=http://localhost:3000\nANALYTICS_URL=http://localhost:3000\nINGEST_SECRET=replace-with-a-long-random-secret\nORIGIN_ALLOWLIST=http://localhost:3000\nDATABASE_URL=postgres://user:password@host/db\nIP_HASH_SECRET=replace-with-at-least-32-characters\n`;\n}\n\nfunction readmeSeparate(projectName: string, projectId: string): string {\n\treturn `# ${projectName}\n\nTier 1 setup: SDK in \\`apps/web\\`, ingestion in \\`apps/analytics-api\\` (separate deploy).\n\n## 1. Web app\n\n\\`\\`\\`bash\ncd apps/web\nnpm install\ncp .env.example .env.local\nnpm run dev\n\\`\\`\\`\n\n## 2. Analytics API\n\n\\`\\`\\`bash\ncd apps/analytics-api\nnpm install\ncp .env.example .env\n\\`\\`\\`\n\nCreate a Neon database, set \\`DATABASE_URL\\` and \\`IP_HASH_SECRET\\` (min 32 chars).\n\nRun migrations:\n\n\\`\\`\\`bash\nnpx drizzle-kit up:pg --config node_modules/@remcostoeten/ingestion/drizzle.config.ts\n\\`\\`\\`\n\nDeploy \\`apps/analytics-api\\` to Vercel, then set \\`NEXT_PUBLIC_ANALYTICS_URL\\` in the web app to that URL.\n\n## Server-side tracking\n\nFor events that happen on your backend (API routes, webhooks, cron jobs), use the server entry:\n\n\\`\\`\\`typescript\n// apps/web/app/api/example/route.ts\nimport { trackServerEvent } from \"@remcostoeten/analytics/server\";\n\nexport async function POST() {\n await trackServerEvent(\"signup_completed\", { projectId: \"${projectId}\", path: \"/api/signup\" });\n return Response.json({ ok: true });\n}\n\\`\\`\\`\n\nSet \\`ANALYTICS_URL\\` and \\`INGEST_SECRET\\` in \\`apps/web/.env.local\\` (server-only — never \\`NEXT_PUBLIC_\\`).\n\nOn the ingestion side, set the same \\`INGEST_SECRET\\` so it can authenticate server requests.\n\n## Project ID\n\nEvents use \\`projectId=\"${projectId}\"\\`. Change in \\`apps/web/app/layout.tsx\\` if needed.\n`;\n}\n\nfunction readmeColocated(projectName: string, projectId: string): string {\n\treturn `# ${projectName}\n\nTier 2 setup: SDK and ingestion in one Next.js app.\n\nWarning: this adds server-side ingestion dependencies to your app deploy (larger serverless bundle).\n\nIngestion routes: \\`app/e/route.ts\\` and \\`app/ingest/route.ts\\` (SDK posts to \\`/e\\`).\n\n## Setup\n\n\\`\\`\\`bash\nnpm install\ncp .env.example .env.local\n\\`\\`\\`\n\nSet \\`DATABASE_URL\\`, \\`IP_HASH_SECRET\\`, and \\`NEXT_PUBLIC_ANALYTICS_URL\\` (your app URL in production).\n\nRun migrations:\n\n\\`\\`\\`bash\nnpx drizzle-kit up:pg --config node_modules/@remcostoeten/ingestion/drizzle.config.ts\n\\`\\`\\`\n\n\\`\\`\\`bash\nnpm run dev\n\\`\\`\\`\n\n## Server-side tracking\n\nFor events that happen in API routes or server actions, use the server entry. See \\`app/api/example/route.ts\\`:\n\n\\`\\`\\`typescript\nimport { trackServerEvent } from \"@remcostoeten/analytics/server\";\n\nexport async function POST() {\n await trackServerEvent(\"signup_completed\", { projectId: \"${projectId}\", path: \"/api/signup\" });\n return Response.json({ ok: true });\n}\n\\`\\`\\`\n\n\\`ANALYTICS_URL\\` and \\`INGEST_SECRET\\` are already in \\`.env.example\\`. Never use \\`NEXT_PUBLIC_\\` for these.\n\n## Project ID\n\nEvents use \\`projectId=\"${projectId}\"\\`. Change in \\`app/layout.tsx\\` if needed.\n`;\n}\n\nfunction readmeSdkOnly(projectName: string, projectId: string): string {\n\treturn `# ${projectName}\n\nTier 3 setup: SDK only. Point at an existing ingestion URL.\n\n## Setup\n\n\\`\\`\\`bash\nnpm install\ncp .env.example .env.local\n\\`\\`\\`\n\nSet \\`NEXT_PUBLIC_ANALYTICS_URL\\` to your ingestion base URL (posts to \\`/e\\`).\n\n\\`\\`\\`bash\nnpm run dev\n\\`\\`\\`\n\n## Project ID\n\nEvents use \\`projectId=\"${projectId}\"\\`. Change in \\`app/layout.tsx\\` if needed.\n`;\n}\n\nexport function buildFiles(options: ScaffoldOptions): ScaffoldFile[] {\n\tconst ingestPlaceholder = \"https://your-analytics-api.vercel.app\";\n\n\tif (options.tier === \"separate\") {\n\t\treturn [\n\t\t\t{ path: \"README.md\", content: readmeSeparate(options.projectName, options.projectId) },\n\t\t\t{ path: \"apps/web/package.json\", content: webPackageJson() },\n\t\t\t{ path: \"apps/web/tsconfig.json\", content: webTsConfig() },\n\t\t\t{ path: \"apps/web/next.config.mjs\", content: \"export default {};\\n\" },\n\t\t\t{ path: \"apps/web/.env.example\", content: webEnvExample(ingestPlaceholder) },\n\t\t\t{ path: \"apps/web/app/layout.tsx\", content: webLayout(options.projectId) },\n\t\t\t{ path: \"apps/web/app/page.tsx\", content: webPage() },\n\t\t\t{ path: \"apps/web/app/api/example/route.ts\", content: serverTrackingExample(options.projectId) },\n\t\t\t{ path: \"apps/analytics-api/package.json\", content: apiPackageJson() },\n\t\t\t{ path: \"apps/analytics-api/api/index.ts\", content: apiHandler() },\n\t\t\t{ path: \"apps/analytics-api/vercel.json\", content: apiVercelJson() },\n\t\t\t{ path: \"apps/analytics-api/.env.example\", content: apiEnvExample() },\n\t\t];\n\t}\n\n\tif (options.tier === \"colocated\") {\n\t\treturn [\n\t\t\t{ path: \"README.md\", content: readmeColocated(options.projectName, options.projectId) },\n\t\t\t{ path: \"package.json\", content: colocatedPackageJson(options.projectName) },\n\t\t\t{ path: \"tsconfig.json\", content: webTsConfig() },\n\t\t\t{ path: \"next.config.mjs\", content: \"export default {};\\n\" },\n\t\t\t{ path: \".env.example\", content: colocatedEnvExample() },\n\t\t\t{ path: \"app/layout.tsx\", content: webLayout(options.projectId) },\n\t\t\t{ path: \"app/page.tsx\", content: webPage() },\n\t\t\t{ path: \"app/e/route.ts\", content: ingestRoute() },\n\t\t\t{ path: \"app/ingest/route.ts\", content: ingestRoute() },\n\t\t\t{ path: \"app/api/example/route.ts\", content: serverTrackingExample(options.projectId) },\n\t\t];\n\t}\n\n\treturn [\n\t\t{ path: \"README.md\", content: readmeSdkOnly(options.projectName, options.projectId) },\n\t\t{ path: \"package.json\", content: webPackageJson().replace('\"name\": \"web\"', `\"name\": \"${options.projectName}\"`) },\n\t\t{ path: \"tsconfig.json\", content: webTsConfig() },\n\t\t{ path: \"next.config.mjs\", content: \"export default {};\\n\" },\n\t\t{ path: \".env.example\", content: webEnvExample(ingestPlaceholder) },\n\t\t{ path: \"app/layout.tsx\", content: webLayout(options.projectId) },\n\t\t{ path: \"app/page.tsx\", content: webPage() },\n\t];\n}\n","import { createInterface } from \"node:readline/promises\";\nimport { stdin as input, stdout as output } from \"node:process\";\nimport { type Tier } from \"./types\";\n\nconst tierLabels: Record<Tier, string> = {\n\tseparate: \"Separate analytics-api project (recommended)\",\n\tcolocated: \"API route in this app (larger server bundle)\",\n\t\"sdk-only\": \"SDK only — I already have an ingestion URL\",\n};\n\nexport async function promptTier(): Promise<Tier> {\n\tconst rl = createInterface({ input, output });\n\n\tconsole.log(\"\\nIntegration tier:\\n\");\n\tconst tiers: Tier[] = [\"separate\", \"colocated\", \"sdk-only\"];\n\tfor (let i = 0; i < tiers.length; i++) {\n\t\tconst marker = i === 0 ? \"→\" : \" \";\n\t\tconsole.log(` ${marker} ${i + 1}. ${tierLabels[tiers[i]]}`);\n\t}\n\n\tconst answer = await rl.question(\"\\nChoose [1]: \");\n\trl.close();\n\n\tconst index = answer.trim() === \"\" ? 0 : Number.parseInt(answer, 10) - 1;\n\tif (index >= 0 && index < tiers.length) return tiers[index];\n\n\treturn \"separate\";\n}\n\nexport async function promptProjectName(defaultName: string): Promise<string> {\n\tconst rl = createInterface({ input, output });\n\tconst answer = await rl.question(`Project name [${defaultName}]: `);\n\trl.close();\n\n\tconst name = answer.trim() || defaultName;\n\treturn name.replace(/[^a-zA-Z0-9-_]/g, \"-\").toLowerCase();\n}\n\nexport function parseTier(value: string | undefined): Tier | null {\n\tif (value === \"separate\" || value === \"1\") return \"separate\";\n\tif (value === \"colocated\" || value === \"2\") return \"colocated\";\n\tif (value === \"sdk-only\" || value === \"sdk\" || value === \"3\") return \"sdk-only\";\n\treturn null;\n}\n"],"mappings":"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","names":["import_node_path","import_promises","import_promises","input","output"]}
package/package.json CHANGED
@@ -1,18 +1,21 @@
1
1
  {
2
2
  "name": "@remcostoeten/create-analytics",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Scaffold Remco Analytics SDK and ingestion wiring",
5
5
  "license": "MIT",
6
6
  "author": "Remco Stoeten",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/remcostoeten/analytics",
9
+ "url": "git+https://github.com/remcostoeten/analytics.git",
10
10
  "directory": "packages/create-analytics"
11
11
  },
12
12
  "type": "module",
13
- "files": ["dist", "README.md"],
13
+ "files": [
14
+ "dist",
15
+ "README.md"
16
+ ],
14
17
  "bin": {
15
- "create-analytics": "./dist/cli.js"
18
+ "create-analytics": "dist/cli.cjs"
16
19
  },
17
20
  "publishConfig": {
18
21
  "access": "public"