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.
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/onboard.d.ts +21 -0
- package/dist/commands/onboard.d.ts.map +1 -0
- package/dist/commands/onboard.js +401 -0
- package/dist/commands/onboard.js.map +1 -0
- package/dist/lib/api-client.d.ts +25 -1
- package/dist/lib/api-client.d.ts.map +1 -1
- package/dist/lib/api-client.js +69 -2
- package/dist/lib/api-client.js.map +1 -1
- package/dist/lib/config.js +2 -2
- package/dist/lib/inject.d.ts +44 -0
- package/dist/lib/inject.d.ts.map +1 -1
- package/dist/lib/inject.js +115 -3
- package/dist/lib/inject.js.map +1 -1
- package/dist/lib/server-tracking.d.ts +6 -2
- package/dist/lib/server-tracking.d.ts.map +1 -1
- package/dist/lib/server-tracking.js +20 -38
- package/dist/lib/server-tracking.js.map +1 -1
- package/dist/lib/stack-detect.d.ts +12 -4
- package/dist/lib/stack-detect.d.ts.map +1 -1
- package/dist/lib/stack-detect.js +67 -2
- package/dist/lib/stack-detect.js.map +1 -1
- package/dist/lib/webhook-detect.d.ts +21 -0
- package/dist/lib/webhook-detect.d.ts.map +1 -0
- package/dist/lib/webhook-detect.js +111 -0
- package/dist/lib/webhook-detect.js.map +1 -0
- package/dist/lib/wizard.d.ts +1 -1
- package/dist/lib/wizard.d.ts.map +1 -1
- package/dist/lib/wizard.js +37 -13
- package/dist/lib/wizard.js.map +1 -1
- package/dist/types.d.ts +77 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -3
package/dist/lib/api-client.js
CHANGED
|
@@ -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
|
-
|
|
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":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAU9C,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAuB5C,MAAM,OAAO,UAAU;IACb,MAAM,CAAS;IACf,MAAM,CAAU;IAExB,YAAY,OAA6C,EAAE;QACzD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,eAAe,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,KAA2C,EAAE,GAAY;QACxE,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACzC,OAAO,IAAI,UAAU,CAAC;YACpB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,eAAe;YACvC,MAAM,EAAE,MAAM,IAAI,SAAS;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAQjB;QACC,OAAO,IAAI,CAAC,OAAO,CAAe,kBAAkB,EAAE;YACpD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,IAK1B;QACC,OAAO,IAAI,CAAC,OAAO,CAAwB,4BAA4B,EAAE;YACvE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,SAAiB;QAC/B,OAAO,IAAI,CAAC,OAAO,CACjB,iCAAiC,SAAS,EAAE,CAC7C,CAAC;IACJ,CAAC;IAED,iEAAiE;IAEjE,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,OAAO,CAAoB,wBAAwB,EAAE;YAC/D,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,KAAa;QAC1B,OAAO,IAAI,CAAC,OAAO,CACjB,+BAA+B,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAC3D,CAAC;IACJ,CAAC;IAED,mEAAmE;IAEnE,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,OAAO,CAAmB,sBAAsB,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,IAGnB;QACC,OAAO,IAAI,CAAC,OAAO,CAAkB,wBAAwB,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,IAAI;SACX,CAAC,CAAC;IACL,CAAC;IAED,mEAAmE;IAEnE;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,OAA6C,EAAE;QAChE,OAAO,IAAI,CAAC,OAAO,CAAkB,+BAA+B,EAAE;YACpE,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,oBAAoB,CACxB,OAA6C,EAAE;QAE/C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;QACvC,MAAM,GAAG,GAAG,GAAG,MAAM,wBAAwB,CAAC;QAE9C,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,YAAY,EAAE,mBAAmB;SAClC,CAAC;QACF,IAAI,GAAG;YAAE,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,GAAG,EAAE,CAAC;QAEpD,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,iBAAiB,CAAC,CAAC;QAE3C,IAAI,GAAa,CAAC;QAClB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBACrB,MAAM,EAAE,MAAM;gBACd,OAAO;gBACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;aACxC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,YAAY,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAA4B,CAAC;QAE7E,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;YACX,4EAA4E;YAC5E,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAA4B,CAAC;QACxD,CAAC;QAED,4EAA4E;QAC5E,+EAA+E;QAC/E,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAoD,CAAC;QACrF,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QACnD,MAAM,UAAU,GACd,MAAM,CAAC,mBAAmB,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAEtF,OAAO;YACL,GAAI,IAAgC;YACpC,WAAW,EAAE,GAAG,CAAC,MAAM;YACvB,YAAY,EAAE,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc;YAClE,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzE,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,OAAO,CAAI,IAAY,EAAE,OAAuB,EAAE;QAC9D,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;QAEvC,MAAM,OAAO,GAA2B;YACtC,cAAc,EAAE,kBAAkB;YAClC,YAAY,EAAE,mBAAmB;SAClC,CAAC;QACF,IAAI,GAAG,EAAE,CAAC;YACR,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,GAAG,EAAE,CAAC;QAC7C,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;QAE/C,IAAI,SAA4B,CAAC;QACjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;oBAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;oBAC5B,OAAO;oBACP,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;iBACxD,CAAC,CAAC;gBAEH,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBACvB,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;oBAClD,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;oBACjE,MAAM,IAAI,QAAQ,CAChB,GAAG,EACH,sBAAsB,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,yBAAyB,CACtE,CAAC;gBACJ,CAAC;gBAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAG/C,CAAC;oBACF,oEAAoE;oBACpE,gEAAgE;oBAChE,0DAA0D;oBAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC;oBAC5B,MAAM,OAAO,GACX,CAAC,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC;wBAC7D,IAAI,CAAC,OAAO;wBACZ,gBAAgB,GAAG,CAAC,MAAM,EAAE,CAAC;oBAC/B,MAAM,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAC1C,CAAC;gBAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC9B,OAAQ,IAAqB,CAAC,IAAI,IAAK,IAAU,CAAC;YACpD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,QAAQ;oBAAE,MAAM,GAAG,CAAC;gBACvC,SAAS,GAAG,GAAY,CAAC;gBACzB,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,MAAM,IAAI,GAAG,OAAO,GAAG,IAAI,CAAC;oBAC5B,MAAM,CAAC,KAAK,CAAC,+BAA+B,IAAI,OAAO,CAAC,CAAC;oBACzD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,YAAY,CAAC,SAAS,EAAE,OAAO,IAAI,iCAAiC,CAAC,CAAC;IAClF,CAAC;CACF;AAED,MAAM,OAAO,QAAS,SAAQ,KAAK;IAExB;IADT,YACS,MAAc,EACrB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHR,WAAM,GAAN,MAAM,CAAQ;QAIrB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,kBAAkB,OAAO,kDAAkD,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF"}
|
package/dist/lib/config.js
CHANGED
|
@@ -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/
|
|
231
|
+
npm install @affitor/sdk
|
|
232
232
|
\`\`\`
|
|
233
233
|
|
|
234
234
|
\`\`\`tsx
|
|
235
|
-
import { AffitorProvider } from '@affitor/
|
|
235
|
+
import { AffitorProvider } from '@affitor/sdk/react';
|
|
236
236
|
|
|
237
237
|
<AffitorProvider programId="${config.program_id}">
|
|
238
238
|
{children}
|
package/dist/lib/inject.d.ts
CHANGED
|
@@ -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
|
package/dist/lib/inject.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/lib/inject.js
CHANGED
|
@@ -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
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
package/dist/lib/inject.js.map
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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":"
|
|
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
|
|
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":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"server-tracking.js","sourceRoot":"","sources":["../../src/lib/server-tracking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAmC,MAAM,kBAAkB,CAAC;AAe9E,MAAM,aAAa,GAA0D;IAC3E,MAAM,EAAE,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,KAAK,EAAE,CAAC,eAAe,EAAE,kBAAkB,EAAE,mBAAmB,CAAC;IACjE,YAAY,EAAE,CAAC,+BAA+B,CAAC;IAC/C,MAAM,EAAE,CAAC,yBAAyB,EAAE,mBAAmB,CAAC;CACzD,CAAC;AAEF,uEAAuE;AACvE,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,IAAI,IAAI,GAA2B,EAAE,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACxE,IAAI,GAAG,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,SAAS,CAAC;IACnB,CAAC;IACD,KAAK,MAAM,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,QAA2B,CAAC;IACrE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,mBAAmB;IACjC,OAAO;;;;;;;CAOR,CAAC;AACF,CAAC;AAUD;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,QAAyB;IAC/C,MAAM,MAAM,GAAG,SAAS,CAAC,SAAS,EAAE,QAA0B,EAAE,KAAK,CAAC,CAAC;IACvE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;IAC3C,OAAO,OAAO,CAAC,OAAO,CAAC,yDAAyD,EAAE,EAAE,CAAC,CAAC;AACxF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAyB,EACzB,UAAU,GAAG,eAAe;IAE5B,MAAM,IAAI,GAAG;QACX,4BAA4B,UAAU,IAAI;QAC1C,uEAAuE;QACvE,8FAA8F;KAC/F,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,+EAA+E;IAC/E,2EAA2E;IAC3E,2EAA2E;IAC3E,gFAAgF;IAChF,0CAA0C;IAC1C,MAAM,IAAI,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAEtC,IAAI,WAAmB,CAAC;IACxB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,OAAO;YACV,WAAW,GAAG,oDAAoD,CAAC;YACnE,MAAM;QACR,KAAK,cAAc;YACjB,WAAW,GAAG,+DAA+D,CAAC;YAC9E,MAAM;QACR,KAAK,QAAQ;YACX,WAAW,GAAG,gEAAgE,CAAC;YAC/E,MAAM;QACR,KAAK,QAAQ;YACX,WAAW;gBACT,8HAA8H,CAAC;YACjI,MAAM;QACR;YACE,WAAW,GAAG,qEAAqE,CAAC;IACxF,CAAC;IAED,MAAM,MAAM,GAAG;QACb,oFAAoF;QACpF,mEAAmE;QACnE,qFAAqF;KACtF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACzD,CAAC"}
|
|
@@ -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
|
-
/**
|
|
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
|
-
*
|
|
15
|
-
*
|
|
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,
|
|
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"}
|
package/dist/lib/stack-detect.js
CHANGED
|
@@ -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
|
-
*
|
|
30
|
-
*
|
|
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;
|
|
1
|
+
{"version":3,"file":"stack-detect.js","sourceRoot":"","sources":["../../src/lib/stack-detect.ts"],"names":[],"mappings":"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"}
|