@remcostoeten/create-analytics 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,14 @@
1
- # create-analytics
1
+ # @remcostoeten/create-analytics
2
2
 
3
3
  Scaffold [Remco Analytics](https://github.com/remcostoeten/analytics) wiring for Next.js.
4
4
 
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
- npx create-analytics my-app
9
- npx create-analytics my-app --tier separate --yes
10
- npx create-analytics my-app --tier colocated
11
- npx create-analytics my-app --tier sdk-only
8
+ npx @remcostoeten/create-analytics my-app
9
+ npx @remcostoeten/create-analytics my-app --tier separate --yes
10
+ npx @remcostoeten/create-analytics my-app --tier colocated
11
+ npx @remcostoeten/create-analytics my-app --tier sdk-only
12
12
  ```
13
13
 
14
14
  ## Tiers
package/dist/cli.cjs CHANGED
@@ -430,10 +430,10 @@ function parseTier(value) {
430
430
  // src/cli.ts
431
431
  function printUsage() {
432
432
  console.log(`
433
- create-analytics \u2014 scaffold Remco Analytics
433
+ @remcostoeten/create-analytics \u2014 scaffold Remco Analytics
434
434
 
435
435
  Usage:
436
- npx create-analytics [project-name] [options]
436
+ npx @remcostoeten/create-analytics [project-name] [options]
437
437
 
438
438
  Options:
439
439
  --tier <separate|colocated|sdk-only> Integration tier (default: separate)
package/dist/cli.cjs.map CHANGED
@@ -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":["import_node_path","import_promises","import_promises","input","output"]}
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(`\n@remcostoeten/create-analytics — scaffold Remco Analytics\n\nUsage:\n npx @remcostoeten/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":";;;;AAAA,uBAA0B;AAC1B,IAAAA,oBAAwB;AACxB,IAAAC,mBAAuB;;;ACFvB,sBAAiC;AACjC,uBAA8B;;;ACC9B,SAAS,UAAU,WAA2B;AAC7C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAWoB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAMrC;AAEA,SAAS,UAAkB;AAC1B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASR;AAEA,SAAS,cAAc,WAA2B;AACjD,SAAO;AAAA,4BACoB,SAAS;AAAA;AAAA;AAAA,gBAGrB,SAAS;AAAA;AAAA;AAGzB;AAEA,SAAS,iBAAyB;AACjC,SAAO,KAAK;AAAA,IACX;AAAA,MACC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,MACR;AAAA,MACA,cAAc;AAAA,QACb,2BAA2B;AAAA,QAC3B,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,MACd;AAAA,MACA,iBAAiB;AAAA,QAChB,eAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,YAAY;AAAA,MACb;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEA,SAAS,cAAsB;AAC9B,SAAO,KAAK;AAAA,IACX;AAAA,MACC,iBAAiB;AAAA,QAChB,QAAQ;AAAA,QACR,KAAK,CAAC,OAAO,gBAAgB,QAAQ;AAAA,QACrC,SAAS;AAAA,QACT,cAAc;AAAA,QACd,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,kBAAkB;AAAA,QAClB,iBAAiB;AAAA,QACjB,KAAK;AAAA,QACL,aAAa;AAAA,QACb,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;AAAA,QAC1B,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE;AAAA,MACzB;AAAA,MACA,SAAS,CAAC,iBAAiB,WAAW,UAAU;AAAA,MAChD,SAAS,CAAC,cAAc;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEA,SAAS,iBAAyB;AACjC,SAAO,KAAK;AAAA,IACX;AAAA,MACC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,MAAM;AAAA,MACN,cAAc;AAAA,QACb,2BAA2B;AAAA,QAC3B,4BAA4B;AAAA,QAC5B,eAAe;AAAA,QACf,MAAM;AAAA,QACN,gBAAgB;AAAA,QAChB,KAAK;AAAA,MACN;AAAA,MACA,iBAAiB;AAAA,QAChB,eAAe;AAAA,QACf,YAAY;AAAA,MACb;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEA,SAAS,gBAAwB;AAChC,SAAO;AAAA;AAAA;AAAA;AAAA;AAKR;AAEA,SAAS,aAAqB;AAC7B,SAAO;AAAA;AAER;AAEA,SAAS,gBAAwB;AAChC,SAAO,KAAK;AAAA,IACX;AAAA,MACC,UAAU,CAAC,EAAE,QAAQ,SAAS,aAAa,OAAO,CAAC;AAAA,IACpD;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEA,SAAS,cAAsB;AAC9B,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASR;AAEA,SAAS,sBAAsB,WAA2B;AACzD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,0DAKkD,SAAS;AAAA;AAAA;AAAA;AAAA;AAKnE;AAEA,SAAS,qBAAqB,aAA6B;AAC1D,SAAO,KAAK;AAAA,IACX;AAAA,MACC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACR,KAAK;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,MACR;AAAA,MACA,cAAc;AAAA,QACb,2BAA2B;AAAA,QAC3B,2BAA2B;AAAA,QAC3B,4BAA4B;AAAA,QAC5B,eAAe;AAAA,QACf,MAAM;AAAA,QACN,MAAM;AAAA,QACN,OAAO;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,KAAK;AAAA,MACN;AAAA,MACA,iBAAiB;AAAA,QAChB,eAAe;AAAA,QACf,gBAAgB;AAAA,QAChB,eAAe;AAAA,QACf,YAAY;AAAA,MACb;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEA,SAAS,sBAA8B;AACtC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOR;AAEA,SAAS,eAAe,aAAqB,WAA2B;AACvE,SAAO,KAAK,WAAW;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6DAwCqC,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAW5C,SAAS;AAAA;AAEnC;AAEA,SAAS,gBAAgB,aAAqB,WAA2B;AACxE,SAAO,KAAK,WAAW;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6DAmCqC,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAS5C,SAAS;AAAA;AAEnC;AAEA,SAAS,cAAc,aAAqB,WAA2B;AACtE,SAAO,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAmBE,SAAS;AAAA;AAEnC;AAEO,SAAS,WAAW,SAA0C;AACpE,QAAM,oBAAoB;AAE1B,MAAI,QAAQ,SAAS,YAAY;AAChC,WAAO;AAAA,MACN,EAAE,MAAM,aAAa,SAAS,eAAe,QAAQ,aAAa,QAAQ,SAAS,EAAE;AAAA,MACrF,EAAE,MAAM,yBAAyB,SAAS,eAAe,EAAE;AAAA,MAC3D,EAAE,MAAM,0BAA0B,SAAS,YAAY,EAAE;AAAA,MACzD,EAAE,MAAM,4BAA4B,SAAS,uBAAuB;AAAA,MACpE,EAAE,MAAM,yBAAyB,SAAS,cAAc,iBAAiB,EAAE;AAAA,MAC3E,EAAE,MAAM,2BAA2B,SAAS,UAAU,QAAQ,SAAS,EAAE;AAAA,MACzE,EAAE,MAAM,yBAAyB,SAAS,QAAQ,EAAE;AAAA,MACpD,EAAE,MAAM,qCAAqC,SAAS,sBAAsB,QAAQ,SAAS,EAAE;AAAA,MAC/F,EAAE,MAAM,mCAAmC,SAAS,eAAe,EAAE;AAAA,MACrE,EAAE,MAAM,mCAAmC,SAAS,WAAW,EAAE;AAAA,MACjE,EAAE,MAAM,kCAAkC,SAAS,cAAc,EAAE;AAAA,MACnE,EAAE,MAAM,mCAAmC,SAAS,cAAc,EAAE;AAAA,IACrE;AAAA,EACD;AAEA,MAAI,QAAQ,SAAS,aAAa;AACjC,WAAO;AAAA,MACN,EAAE,MAAM,aAAa,SAAS,gBAAgB,QAAQ,aAAa,QAAQ,SAAS,EAAE;AAAA,MACtF,EAAE,MAAM,gBAAgB,SAAS,qBAAqB,QAAQ,WAAW,EAAE;AAAA,MAC3E,EAAE,MAAM,iBAAiB,SAAS,YAAY,EAAE;AAAA,MAChD,EAAE,MAAM,mBAAmB,SAAS,uBAAuB;AAAA,MAC3D,EAAE,MAAM,gBAAgB,SAAS,oBAAoB,EAAE;AAAA,MACvD,EAAE,MAAM,kBAAkB,SAAS,UAAU,QAAQ,SAAS,EAAE;AAAA,MAChE,EAAE,MAAM,gBAAgB,SAAS,QAAQ,EAAE;AAAA,MAC3C,EAAE,MAAM,kBAAkB,SAAS,YAAY,EAAE;AAAA,MACjD,EAAE,MAAM,uBAAuB,SAAS,YAAY,EAAE;AAAA,MACtD,EAAE,MAAM,4BAA4B,SAAS,sBAAsB,QAAQ,SAAS,EAAE;AAAA,IACvF;AAAA,EACD;AAEA,SAAO;AAAA,IACN,EAAE,MAAM,aAAa,SAAS,cAAc,QAAQ,aAAa,QAAQ,SAAS,EAAE;AAAA,IACpF,EAAE,MAAM,gBAAgB,SAAS,eAAe,EAAE,QAAQ,iBAAiB,YAAY,QAAQ,WAAW,GAAG,EAAE;AAAA,IAC/G,EAAE,MAAM,iBAAiB,SAAS,YAAY,EAAE;AAAA,IAChD,EAAE,MAAM,mBAAmB,SAAS,uBAAuB;AAAA,IAC3D,EAAE,MAAM,gBAAgB,SAAS,cAAc,iBAAiB,EAAE;AAAA,IAClE,EAAE,MAAM,kBAAkB,SAAS,UAAU,QAAQ,SAAS,EAAE;AAAA,IAChE,EAAE,MAAM,gBAAgB,SAAS,QAAQ,EAAE;AAAA,EAC5C;AACD;;;AD7XA,eAAsB,gBAAgB,SAA6C;AAClF,QAAM,QAAQ,WAAW,OAAO;AAChC,QAAM,UAAoB,CAAC;AAE3B,aAAW,QAAQ,OAAO;AACzB,UAAM,eAAW,uBAAK,QAAQ,WAAW,KAAK,IAAI;AAClD,cAAM,2BAAM,0BAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,cAAM,2BAAU,UAAU,KAAK,SAAS,MAAM;AAC9C,YAAQ,KAAK,KAAK,IAAI;AAAA,EACvB;AAEA,SAAO;AACR;;;AEjBA,IAAAC,mBAAgC;AAChC,0BAAiD;AAGjD,IAAM,aAAmC;AAAA,EACxC,UAAU;AAAA,EACV,WAAW;AAAA,EACX,YAAY;AACb;AAEA,eAAsB,aAA4B;AACjD,QAAM,SAAK,kCAAgB,EAAE,2BAAAC,OAAO,4BAAAC,OAAO,CAAC;AAE5C,UAAQ,IAAI,uBAAuB;AACnC,QAAM,QAAgB,CAAC,YAAY,aAAa,UAAU;AAC1D,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACtC,UAAM,SAAS,MAAM,IAAI,WAAM;AAC/B,YAAQ,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,KAAK,WAAW,MAAM,CAAC,CAAC,CAAC,EAAE;AAAA,EAC5D;AAEA,QAAM,SAAS,MAAM,GAAG,SAAS,gBAAgB;AACjD,KAAG,MAAM;AAET,QAAM,QAAQ,OAAO,KAAK,MAAM,KAAK,IAAI,OAAO,SAAS,QAAQ,EAAE,IAAI;AACvE,MAAI,SAAS,KAAK,QAAQ,MAAM,OAAQ,QAAO,MAAM,KAAK;AAE1D,SAAO;AACR;AAEA,eAAsB,kBAAkB,aAAsC;AAC7E,QAAM,SAAK,kCAAgB,EAAE,2BAAAD,OAAO,4BAAAC,OAAO,CAAC;AAC5C,QAAM,SAAS,MAAM,GAAG,SAAS,iBAAiB,WAAW,KAAK;AAClE,KAAG,MAAM;AAET,QAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,SAAO,KAAK,QAAQ,mBAAmB,GAAG,EAAE,YAAY;AACzD;AAEO,SAAS,UAAU,OAAwC;AACjE,MAAI,UAAU,cAAc,UAAU,IAAK,QAAO;AAClD,MAAI,UAAU,eAAe,UAAU,IAAK,QAAO;AACnD,MAAI,UAAU,cAAc,UAAU,SAAS,UAAU,IAAK,QAAO;AACrE,SAAO;AACR;;;AH9BA,SAAS,aAAmB;AAC3B,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAeZ;AACD;AAEA,eAAe,eAAe,KAA+B;AAC5D,MAAI;AACH,cAAM,yBAAO,GAAG;AAChB,WAAO;AAAA,EACR,QAAQ;AACP,WAAO;AAAA,EACR;AACD;AAEA,eAAe,eAAe,MAAe,gBAK1C;AACF,QAAM,cAAc,gBAAgB,KAAK,KAAK;AAC9C,MAAI,OAAO,UAAU,KAAK,IAAI;AAC9B,MAAI,cAAc;AAElB,MAAI,CAAC,QAAQ,CAAC,KAAK,KAAK;AACvB,WAAO,MAAM,WAAW;AACxB,kBAAc,MAAM,kBAAkB,WAAW;AAAA,EAClD,OAAO;AACN,WAAO,QAAQ;AACf,QAAI,CAAC,KAAK,OAAO,CAAC,KAAK,MAAM;AAC5B,oBAAc,MAAM,kBAAkB,WAAW;AAAA,IAClD;AAAA,EACD;AAEA,QAAM,gBAAY,2BAAQ,QAAQ,IAAI,GAAG,WAAW;AAEpD,MAAI,CAAE,MAAM,eAAe,SAAS,GAAI;AACvC,YAAQ,MAAM,WAAW,WAAW,gEAAgE;AACpG,YAAQ,KAAK,CAAC;AAAA,EACf;AAEA,SAAO;AAAA,IACN;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,EACD;AACD;AAEA,eAAe,OAAsB;AACpC,QAAM,EAAE,QAAQ,YAAY,QAAI,4BAAU;AAAA,IACzC,MAAM,QAAQ,KAAK,MAAM,CAAC;AAAA,IAC1B,SAAS;AAAA,MACR,MAAM,EAAE,MAAM,SAAS;AAAA,MACvB,KAAK,EAAE,MAAM,WAAW,OAAO,IAAI;AAAA,MACnC,MAAM,EAAE,MAAM,WAAW,OAAO,IAAI;AAAA,IACrC;AAAA,IACA,kBAAkB;AAAA,EACnB,CAAC;AAED,MAAI,OAAO,MAAM;AAChB,eAAW;AACX;AAAA,EACD;AAEA,QAAM,UAAU,MAAM;AAAA,IACrB;AAAA,MACC,MAAM,OAAO;AAAA,MACb,KAAK,OAAO;AAAA,IACb;AAAA,IACA,YAAY,CAAC;AAAA,EACd;AAEA,UAAQ,IAAI;AAAA,cAAiB,QAAQ,WAAW,KAAK,QAAQ,IAAI;AAAA,CAAQ;AAEzE,QAAM,UAAU,MAAM,gBAAgB,OAAO;AAE7C,aAAW,QAAQ,SAAS;AAC3B,YAAQ,IAAI,aAAa,IAAI,EAAE;AAAA,EAChC;AAEA,UAAQ,IAAI;AAAA;AAAA;AAAA,OAGN,QAAQ,WAAW;AAAA;AAAA,CAEzB;AAEA,MAAI,QAAQ,SAAS,YAAY;AAChC,YAAQ,IAAI,gGAAgG;AAAA,EAC7G;AAEA,MAAI,QAAQ,SAAS,aAAa;AACjC,YAAQ,IAAI,oFAA+E;AAAA,EAC5F;AACD;AAEA,KAAK,EAAE,MAAM,SAAU,OAAO;AAC7B,UAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAC5D,UAAQ,KAAK,CAAC;AACf,CAAC;","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.1",
3
+ "version": "0.1.3",
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.cjs"
18
+ "create-analytics": "dist/cli.cjs"
16
19
  },
17
20
  "publishConfig": {
18
21
  "access": "public"