@remcostoeten/create-analytics 0.1.0 → 0.1.1
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/dist/{cli.js → cli.cjs}
RENAMED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
2
3
|
|
|
3
4
|
// src/cli.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
-
|
|
395
|
-
|
|
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.
|
|
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":";;;;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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remcostoeten/create-analytics",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Scaffold Remco Analytics SDK and ingestion wiring",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Remco Stoeten",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"type": "module",
|
|
13
13
|
"files": ["dist", "README.md"],
|
|
14
14
|
"bin": {
|
|
15
|
-
"create-analytics": "./dist/cli.
|
|
15
|
+
"create-analytics": "./dist/cli.cjs"
|
|
16
16
|
},
|
|
17
17
|
"publishConfig": {
|
|
18
18
|
"access": "public"
|