From ea384c67c48212deebe26ee26a05aedb093fb178 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Mon, 18 May 2026 11:26:24 +0000 Subject: [PATCH] feat(core-analytics): scaffold @repo/core-analytics via generator Add the `pnpm turbo gen core-package analytics` generator template and run it to scaffold the @repo/core-analytics workspace package. The package lands in placeholder state (empty barrel export) ready for the IAnalytics + NoopAnalytics implementation in the next commit. Includes: - turbo/generators/templates/core-package/analytics/ templates - turbo/generators/config.ts analytics generator registration - packages/core-analytics/ placeholder scaffold - apps/web-next/next.config.mjs transpilePackages entry Co-Authored-By: Claude Sonnet 4.6 --- apps/web-next/next.config.mjs | 1 + packages/core-analytics/AGENTS.md | 32 +++++++++++++ packages/core-analytics/eslint.config.js | 3 ++ packages/core-analytics/package.json | 23 ++++++++++ packages/core-analytics/src/index.ts | 2 + packages/core-analytics/tsconfig.json | 12 +++++ packages/core-analytics/turbo.json | 4 ++ packages/core-analytics/vitest.config.ts | 9 ++++ pnpm-lock.yaml | 21 +++++++++ turbo/generators/config.ts | 46 ++++++++++++++++++- .../core-package/analytics/AGENTS.md.hbs | 32 +++++++++++++ .../analytics/eslint.config.js.hbs | 3 ++ .../core-package/analytics/package.json.hbs | 23 ++++++++++ .../core-package/analytics/src/index.ts.hbs | 2 + .../core-package/analytics/tsconfig.json.hbs | 12 +++++ .../core-package/analytics/turbo.json.hbs | 4 ++ .../analytics/vitest.config.ts.hbs | 9 ++++ 17 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 packages/core-analytics/AGENTS.md create mode 100644 packages/core-analytics/eslint.config.js create mode 100644 packages/core-analytics/package.json create mode 100644 packages/core-analytics/src/index.ts create mode 100644 packages/core-analytics/tsconfig.json create mode 100644 packages/core-analytics/turbo.json create mode 100644 packages/core-analytics/vitest.config.ts create mode 100644 turbo/generators/templates/core-package/analytics/AGENTS.md.hbs create mode 100644 turbo/generators/templates/core-package/analytics/eslint.config.js.hbs create mode 100644 turbo/generators/templates/core-package/analytics/package.json.hbs create mode 100644 turbo/generators/templates/core-package/analytics/src/index.ts.hbs create mode 100644 turbo/generators/templates/core-package/analytics/tsconfig.json.hbs create mode 100644 turbo/generators/templates/core-package/analytics/turbo.json.hbs create mode 100644 turbo/generators/templates/core-package/analytics/vitest.config.ts.hbs diff --git a/apps/web-next/next.config.mjs b/apps/web-next/next.config.mjs index f110565..02232d6 100644 --- a/apps/web-next/next.config.mjs +++ b/apps/web-next/next.config.mjs @@ -5,6 +5,7 @@ const nextConfig = { transpilePackages: [ "@repo/auth", "@repo/blog", + "@repo/core-analytics", "@repo/core-api", "@repo/core-audit", "@repo/core-cms", diff --git a/packages/core-analytics/AGENTS.md b/packages/core-analytics/AGENTS.md new file mode 100644 index 0000000..3491801 --- /dev/null +++ b/packages/core-analytics/AGENTS.md @@ -0,0 +1,32 @@ +# @repo/core-analytics + +Optional core package providing a vendor-neutral product analytics interface. Scaffold via `pnpm turbo gen core-package analytics`. + +## Structure + +``` +src/ + analytics.interface.ts # IAnalytics — track, identify, pageView, flush + noop-analytics.ts # NoopAnalytics (default no-op implementation) + index.ts # Barrel export +``` + +## Design + +`IAnalytics` exposes four methods: + +- `track(event, attributes?)` — record a named event with optional attributes +- `identify(user)` — associate subsequent events with a user +- `pageView(path, attributes?)` — record a page-view event +- `flush()` — drain any in-flight queued events (returns `Promise`) + +The interface is vendor-neutral: no third-party analytics SDK is bundled. Feature +packages depend on `IAnalytics` only; concrete implementations (e.g. a PostHog +or Segment adapter) are wired at DI bind time in `bind-production`. + +`NoopAnalytics` is the default implementation — all methods are no-ops and +`flush()` resolves immediately via `Promise.resolve()`. Use it in dev-seed +bindings and unit tests. + +See `docs/architecture/agent-first-workflow-and-conformance.md` for the +dependency-injection conventions. diff --git a/packages/core-analytics/eslint.config.js b/packages/core-analytics/eslint.config.js new file mode 100644 index 0000000..7440d8f --- /dev/null +++ b/packages/core-analytics/eslint.config.js @@ -0,0 +1,3 @@ +import baseConfig from "@repo/core-eslint/base"; + +export default baseConfig; diff --git a/packages/core-analytics/package.json b/packages/core-analytics/package.json new file mode 100644 index 0000000..51d4cc3 --- /dev/null +++ b/packages/core-analytics/package.json @@ -0,0 +1,23 @@ +{ + "name": "@repo/core-analytics", + "version": "0.0.1", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "build": "tsc --noEmit", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "test": "vitest run --passWithNoTests" + }, + "devDependencies": { + "@repo/core-eslint": "workspace:*", + "@repo/core-testing": "workspace:*", + "@repo/core-typescript": "workspace:*", + "@vitest/coverage-v8": "^3.0.0", + "typescript": "^5.8.0", + "vitest": "^3.0.0" + } +} diff --git a/packages/core-analytics/src/index.ts b/packages/core-analytics/src/index.ts new file mode 100644 index 0000000..8e2040a --- /dev/null +++ b/packages/core-analytics/src/index.ts @@ -0,0 +1,2 @@ +// placeholder — populated by story 01-scaffold-core-analytics-package +export {}; diff --git a/packages/core-analytics/tsconfig.json b/packages/core-analytics/tsconfig.json new file mode 100644 index 0000000..8facef5 --- /dev/null +++ b/packages/core-analytics/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@repo/core-typescript/base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/core-analytics/turbo.json b/packages/core-analytics/turbo.json new file mode 100644 index 0000000..dcb8fb3 --- /dev/null +++ b/packages/core-analytics/turbo.json @@ -0,0 +1,4 @@ +{ + "extends": ["//"], + "tags": ["core"] +} diff --git a/packages/core-analytics/vitest.config.ts b/packages/core-analytics/vitest.config.ts new file mode 100644 index 0000000..2ee07c1 --- /dev/null +++ b/packages/core-analytics/vitest.config.ts @@ -0,0 +1,9 @@ +import path from "node:path"; +import { mergeConfig } from "vitest/config"; +import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node"; + +export default mergeConfig(nodeVitestConfig, { + resolve: { + alias: { "@": path.resolve(__dirname, "./src") }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fdd5a9a..d7c7277 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -415,6 +415,27 @@ importers: specifier: ^3.1.0 version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + packages/core-analytics: + devDependencies: + "@repo/core-eslint": + specifier: workspace:* + version: link:../core-eslint + "@repo/core-testing": + specifier: workspace:* + version: link:../core-testing + "@repo/core-typescript": + specifier: workspace:* + version: link:../core-typescript + "@vitest/coverage-v8": + specifier: ^3.0.0 + version: 3.2.4(vitest@3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0)) + typescript: + specifier: ^5.8.0 + version: 5.9.3 + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0)(yaml@2.9.0) + packages/core-api: dependencies: "@repo/auth": diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts index 28cf81a..0d3ea15 100644 --- a/turbo/generators/config.ts +++ b/turbo/generators/config.ts @@ -764,17 +764,35 @@ import noRealtimeHandlerReexport from "./rules/no-realtime-handler-reexport.js"; }, printAuditNextSteps, ], + analytics: () => [ + () => { + assertOptionalPackageNotPresent("core-analytics"); + return "Guard passed — packages/core-analytics does not exist yet."; + }, + ...emitTemplateTree( + "core-package/analytics", + "packages/core-analytics", + ), + () => { + addToTranspilePackages( + "apps/web-next/next.config.mjs", + "@repo/core-analytics", + ); + return "Added @repo/core-analytics to transpilePackages."; + }, + printAnalyticsNextSteps, + ], }; plop.setGenerator("core-package", { description: - "Scaffold an optional core package (realtime, events, trpc, ui, audit)", + "Scaffold an optional core package (realtime, events, trpc, ui, audit, analytics)", prompts: [ { type: "list", name: "name", message: "Which optional core package?", - choices: ["realtime", "events", "trpc", "ui", "audit"], + choices: ["analytics", "audit", "events", "realtime", "trpc", "ui"], }, ], actions: (answers) => { @@ -1475,6 +1493,30 @@ function printAuditNextSteps(): string { ].join("\n"); } +function printAnalyticsNextSteps(): string { + return [ + "─────────────────────────────────────────────────────────────", + "@repo/core-analytics scaffolded into packages/core-analytics/.", + "", + "Next steps:", + "", + " 1. pnpm install # link the new workspace package", + "", + " 2. Implement IAnalytics + NoopAnalytics in packages/core-analytics/src/", + " (see story 01-scaffold-core-analytics-package bullet 2)", + "", + " 3. Add @repo/core-analytics to feature package.json files that need analytics", + "", + " 4. Wire IAnalytics into apps/web-next/src/server/bind-production.ts:", + ' - import { NoopAnalytics, type IAnalytics } from "@repo/core-analytics";', + " - add analytics field to BindAllDeps and BindProductionContext", + " - pass NoopAnalytics (or a vendor adapter) into each feature binder", + "", + " 5. pnpm typecheck && pnpm lint && pnpm test", + "─────────────────────────────────────────────────────────────", + ].join("\n"); +} + function coreUiComponentActions(a: { tier: "atom" | "molecule" | "organism"; name: string; diff --git a/turbo/generators/templates/core-package/analytics/AGENTS.md.hbs b/turbo/generators/templates/core-package/analytics/AGENTS.md.hbs new file mode 100644 index 0000000..3491801 --- /dev/null +++ b/turbo/generators/templates/core-package/analytics/AGENTS.md.hbs @@ -0,0 +1,32 @@ +# @repo/core-analytics + +Optional core package providing a vendor-neutral product analytics interface. Scaffold via `pnpm turbo gen core-package analytics`. + +## Structure + +``` +src/ + analytics.interface.ts # IAnalytics — track, identify, pageView, flush + noop-analytics.ts # NoopAnalytics (default no-op implementation) + index.ts # Barrel export +``` + +## Design + +`IAnalytics` exposes four methods: + +- `track(event, attributes?)` — record a named event with optional attributes +- `identify(user)` — associate subsequent events with a user +- `pageView(path, attributes?)` — record a page-view event +- `flush()` — drain any in-flight queued events (returns `Promise`) + +The interface is vendor-neutral: no third-party analytics SDK is bundled. Feature +packages depend on `IAnalytics` only; concrete implementations (e.g. a PostHog +or Segment adapter) are wired at DI bind time in `bind-production`. + +`NoopAnalytics` is the default implementation — all methods are no-ops and +`flush()` resolves immediately via `Promise.resolve()`. Use it in dev-seed +bindings and unit tests. + +See `docs/architecture/agent-first-workflow-and-conformance.md` for the +dependency-injection conventions. diff --git a/turbo/generators/templates/core-package/analytics/eslint.config.js.hbs b/turbo/generators/templates/core-package/analytics/eslint.config.js.hbs new file mode 100644 index 0000000..7440d8f --- /dev/null +++ b/turbo/generators/templates/core-package/analytics/eslint.config.js.hbs @@ -0,0 +1,3 @@ +import baseConfig from "@repo/core-eslint/base"; + +export default baseConfig; diff --git a/turbo/generators/templates/core-package/analytics/package.json.hbs b/turbo/generators/templates/core-package/analytics/package.json.hbs new file mode 100644 index 0000000..51d4cc3 --- /dev/null +++ b/turbo/generators/templates/core-package/analytics/package.json.hbs @@ -0,0 +1,23 @@ +{ + "name": "@repo/core-analytics", + "version": "0.0.1", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "build": "tsc --noEmit", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "test": "vitest run --passWithNoTests" + }, + "devDependencies": { + "@repo/core-eslint": "workspace:*", + "@repo/core-testing": "workspace:*", + "@repo/core-typescript": "workspace:*", + "@vitest/coverage-v8": "^3.0.0", + "typescript": "^5.8.0", + "vitest": "^3.0.0" + } +} diff --git a/turbo/generators/templates/core-package/analytics/src/index.ts.hbs b/turbo/generators/templates/core-package/analytics/src/index.ts.hbs new file mode 100644 index 0000000..8e2040a --- /dev/null +++ b/turbo/generators/templates/core-package/analytics/src/index.ts.hbs @@ -0,0 +1,2 @@ +// placeholder — populated by story 01-scaffold-core-analytics-package +export {}; diff --git a/turbo/generators/templates/core-package/analytics/tsconfig.json.hbs b/turbo/generators/templates/core-package/analytics/tsconfig.json.hbs new file mode 100644 index 0000000..8facef5 --- /dev/null +++ b/turbo/generators/templates/core-package/analytics/tsconfig.json.hbs @@ -0,0 +1,12 @@ +{ + "extends": "@repo/core-typescript/base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["**/*.ts"], + "exclude": ["node_modules", "dist"] +} diff --git a/turbo/generators/templates/core-package/analytics/turbo.json.hbs b/turbo/generators/templates/core-package/analytics/turbo.json.hbs new file mode 100644 index 0000000..dcb8fb3 --- /dev/null +++ b/turbo/generators/templates/core-package/analytics/turbo.json.hbs @@ -0,0 +1,4 @@ +{ + "extends": ["//"], + "tags": ["core"] +} diff --git a/turbo/generators/templates/core-package/analytics/vitest.config.ts.hbs b/turbo/generators/templates/core-package/analytics/vitest.config.ts.hbs new file mode 100644 index 0000000..2ee07c1 --- /dev/null +++ b/turbo/generators/templates/core-package/analytics/vitest.config.ts.hbs @@ -0,0 +1,9 @@ +import path from "node:path"; +import { mergeConfig } from "vitest/config"; +import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node"; + +export default mergeConfig(nodeVitestConfig, { + resolve: { + alias: { "@": path.resolve(__dirname, "./src") }, + }, +});