affitor 0.3.0 → 0.4.0

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.
@@ -53,6 +53,68 @@ export class AffitorAPI {
53
53
  body: data,
54
54
  });
55
55
  }
56
+ // ─── Onboarding / verification endpoints ────────────────────────
57
+ /**
58
+ * GET /api/v1/programs/me/readiness — the per-program 5-gate verdict
59
+ * (Bearer key). `integration_verified` flips true when every gate passes;
60
+ * the blocking gate's `next_action` tells the caller what to fix.
61
+ */
62
+ async getReadiness(opts = {}) {
63
+ return this.request("/api/v1/programs/me/readiness", {
64
+ apiKey: opts.apiKey,
65
+ apiUrl: opts.apiUrl,
66
+ });
67
+ }
68
+ /**
69
+ * POST /api/v1/cli/test-event {type:'chain'} — fire the synthetic
70
+ * click→lead→sale chain through the REAL attribution pipeline (isolated
71
+ * is_test rows). Mirrors the MCP `fireVerificationChain` contract exactly.
72
+ *
73
+ * Does NOT use the shared `request()` helper because that throws on 429. A
74
+ * 429 here is expected (the chain is rate-limited to 10/program/hour) and the
75
+ * caller must read `retry_after_seconds` and back off — so on a non-2xx this
76
+ * returns the parsed body (with `rate_limited` + `retry_after_seconds` merged
77
+ * in for 429) instead of throwing. Only a network/parse failure throws.
78
+ */
79
+ async runVerificationChain(opts = {}) {
80
+ const apiUrl = opts.apiUrl ?? this.apiUrl;
81
+ const key = opts.apiKey ?? this.apiKey;
82
+ const url = `${apiUrl}/api/v1/cli/test-event`;
83
+ const headers = {
84
+ "Content-Type": "application/json",
85
+ "User-Agent": "affitor-cli/0.2.0",
86
+ };
87
+ if (key)
88
+ headers["Authorization"] = `Bearer ${key}`;
89
+ logger.debug(`POST ${url} {type:'chain'}`);
90
+ let res;
91
+ try {
92
+ res = await fetch(url, {
93
+ method: "POST",
94
+ headers,
95
+ body: JSON.stringify({ type: "chain" }),
96
+ });
97
+ }
98
+ catch (err) {
99
+ throw new NetworkError(err.message);
100
+ }
101
+ const body = (await res.json().catch(() => ({})));
102
+ if (res.ok) {
103
+ // Server returns { data: { verdict, attributed, ... } } or the bare object.
104
+ return (body.data ?? body);
105
+ }
106
+ // Non-2xx (incl. 429): DO NOT throw — surface the parsed body so the caller
107
+ // can read retry_after_seconds and back off. Mirror MCP fireVerificationChain.
108
+ const errObj = (body.error ?? {});
109
+ const retryHeader = res.headers.get("Retry-After");
110
+ const retryAfter = errObj.retry_after_seconds ?? (retryHeader ? parseInt(retryHeader, 10) : undefined);
111
+ return {
112
+ ...body,
113
+ http_status: res.status,
114
+ rate_limited: res.status === 429 || errObj.code === "rate_limited",
115
+ ...(retryAfter !== undefined ? { retry_after_seconds: retryAfter } : {}),
116
+ };
117
+ }
56
118
  async request(path, opts = {}) {
57
119
  const url = `${opts.apiUrl ?? this.apiUrl}${path}`;
58
120
  const key = opts.apiKey ?? this.apiKey;
@@ -78,8 +140,13 @@ export class AffitorAPI {
78
140
  throw new APIError(429, `Rate limited. Wait ${Math.ceil(wait / 1000)} seconds and try again.`);
79
141
  }
80
142
  if (!res.ok) {
81
- const body = await res.json().catch(() => ({}));
82
- const message = body.error ??
143
+ const body = (await res.json().catch(() => ({})));
144
+ // Strapi returns errors as an object ({ error: { message, ... } }),
145
+ // but some endpoints return { error: "string" } or { message }.
146
+ // Extract a string so we never surface "[object Object]".
147
+ const rawError = body.error;
148
+ const message = (typeof rawError === "string" ? rawError : rawError?.message) ??
149
+ body.message ??
83
150
  `API returned ${res.status}`;
84
151
  throw new APIError(res.status, message);
85
152
  }
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"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"}
@@ -228,11 +228,11 @@ Add this script tag to every page in \`<head>\`. It auto-detects affiliate visit
228
228
  For React/Next.js, use the SDK instead:
229
229
 
230
230
  \`\`\`bash
231
- npm install @affitor/tracker
231
+ npm install @affitor/sdk
232
232
  \`\`\`
233
233
 
234
234
  \`\`\`tsx
235
- import { AffitorProvider } from '@affitor/tracker/react';
235
+ import { AffitorProvider } from '@affitor/sdk/react';
236
236
 
237
237
  <AffitorProvider programId="${config.program_id}">
238
238
  {children}
@@ -24,4 +24,48 @@ export declare function injectAppLayout(content: string, importSpecifier: string
24
24
  * every page, and add the import.
25
25
  */
26
26
  export declare function injectPagesApp(content: string, importSpecifier: string): InjectResult;
27
+ export interface InjectStripeOpts {
28
+ /**
29
+ * The provider sale snippet from `@affitor/recipes` (e.g.
30
+ * `getRecipe(framework, 'stripe', 's2s').sale!.snippet`). The SDK import line
31
+ * (`import { Affitor } from '@affitor/sdk/server';`) is stripped before
32
+ * insertion — the inserted body is just the `affitor.trackSale({...})` call,
33
+ * indented to match the matched session binding.
34
+ */
35
+ saleSnippet: string;
36
+ /**
37
+ * Module specifier for the `affitor` client import — e.g. `'../lib/affitor'`
38
+ * or `'@/lib/affitor'`. When provided and status is 'injected', the transform
39
+ * also adds `import { affitor } from '<importSpecifier>';` at the top of the
40
+ * file (idempotent: skipped if that line is already present). Included in
41
+ * `added[]` so the diff preview shows it.
42
+ */
43
+ importSpecifier?: string;
44
+ /**
45
+ * Optional indent override (spaces). When omitted, the indent of the matched
46
+ * session-binding line is reused so the inserted call lines up with its block.
47
+ */
48
+ indent?: string;
49
+ }
50
+ /**
51
+ * Insert `affitor.trackSale(...)` into a Stripe webhook handler, right after the
52
+ * verified `checkout.session.completed` session object is bound.
53
+ *
54
+ * CONSERVATIVE BY DESIGN — only injects when ALL of these hold (else returns
55
+ * `unrecognized` so the caller prints the patch instead of editing payment code):
56
+ * 1. The file contains `stripe.webhooks.constructEvent` (it really is a
57
+ * verified Stripe webhook — we never edit an unverified handler).
58
+ * 2. There is a single `checkout.session.completed` reference to anchor on
59
+ * (the recipe's stripe snippet reads `session.*`, so the sale belongs in
60
+ * that case). Zero or multiple matches → unrecognized (ambiguous).
61
+ * 3. After that anchor, exactly one line binds the event object to a name we
62
+ * can reuse — `const session = event.data.object …` (or `= session …`).
63
+ * The trackSale call is inserted on the line after that binding, at the
64
+ * binding's own indentation. No binding, or an unexpected variable name →
65
+ * unrecognized.
66
+ *
67
+ * Returns `already` when a `affitor.trackSale(` / `@affitor/sdk/server` marker is
68
+ * already present (idempotent re-runs do nothing).
69
+ */
70
+ export declare function injectStripeTrackSale(content: string, opts: InjectStripeOpts): InjectResult;
27
71
  //# sourceMappingURL=inject.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inject.d.ts","sourceRoot":"","sources":["../../src/lib/inject.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAG,MAAM,CAiBtG;AAmBD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,YAAY,CAetF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,YAAY,CAerF"}
1
+ {"version":3,"file":"inject.d.ts","sourceRoot":"","sources":["../../src/lib/inject.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,cAAc,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,gFAAgF;AAChF,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,kBAAkB,EAAE,OAAO,GAAG,MAAM,CAiBtG;AA6BD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,YAAY,CAetF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,YAAY,CAerF;AAiBD,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAsBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,YAAY,CA+D3F"}
@@ -11,7 +11,7 @@ export function trackerComponentSource(programId, useClientDirective) {
11
11
  const idLiteral = typeof programId === "number" ? `${programId}` : JSON.stringify(programId);
12
12
  const prefix = useClientDirective ? "'use client';\n\n" : "";
13
13
  return `${prefix}import { useEffect } from 'react';
14
- import { init } from 'affitor-sdk';
14
+ import { init } from '@affitor/sdk';
15
15
 
16
16
  /**
17
17
  * Affitor affiliate tracking — auto-generated by \`affitor init\`.
@@ -28,11 +28,20 @@ export function ${TRACKER_MARKER}() {
28
28
  function importLineFor(importSpecifier) {
29
29
  return `import { ${TRACKER_MARKER} } from '${importSpecifier}';`;
30
30
  }
31
- /** Add an import after the last existing top-level import (or at the very top). */
31
+ /**
32
+ * Add an import after the last existing top-level import (or at the very top).
33
+ *
34
+ * Matches COMPLETE import statements — including multi-line destructured imports
35
+ * (`import {\n a,\n b\n} from 'x';`) — so the new line lands AFTER the last
36
+ * import, never spliced inside a destructure. An `import` starting at column 0
37
+ * runs to its terminating `from '...'` / `from "..."` clause (or, for a
38
+ * side-effect import, the bare `import '...'`), spanning newlines as needed.
39
+ */
32
40
  function addImport(content, importLine) {
33
41
  if (content.includes(importLine))
34
42
  return content;
35
- const importRe = /^import .*$/gm;
43
+ // ^import … from '…'|"…" (multi-line tolerant), OR a side-effect import '…'.
44
+ const importRe = /^import\b[\s\S]*?from\s+(['"])[^'"]*\1\s*;?|^import\s+(['"])[^'"]*\2\s*;?/gm;
36
45
  let lastEnd = -1;
37
46
  let m;
38
47
  while ((m = importRe.exec(content)) !== null) {
@@ -78,4 +87,107 @@ export function injectPagesApp(content, importSpecifier) {
78
87
  const injected = withImport.replace(compRe, replacement);
79
88
  return { content: injected, status: "injected", added: [importLine, `<${TRACKER_MARKER} />`] };
80
89
  }
90
+ // ── Server-side: inject `affitor.trackSale(...)` into a Stripe webhook ──
91
+ //
92
+ // This edits PAYMENT code, so it is deliberately conservative: it only injects
93
+ // when it can recognize a clean `checkout.session.completed` shape with a single
94
+ // obvious session binding. Anything ambiguous → `unrecognized`, and the caller
95
+ // PRINTS the exact patch instead of forcing an edit. The transform is pure (no
96
+ // fs) and idempotent (a trackSale / @affitor/sdk/server marker → `already`).
97
+ /** Markers that mean a trackSale call (or the server import) is already wired. */
98
+ const TRACK_SALE_MARKER = "affitor.trackSale(";
99
+ const SERVER_IMPORT_MARKER = "@affitor/sdk/server";
100
+ /** The canonical Stripe webhook-verification call (Node SDK) we anchor on. */
101
+ const CONSTRUCT_EVENT_NEEDLE = "stripe.webhooks.constructEvent";
102
+ /** Drop the recipe's leading `@affitor/sdk/server` import + its blank line. */
103
+ function saleCallBody(snippet) {
104
+ return snippet
105
+ .replace(/^import \{ Affitor \} from '@affitor\/sdk\/server';\n+/, "")
106
+ .trimEnd();
107
+ }
108
+ /** Re-indent a multi-line snippet body to a given base indent. */
109
+ function indentBody(body, indent) {
110
+ return body
111
+ .split("\n")
112
+ .map((line) => (line.length ? indent + line : line))
113
+ .join("\n");
114
+ }
115
+ /** The import line for the affitor client given a module specifier. */
116
+ function affitorImportLine(importSpecifier) {
117
+ return `import { affitor } from '${importSpecifier}';`;
118
+ }
119
+ /**
120
+ * Insert `affitor.trackSale(...)` into a Stripe webhook handler, right after the
121
+ * verified `checkout.session.completed` session object is bound.
122
+ *
123
+ * CONSERVATIVE BY DESIGN — only injects when ALL of these hold (else returns
124
+ * `unrecognized` so the caller prints the patch instead of editing payment code):
125
+ * 1. The file contains `stripe.webhooks.constructEvent` (it really is a
126
+ * verified Stripe webhook — we never edit an unverified handler).
127
+ * 2. There is a single `checkout.session.completed` reference to anchor on
128
+ * (the recipe's stripe snippet reads `session.*`, so the sale belongs in
129
+ * that case). Zero or multiple matches → unrecognized (ambiguous).
130
+ * 3. After that anchor, exactly one line binds the event object to a name we
131
+ * can reuse — `const session = event.data.object …` (or `= session …`).
132
+ * The trackSale call is inserted on the line after that binding, at the
133
+ * binding's own indentation. No binding, or an unexpected variable name →
134
+ * unrecognized.
135
+ *
136
+ * Returns `already` when a `affitor.trackSale(` / `@affitor/sdk/server` marker is
137
+ * already present (idempotent re-runs do nothing).
138
+ */
139
+ export function injectStripeTrackSale(content, opts) {
140
+ // Idempotent: never double-inject.
141
+ // Check for trackSale marker, server import marker, OR the affitor client import
142
+ // (the specifier we would add) — any of these mean the file is already wired.
143
+ const clientImportLine = opts.importSpecifier ? affitorImportLine(opts.importSpecifier) : null;
144
+ if (content.includes(TRACK_SALE_MARKER) ||
145
+ content.includes(SERVER_IMPORT_MARKER) ||
146
+ (clientImportLine && content.includes(clientImportLine))) {
147
+ return { content, status: "already", added: [] };
148
+ }
149
+ // (1) Must be a verified Stripe webhook — anchor on constructEvent.
150
+ if (!content.includes(CONSTRUCT_EVENT_NEEDLE)) {
151
+ return { content, status: "unrecognized", added: [] };
152
+ }
153
+ // (2) Exactly one checkout.session.completed reference (the sale anchor).
154
+ // Zero (some other event) or many (ambiguous which case) → bail.
155
+ const completedRe = /checkout\.session\.completed/g;
156
+ const completedMatches = content.match(completedRe);
157
+ if (!completedMatches || completedMatches.length !== 1) {
158
+ return { content, status: "unrecognized", added: [] };
159
+ }
160
+ const anchorIdx = content.indexOf("checkout.session.completed");
161
+ // (3) After the anchor, find a single clean session binding to insert after.
162
+ // Accept `const session = event.data.object …` or `const session = session …`
163
+ // (cast variants). The variable MUST be named `session` — the recipe's stripe
164
+ // snippet reads `session.client_reference_id` / `session.amount_total` /
165
+ // `session.id`, so any other name would not compile.
166
+ const after = content.slice(anchorIdx);
167
+ const bindingRe = /^([ \t]*)const session\s*=\s*(?:event\.data\.object|session)\b[^\n]*$/m;
168
+ const bindingMatch = after.match(bindingRe);
169
+ if (!bindingMatch || bindingMatch.index === undefined) {
170
+ return { content, status: "unrecognized", added: [] };
171
+ }
172
+ // Absolute offset of the end of the binding line (insertion point).
173
+ const bindingLineStart = anchorIdx + bindingMatch.index;
174
+ const bindingLineEnd = bindingLineStart + bindingMatch[0].length;
175
+ const indent = opts.indent ?? bindingMatch[1] ?? " ";
176
+ const body = saleCallBody(opts.saleSnippet);
177
+ if (!body.includes(TRACK_SALE_MARKER)) {
178
+ // The provided snippet isn't a trackSale call — refuse rather than guess.
179
+ return { content, status: "unrecognized", added: [] };
180
+ }
181
+ const block = indentBody(body, indent);
182
+ const insertion = `\n${indent}// Affitor: report the sale (auto-added by \`affitor onboard\`)\n${block}`;
183
+ const withSale = content.slice(0, bindingLineEnd) + insertion + content.slice(bindingLineEnd);
184
+ // Also add the affitor client import when a specifier was provided.
185
+ let finalContent = withSale;
186
+ const added = insertion.split("\n").filter((l) => l.trim().length > 0);
187
+ if (clientImportLine) {
188
+ finalContent = addImport(withSale, clientImportLine);
189
+ added.unshift(clientImportLine);
190
+ }
191
+ return { content: finalContent, status: "injected", added };
192
+ }
81
193
  //# sourceMappingURL=inject.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"inject.js","sourceRoot":"","sources":["../../src/lib/inject.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,cAAc,GAAG,gBAAgB,CAAC;AAWxC,gFAAgF;AAChF,MAAM,UAAU,sBAAsB,CAAC,SAA0B,EAAE,kBAA2B;IAC5F,MAAM,SAAS,GAAG,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7F,MAAM,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,OAAO,GAAG,MAAM;;;;;;;kBAOA,cAAc;;wBAER,SAAS;;;;CAIhC,CAAC;AACF,CAAC;AAED,SAAS,aAAa,CAAC,eAAuB;IAC5C,OAAO,YAAY,cAAc,YAAY,eAAe,IAAI,CAAC;AACnE,CAAC;AAED,mFAAmF;AACnF,SAAS,SAAS,CAAC,OAAe,EAAE,UAAkB;IACpD,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,OAAO,CAAC;IACjD,MAAM,QAAQ,GAAG,eAAe,CAAC;IACjC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC;IACjB,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7C,OAAO,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAClC,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,CAAC;QAAE,OAAO,GAAG,UAAU,KAAK,OAAO,EAAE,CAAC;IACvD,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,GAAG,KAAK,UAAU,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,eAAuB;IACtE,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAEvF,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC9C,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAErE,MAAM,UAAU,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAElD,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;IACjD,MAAM,QAAQ,GAAG,YAAY,cAAc,KAAK,CAAC;IACjD,MAAM,QAAQ,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAEpF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;AACzF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,eAAuB;IACrE,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAEvF,MAAM,MAAM,GAAG,wCAAwC,CAAC;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAElE,MAAM,UAAU,GAAG,aAAa,CAAC,eAAe,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,WAAW,GAAG,cAAc,cAAc,cAAc,SAAS,WAAW,CAAC;IACnF,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAEzD,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,UAAU,EAAE,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;AACjG,CAAC"}
1
+ {"version":3,"file":"inject.js","sourceRoot":"","sources":["../../src/lib/inject.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,12 @@
1
1
  /**
2
- * Server-side conversion tracking helpers for the wizard (affitor-node path).
2
+ * Server-side conversion tracking helpers for the wizard (@affitor/sdk/server path).
3
3
  * Pure functions (detection + generated source/snippets) — easy to unit test.
4
- * The wizard installs affitor-node + scaffolds the client file, then PRINTS the
4
+ * The wizard installs @affitor/sdk + scaffolds the client file, then PRINTS the
5
5
  * trackLead/trackSale snippets — it never edits auth/payment code (per PR-4 §9.3).
6
+ *
7
+ * The per-provider `sale` snippet is SOURCED FROM `@affitor/recipes` (the canonical
8
+ * single source of truth read by CLI + MCP + docs). Do NOT re-inline the snippet
9
+ * strings here — edit them in `@affitor/recipes` so all three surfaces stay in sync.
6
10
  */
7
11
  export type PaymentProvider = "stripe" | "polar" | "lemonsqueezy" | "paddle" | "unknown";
8
12
  /** Detect the advertiser's payment provider from package.json deps. */
@@ -1 +1 @@
1
- {"version":3,"file":"server-tracking.d.ts","sourceRoot":"","sources":["../../src/lib/server-tracking.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AAEH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AASzF,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAYlE;AAED,6FAA6F;AAC7F,wBAAgB,mBAAmB,IAAI,MAAM,CAS5C;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,EACzB,UAAU,SAAkB,GAC3B,gBAAgB,CAsElB"}
1
+ {"version":3,"file":"server-tracking.d.ts","sourceRoot":"","sources":["../../src/lib/server-tracking.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,OAAO,GAAG,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AASzF,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAYlE;AAED,6FAA6F;AAC7F,wBAAgB,mBAAmB,IAAI,MAAM,CAS5C;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAeD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,EACzB,UAAU,SAAkB,GAC3B,gBAAgB,CAwClB"}
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { getRecipe } from "@affitor/recipes";
3
4
  const PROVIDER_DEPS = {
4
5
  stripe: ["stripe", "@stripe/stripe-js"],
5
6
  polar: ["@polar-sh/sdk", "@polar-sh/nextjs", "@polar-sh/express"],
@@ -24,7 +25,7 @@ export function detectPaymentProvider(cwd) {
24
25
  }
25
26
  /** Source for the scaffolded `lib/affitor.ts` server client (a NEW file — safe to write). */
26
27
  export function affitorClientSource() {
27
- return `import { Affitor } from 'affitor-node';
28
+ return `import { Affitor } from '@affitor/sdk/server';
28
29
 
29
30
  /**
30
31
  * Affitor server-side tracking client — auto-generated by \`affitor init\`.
@@ -33,6 +34,18 @@ export function affitorClientSource() {
33
34
  export const affitor = new Affitor({ apiKey: process.env.AFFITOR_API_KEY ?? '' });
34
35
  `;
35
36
  }
37
+ /**
38
+ * The trackSale snippet body for a provider, sourced from the canonical
39
+ * `@affitor/recipes` registry. We use framework "unknown" (the snippet body is
40
+ * framework-independent) + mode "s2s" (so a sale snippet exists, not the
41
+ * Connect/metadata-only path), then strip the leading `@affitor/sdk/server`
42
+ * import + its blank line — the wizard prints that import via the lead snippet.
43
+ */
44
+ function recipeSaleBody(provider) {
45
+ const recipe = getRecipe("unknown", provider, "s2s");
46
+ const snippet = recipe.sale?.snippet ?? "";
47
+ return snippet.replace(/^import \{ Affitor \} from '@affitor\/sdk\/server';\n\n/, "");
48
+ }
36
49
  /**
37
50
  * Tailored trackLead + trackSale snippets. The sale snippet is provider-specific
38
51
  * (event name + field paths from the verified provider research). Printed, not
@@ -44,60 +57,29 @@ export function serverTrackingSnippets(provider, importPath = "@/lib/affitor") {
44
57
  `// After a user signs up — binds the affiliate click to your user id:`,
45
58
  `await affitor.trackLead({ customerExternalId: user.id, clickId: cookies.affitor_click_id });`,
46
59
  ].join("\n");
60
+ // The trackSale snippet body is canonical in @affitor/recipes. We pull it from
61
+ // there (framework "unknown" — the body is framework-independent; only the
62
+ // inject_target differs, which the wizard doesn't print) and strip the SDK
63
+ // import line the recipe prepends (the wizard already prints the import as part
64
+ // of the lead snippet, via `importPath`).
65
+ const sale = recipeSaleBody(provider);
47
66
  let saleContext;
48
- let sale;
49
67
  switch (provider) {
50
68
  case "polar":
51
69
  saleContext = "in your Polar webhook handler (event 'order.paid')";
52
- sale = [
53
- `await affitor.trackSale({`,
54
- ` customerExternalId: order.metadata.user_id ?? order.customer_id,`,
55
- ` amount: order.total_amount, // integer cents`,
56
- ` invoiceId: order.id,`,
57
- ` saleType: order.subscription_id ? 'subscription' : 'payment',`,
58
- `});`,
59
- ].join("\n");
60
70
  break;
61
71
  case "lemonsqueezy":
62
72
  saleContext = "in your Lemon Squeezy webhook handler (event 'order_created')";
63
- sale = [
64
- `await affitor.trackSale({`,
65
- ` customerExternalId: payload.meta.custom_data?.user_id ?? data.attributes.user_email,`,
66
- ` amount: data.attributes.total, // integer cents`,
67
- ` invoiceId: String(data.id),`,
68
- `});`,
69
- ].join("\n");
70
73
  break;
71
74
  case "paddle":
72
75
  saleContext = "in your Paddle webhook handler (event 'transaction.completed')";
73
- sale = [
74
- `await affitor.trackSale({`,
75
- ` customerExternalId: data.custom_data?.user_id,`,
76
- ` amount: Number(data.details.totals.total), // cents`,
77
- ` invoiceId: data.id,`,
78
- `});`,
79
- ].join("\n");
80
76
  break;
81
77
  case "stripe":
82
78
  saleContext =
83
79
  "Stripe: prefer Connect autocapture (`affitor stripe connect`). To track manually, in your checkout.session.completed handler";
84
- sale = [
85
- `await affitor.trackSale({`,
86
- ` customerExternalId: session.client_reference_id,`,
87
- ` amount: session.amount_total, // cents`,
88
- ` invoiceId: session.id,`,
89
- `});`,
90
- ].join("\n");
91
80
  break;
92
81
  default:
93
82
  saleContext = "in your payment provider's webhook handler, when a payment succeeds";
94
- sale = [
95
- `await affitor.trackSale({`,
96
- ` customerExternalId: user.id,`,
97
- ` amount: amountInCents,`,
98
- ` invoiceId: transactionId,`,
99
- `});`,
100
- ].join("\n");
101
83
  }
102
84
  const refund = [
103
85
  `// In your provider's refund webhook (e.g. order.refunded), reverse the commission`,
@@ -1 +1 @@
1
- {"version":3,"file":"server-tracking.js","sourceRoot":"","sources":["../../src/lib/server-tracking.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server-tracking.js","sourceRoot":"","sources":["../../src/lib/server-tracking.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,14 @@
1
- export type Framework = "next-app" | "next-pages" | "unknown";
1
+ export type Framework = "next-app" | "next-pages" | "fastify" | "express" | "node" | "unknown";
2
2
  export type PackageManager = "npm" | "yarn" | "pnpm";
3
3
  export interface StackInfo {
4
4
  framework: Framework;
5
5
  packageManager: PackageManager;
6
- /** Absolute path to the entry file to wire (layout.tsx / _app.tsx), or null. */
6
+ /**
7
+ * Absolute path to the entry file to wire (layout.tsx / _app.tsx), or null.
8
+ * For server frameworks (fastify/express/node) this is the detected server
9
+ * entry if findable (best-effort), else null — the wizard only auto-wires the
10
+ * Next entry files, so a null here routes the new stacks to printed snippets.
11
+ */
7
12
  entryFile: string | null;
8
13
  /** Directory to create the tracker component in, or null. */
9
14
  componentDir: string | null;
@@ -11,8 +16,11 @@ export interface StackInfo {
11
16
  export declare function detectPackageManager(cwd: string): PackageManager;
12
17
  /**
13
18
  * Detect the framework + package manager + entry file of the project at `cwd`.
14
- * Next.js only for now (App Router preferred, then Pages Router); everything
15
- * else is `unknown` so the wizard falls back to printed instructions.
19
+ * Order is most-specific first: next-app → next-pages → fastify → express →
20
+ * node (a package.json + a server signal but no framework) → unknown. Next.js
21
+ * is the only stack the wizard auto-wires; server frameworks expose a
22
+ * best-effort `entryFile` but `componentDir: null` keeps them on the printed
23
+ * snippet path (auto-injection is a later task).
16
24
  */
17
25
  export declare function detectStack(cwd: string): StackInfo;
18
26
  //# sourceMappingURL=stack-detect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stack-detect.d.ts","sourceRoot":"","sources":["../../src/lib/stack-detect.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,SAAS,CAAC;AAC9D,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,gFAAgF;IAChF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAWD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAIhE;AAUD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CA2BlD"}
1
+ {"version":3,"file":"stack-detect.d.ts","sourceRoot":"","sources":["../../src/lib/stack-detect.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GACjB,UAAU,GACV,YAAY,GACZ,SAAS,GACT,SAAS,GACT,MAAM,GACN,SAAS,CAAC;AACd,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAWD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAIhE;AAwDD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAwClD"}
@@ -24,10 +24,63 @@ function firstExisting(cwd, candidates) {
24
24
  }
25
25
  return null;
26
26
  }
27
+ /** Common server entry candidates, in priority order, for non-Next stacks. */
28
+ const SERVER_ENTRY_CANDIDATES = [
29
+ "src/server.ts",
30
+ "src/server.js",
31
+ "src/index.ts",
32
+ "src/index.js",
33
+ "src/app.ts",
34
+ "src/app.js",
35
+ "src/main.ts",
36
+ "src/main.js",
37
+ "server.ts",
38
+ "server.js",
39
+ "index.ts",
40
+ "index.js",
41
+ "app.ts",
42
+ "app.js",
43
+ ];
44
+ /** Best-effort server entry: the package.json `main`, then common filenames. */
45
+ function findServerEntry(cwd) {
46
+ try {
47
+ const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
48
+ if (typeof pkg.main === "string" && pkg.main) {
49
+ const mainPath = join(cwd, pkg.main);
50
+ if (existsSync(mainPath))
51
+ return mainPath;
52
+ }
53
+ }
54
+ catch {
55
+ /* fall through to filename probing */
56
+ }
57
+ return firstExisting(cwd, SERVER_ENTRY_CANDIDATES);
58
+ }
59
+ /** Heuristic: does this look like a runnable Node service (no framework dep)? */
60
+ function hasNodeServerSignal(cwd, deps) {
61
+ if (deps["stripe"])
62
+ return true; // server-ish: payment provider implies a backend
63
+ try {
64
+ const pkg = JSON.parse(readFileSync(join(cwd, "package.json"), "utf8"));
65
+ if (typeof pkg.main === "string" && pkg.main)
66
+ return true;
67
+ if (pkg.bin)
68
+ return true;
69
+ if (pkg.scripts && (pkg.scripts.start || pkg.scripts.serve))
70
+ return true;
71
+ }
72
+ catch {
73
+ return false;
74
+ }
75
+ return false;
76
+ }
27
77
  /**
28
78
  * Detect the framework + package manager + entry file of the project at `cwd`.
29
- * Next.js only for now (App Router preferred, then Pages Router); everything
30
- * else is `unknown` so the wizard falls back to printed instructions.
79
+ * Order is most-specific first: next-app → next-pages → fastify → express →
80
+ * node (a package.json + a server signal but no framework) → unknown. Next.js
81
+ * is the only stack the wizard auto-wires; server frameworks expose a
82
+ * best-effort `entryFile` but `componentDir: null` keeps them on the printed
83
+ * snippet path (auto-injection is a later task).
31
84
  */
32
85
  export function detectStack(cwd) {
33
86
  const packageManager = detectPackageManager(cwd);
@@ -52,6 +105,18 @@ export function detectStack(cwd) {
52
105
  return { framework: "next-pages", packageManager, entryFile: pagesApp, componentDir: dirname(pagesApp) };
53
106
  }
54
107
  }
108
+ // Server frameworks (Echoly-class: Fastify + TS + Postgres + Stripe webhook).
109
+ // entryFile is best-effort; componentDir stays null (no browser component to
110
+ // scaffold on a backend), which keeps these stacks on the printed-snippet path.
111
+ if (deps["fastify"]) {
112
+ return { framework: "fastify", packageManager, entryFile: findServerEntry(cwd), componentDir: null };
113
+ }
114
+ if (deps["express"]) {
115
+ return { framework: "express", packageManager, entryFile: findServerEntry(cwd), componentDir: null };
116
+ }
117
+ if (hasNodeServerSignal(cwd, deps)) {
118
+ return { framework: "node", packageManager, entryFile: findServerEntry(cwd), componentDir: null };
119
+ }
55
120
  return { framework: "unknown", packageManager, entryFile: null, componentDir: null };
56
121
  }
57
122
  //# sourceMappingURL=stack-detect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stack-detect.js","sourceRoot":"","sources":["../../src/lib/stack-detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAc1C,SAAS,QAAQ,CAAC,GAAW;IAC3B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACxE,OAAO,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,EAAE,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC3D,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACtD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,GAAW,EAAE,UAAoB;IACtD,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QAC1B,IAAI,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,MAAM,cAAc,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAE3B,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,EAAE;YACnC,gBAAgB;YAChB,gBAAgB;YAChB,oBAAoB;YACpB,oBAAoB;SACrB,CAAC,CAAC;QACH,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3G,CAAC;QAED,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,EAAE;YAClC,gBAAgB;YAChB,gBAAgB;YAChB,oBAAoB;YACpB,oBAAoB;SACrB,CAAC,CAAC;QACH,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3G,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;AACvF,CAAC"}
1
+ {"version":3,"file":"stack-detect.js","sourceRoot":"","sources":["../../src/lib/stack-detect.ts"],"names":[],"mappings":"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"}