@gurulu/cli 0.4.6 → 1.0.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.
Files changed (180) hide show
  1. package/LICENSE +92 -0
  2. package/README.md +35 -106
  3. package/dist/bin.d.ts +3 -0
  4. package/dist/bin.d.ts.map +1 -0
  5. package/dist/bin.js +25410 -0
  6. package/dist/commands/auth.d.ts +23 -20
  7. package/dist/commands/auth.d.ts.map +1 -0
  8. package/dist/commands/doctor.d.ts +20 -6
  9. package/dist/commands/doctor.d.ts.map +1 -0
  10. package/dist/commands/init.d.ts +25 -11
  11. package/dist/commands/init.d.ts.map +1 -0
  12. package/dist/commands/pull.d.ts +13 -0
  13. package/dist/commands/pull.d.ts.map +1 -0
  14. package/dist/commands/push.d.ts +40 -0
  15. package/dist/commands/push.d.ts.map +1 -0
  16. package/dist/commands/validate.d.ts +36 -0
  17. package/dist/commands/validate.d.ts.map +1 -0
  18. package/dist/index.d.ts +4 -1
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +24985 -853
  21. package/dist/lib/api.d.ts +139 -0
  22. package/dist/lib/api.d.ts.map +1 -0
  23. package/dist/lib/codegen.d.ts +4 -0
  24. package/dist/lib/codegen.d.ts.map +1 -0
  25. package/dist/lib/config.d.ts +43 -0
  26. package/dist/lib/config.d.ts.map +1 -0
  27. package/package.json +40 -20
  28. package/bin/gurulu.js +0 -2
  29. package/dist/api-client.d.ts +0 -33
  30. package/dist/api-client.js +0 -175
  31. package/dist/commands/add-server.d.ts +0 -9
  32. package/dist/commands/add-server.js +0 -162
  33. package/dist/commands/alerts.d.ts +0 -27
  34. package/dist/commands/alerts.js +0 -309
  35. package/dist/commands/api-keys.d.ts +0 -20
  36. package/dist/commands/api-keys.js +0 -130
  37. package/dist/commands/attribution.d.ts +0 -22
  38. package/dist/commands/attribution.js +0 -111
  39. package/dist/commands/audiences.d.ts +0 -23
  40. package/dist/commands/audiences.js +0 -243
  41. package/dist/commands/audit.d.ts +0 -20
  42. package/dist/commands/audit.js +0 -130
  43. package/dist/commands/auth.js +0 -249
  44. package/dist/commands/chat.d.ts +0 -18
  45. package/dist/commands/chat.js +0 -117
  46. package/dist/commands/config.d.ts +0 -10
  47. package/dist/commands/config.js +0 -92
  48. package/dist/commands/consent.d.ts +0 -27
  49. package/dist/commands/consent.js +0 -233
  50. package/dist/commands/conversion-paths.d.ts +0 -19
  51. package/dist/commands/conversion-paths.js +0 -55
  52. package/dist/commands/db.d.ts +0 -25
  53. package/dist/commands/db.js +0 -330
  54. package/dist/commands/destinations.d.ts +0 -20
  55. package/dist/commands/destinations.js +0 -191
  56. package/dist/commands/doctor.js +0 -360
  57. package/dist/commands/errors.d.ts +0 -27
  58. package/dist/commands/errors.js +0 -121
  59. package/dist/commands/events.d.ts +0 -33
  60. package/dist/commands/events.js +0 -349
  61. package/dist/commands/experiments.d.ts +0 -22
  62. package/dist/commands/experiments.js +0 -264
  63. package/dist/commands/funnels.d.ts +0 -17
  64. package/dist/commands/funnels.js +0 -203
  65. package/dist/commands/goals.d.ts +0 -18
  66. package/dist/commands/goals.js +0 -214
  67. package/dist/commands/heatmap.d.ts +0 -27
  68. package/dist/commands/heatmap.js +0 -112
  69. package/dist/commands/identity.d.ts +0 -29
  70. package/dist/commands/identity.js +0 -328
  71. package/dist/commands/init.js +0 -215
  72. package/dist/commands/insights.d.ts +0 -10
  73. package/dist/commands/insights.js +0 -65
  74. package/dist/commands/install.d.ts +0 -259
  75. package/dist/commands/install.js +0 -1590
  76. package/dist/commands/login.d.ts +0 -20
  77. package/dist/commands/login.js +0 -170
  78. package/dist/commands/logout.d.ts +0 -10
  79. package/dist/commands/logout.js +0 -41
  80. package/dist/commands/playground.d.ts +0 -11
  81. package/dist/commands/playground.js +0 -47
  82. package/dist/commands/releases.d.ts +0 -17
  83. package/dist/commands/releases.js +0 -54
  84. package/dist/commands/replay.d.ts +0 -18
  85. package/dist/commands/replay.js +0 -64
  86. package/dist/commands/secrets.d.ts +0 -19
  87. package/dist/commands/secrets.js +0 -145
  88. package/dist/commands/sites.d.ts +0 -18
  89. package/dist/commands/sites.js +0 -139
  90. package/dist/commands/skad.d.ts +0 -18
  91. package/dist/commands/skad.js +0 -53
  92. package/dist/commands/sourcemap.d.ts +0 -33
  93. package/dist/commands/sourcemap.js +0 -204
  94. package/dist/commands/status.d.ts +0 -7
  95. package/dist/commands/status.js +0 -136
  96. package/dist/commands/upgrade.d.ts +0 -21
  97. package/dist/commands/upgrade.js +0 -183
  98. package/dist/commands/warehouse.d.ts +0 -20
  99. package/dist/commands/warehouse.js +0 -65
  100. package/dist/commands/warehouses.d.ts +0 -17
  101. package/dist/commands/warehouses.js +0 -182
  102. package/dist/commands/watch.d.ts +0 -45
  103. package/dist/commands/watch.js +0 -258
  104. package/dist/commands/whoami.d.ts +0 -9
  105. package/dist/commands/whoami.js +0 -50
  106. package/dist/config.d.ts +0 -75
  107. package/dist/config.js +0 -329
  108. package/dist/frameworks/detect.d.ts +0 -8
  109. package/dist/frameworks/detect.js +0 -444
  110. package/dist/install-intent-proposal.d.ts +0 -99
  111. package/dist/install-intent-proposal.js +0 -202
  112. package/dist/utils/api.d.ts +0 -20
  113. package/dist/utils/api.js +0 -47
  114. package/dist/utils/config.d.ts +0 -13
  115. package/dist/utils/config.js +0 -30
  116. package/dist/utils/confirm.d.ts +0 -17
  117. package/dist/utils/confirm.js +0 -40
  118. package/dist/utils/dry-run.d.ts +0 -20
  119. package/dist/utils/dry-run.js +0 -67
  120. package/dist/utils/from-file.d.ts +0 -9
  121. package/dist/utils/from-file.js +0 -72
  122. package/dist/utils/redact.d.ts +0 -14
  123. package/dist/utils/redact.js +0 -48
  124. package/dist/utils/ui.d.ts +0 -14
  125. package/dist/utils/ui.js +0 -59
  126. package/scripts/.gitkeep +0 -0
  127. package/scripts/README-gurulu-agentic-install.md +0 -114
  128. package/scripts/README-gurulu-scan.md +0 -98
  129. package/scripts/audit-cli-scopes.mjs +0 -204
  130. package/scripts/backfill-tenant-id.mjs +0 -172
  131. package/scripts/backfill-tenant-links.ts +0 -252
  132. package/scripts/backup-clickhouse.sh +0 -27
  133. package/scripts/backup-postgres.sh +0 -19
  134. package/scripts/bootstrap-runtime-schema.mjs +0 -87
  135. package/scripts/bootstrap-stripe.mjs +0 -158
  136. package/scripts/gurulu-agentic-install.lib.cjs +0 -762
  137. package/scripts/gurulu-agentic-install.mjs +0 -623
  138. package/scripts/gurulu-scan.lib.cjs +0 -1509
  139. package/scripts/gurulu-scan.mjs +0 -91
  140. package/scripts/gurulu-verify-install.lib.cjs +0 -334
  141. package/scripts/gurulu-verify-install.mjs +0 -59
  142. package/scripts/init-ssl.sh +0 -26
  143. package/scripts/migrate-flow-graph-enums.sh +0 -86
  144. package/scripts/monitor-disk.sh +0 -24
  145. package/scripts/patches/astro.patch.cjs +0 -74
  146. package/scripts/patches/auto-instrument/ast-helper.cjs +0 -480
  147. package/scripts/patches/auto-instrument/astro.cjs +0 -273
  148. package/scripts/patches/auto-instrument/express.cjs +0 -383
  149. package/scripts/patches/auto-instrument/fastify.cjs +0 -262
  150. package/scripts/patches/auto-instrument/hono.cjs +0 -392
  151. package/scripts/patches/auto-instrument/index.cjs +0 -80
  152. package/scripts/patches/auto-instrument/nestjs.cjs +0 -286
  153. package/scripts/patches/auto-instrument/nextjs-app-router.cjs +0 -345
  154. package/scripts/patches/auto-instrument/nextjs-pages.cjs +0 -361
  155. package/scripts/patches/auto-instrument/remix.cjs +0 -168
  156. package/scripts/patches/auto-instrument/sdk-helper-map.cjs +0 -241
  157. package/scripts/patches/auto-instrument/singleton-helper.cjs +0 -193
  158. package/scripts/patches/auto-instrument/sveltekit.cjs +0 -161
  159. package/scripts/patches/auto-instrument/vite-react.cjs +0 -37
  160. package/scripts/patches/auto-instrument/vue.cjs +0 -196
  161. package/scripts/patches/express.patch.cjs +0 -99
  162. package/scripts/patches/fastify.patch.cjs +0 -108
  163. package/scripts/patches/index.cjs +0 -300
  164. package/scripts/patches/nestjs.patch.cjs +0 -112
  165. package/scripts/patches/nextjs-app-router.patch.cjs +0 -97
  166. package/scripts/patches/nextjs-pages.patch.cjs +0 -97
  167. package/scripts/patches/remix.patch.cjs +0 -75
  168. package/scripts/patches/sveltekit.patch.cjs +0 -72
  169. package/scripts/patches/vite-react.patch.cjs +0 -73
  170. package/scripts/patches/vue.patch.cjs +0 -82
  171. package/scripts/renew-ssl.sh +0 -14
  172. package/scripts/resolve-migration.sh +0 -23
  173. package/scripts/seed-cli-dev-keys.mjs +0 -130
  174. package/scripts/seed-test-data.mjs +0 -391
  175. package/scripts/spike-browserless.ts +0 -65
  176. package/scripts/tenant-pivot-consistency-check.mjs +0 -205
  177. package/scripts/tenant-pivot-phase-3-cleanup.lib.cjs +0 -258
  178. package/scripts/tenant-pivot-phase-3-cleanup.mjs +0 -98
  179. package/scripts/test-identity-resolution.ts +0 -804
  180. package/scripts/validate-gurulu-schemas.mjs +0 -79
@@ -1,161 +0,0 @@
1
- // scripts/patches/auto-instrument/sveltekit.cjs — Phase 20 W1 A3.
2
- //
3
- // SvelteKit routes live under `src/routes/**`. API routes use
4
- // `+server.ts` and export one function per HTTP verb (`GET`, `POST`, ...).
5
- // Form actions live in `+page.server.ts` and export a `load` function plus
6
- // `actions` object. This patcher targets the `+server.ts` + `+page.server.ts`
7
- // shape and inserts `gurulu.track(...)` before the last return of the
8
- // matching handler.
9
-
10
- const fs = require('fs');
11
- const path = require('path');
12
-
13
- const ast = require('./ast-helper.cjs');
14
- const singleton = require('./singleton-helper.cjs');
15
-
16
- const NAME = 'auto-instrument-sveltekit';
17
- const SUPPORTED = ['sveltekit'];
18
-
19
- function parseEventRoute(routeStr) {
20
- if (!routeStr || typeof routeStr !== 'string') return null;
21
- const m = routeStr.trim().match(/^([A-Z]+)\s+(\/.*)$/);
22
- if (!m) return null;
23
- return { method: m[1].toUpperCase(), urlPath: m[2].replace(/\/+$/, '') || '/' };
24
- }
25
-
26
- function routeToCandidates(urlPath) {
27
- const cleaned = urlPath.replace(/^\//, '').replace(/\/+$/, '');
28
- const segments = cleaned.split('/').filter(Boolean);
29
- const exts = ['ts', 'js'];
30
- const out = [];
31
- for (const ext of exts) {
32
- out.push(path.posix.join('src', 'routes', ...segments, `+server.${ext}`));
33
- out.push(path.posix.join('src', 'routes', ...segments, `+page.server.${ext}`));
34
- }
35
- return out;
36
- }
37
-
38
- function astInstrumentFile(source, method, events, opts = {}) {
39
- const tree = ast.parseSource(source);
40
- const fns = ast.findExportedFunction(tree, method);
41
- if (fns.length === 0) return { ok: false, reason: `${method}-not-found` };
42
- const target = fns[0];
43
- if (ast.hasInstrumentedMarker(target.fn)) {
44
- return {
45
- ok: true,
46
- after: source,
47
- instrumented: [],
48
- skipped: events.map((e) => ({ event: e.name, reason: 'already-instrumented' })),
49
- changed: false,
50
- };
51
- }
52
- const stmts = events.map((e) => ast.buildTrackStatement(e.name, e.autoProperties));
53
- ast.injectTrackBeforeLastReturn(target.fn, target.body, stmts);
54
- // Sprint D / D4 — alias-aware import.
55
- const specifier = ast.resolveGuruluImportSpecifier(opts.repoRoot, opts.relPath);
56
- ast.ensureGuruluImport(tree, specifier);
57
- const after = ast.generateSource(tree, source);
58
- return {
59
- ok: true,
60
- after,
61
- instrumented: events.map((e) => e.name),
62
- skipped: [],
63
- changed: true,
64
- };
65
- }
66
-
67
- function instrumentEvents(ctx, events) {
68
- const helper = singleton.ensureSingletonHelper(ctx, 'sveltekit');
69
- const changes = [...helper.changes];
70
- const notes = [...helper.notes];
71
- let eventsInstrumented = 0;
72
- let eventsSkipped = 0;
73
-
74
- if (helper.collision) {
75
- return {
76
- changes: [],
77
- notes,
78
- filesModified: 0,
79
- eventsInstrumented: 0,
80
- eventsSkipped: events ? events.length : 0,
81
- collision: true,
82
- };
83
- }
84
-
85
- const groups = new Map();
86
- for (const e of events || []) {
87
- const parsed = parseEventRoute(e && e.source && e.source.route);
88
- if (!parsed) {
89
- notes.push(`skip:${e && e.name}:no-source-route`);
90
- eventsSkipped++;
91
- continue;
92
- }
93
- const candidates = routeToCandidates(parsed.urlPath);
94
- const found = candidates.find((rel) => fs.existsSync(path.join(ctx.repoRoot, rel)));
95
- if (!found) {
96
- notes.push(`skip:${e.name}:route-file-not-found`);
97
- eventsSkipped++;
98
- continue;
99
- }
100
- const key = `${found}::${parsed.method}`;
101
- if (!groups.has(key)) groups.set(key, { relPath: found, method: parsed.method, events: [] });
102
- groups.get(key).events.push(e);
103
- }
104
-
105
- for (const group of groups.values()) {
106
- const abs = path.join(ctx.repoRoot, group.relPath);
107
- const before = fs.readFileSync(abs, 'utf8');
108
- // Sprint D / D4 — pass repoRoot + relPath to the import resolver.
109
- const fileOpts = { repoRoot: (ctx && ctx.repoRoot) || null, relPath: group.relPath };
110
- let res;
111
- try {
112
- res = astInstrumentFile(before, group.method, group.events, fileOpts);
113
- } catch (err) {
114
- const msg = (err && err.message) || String(err);
115
- // eslint-disable-next-line no-console
116
- console.warn(`[auto-instrument] patch.fallback ${group.relPath}: ${msg}`);
117
- notes.push(`patch.fallback:${group.relPath}:${msg}`);
118
- continue;
119
- }
120
- if (!res.ok) {
121
- for (const e of group.events) {
122
- notes.push(`skip:${e.name}:${res.reason}`);
123
- eventsSkipped++;
124
- }
125
- continue;
126
- }
127
- for (const s of res.skipped || []) {
128
- notes.push(`skip:${s.event}:${s.reason}`);
129
- eventsSkipped++;
130
- }
131
- if (res.changed) {
132
- changes.push({
133
- relPath: group.relPath,
134
- before,
135
- after: res.after,
136
- reason: `auto-instrument-sveltekit`,
137
- type: 'auto-instrument',
138
- });
139
- eventsInstrumented += (res.instrumented || []).length;
140
- }
141
- }
142
-
143
- const filesModified = changes.filter((c) => c.type === 'auto-instrument').length;
144
- return { changes, notes, filesModified, eventsInstrumented, eventsSkipped, collision: false };
145
- }
146
-
147
- function ensureSingletonHelper(ctx) {
148
- return singleton.ensureSingletonHelper(ctx, 'sveltekit');
149
- }
150
-
151
- module.exports = {
152
- name: NAME,
153
- supportedFrameworks: SUPPORTED,
154
- ensureSingletonHelper,
155
- instrumentEvents,
156
- _internals: {
157
- parseEventRoute,
158
- routeToCandidates,
159
- astInstrumentFile,
160
- },
161
- };
@@ -1,37 +0,0 @@
1
- // scripts/patches/auto-instrument/vite-react.cjs — Phase 18.7 B4.
2
- //
3
- // Vite+React is a pure-frontend framework, so there are no server-side
4
- // route handlers to instrument. This module is a graceful no-op so the
5
- // shared dispatcher in `scripts/gurulu-agentic-install.mjs` can still
6
- // resolve a module for the framework and receive a well-formed result.
7
-
8
- const NAME = 'auto-instrument-vite-react';
9
- const SUPPORTED = ['vite-react'];
10
-
11
- function ensureSingletonHelper() {
12
- return {
13
- changes: [],
14
- notes: ['vite-react is client-only; no singleton helper needed'],
15
- collision: false,
16
- };
17
- }
18
-
19
- function instrumentEvents(_ctx, events) {
20
- return {
21
- changes: [],
22
- notes: [
23
- 'vite-react is client-only; install client-side track calls via SDK automatically',
24
- ],
25
- filesModified: 0,
26
- eventsInstrumented: 0,
27
- eventsSkipped: events ? events.length : 0,
28
- collision: false,
29
- };
30
- }
31
-
32
- module.exports = {
33
- name: NAME,
34
- supportedFrameworks: SUPPORTED,
35
- ensureSingletonHelper,
36
- instrumentEvents,
37
- };
@@ -1,196 +0,0 @@
1
- // scripts/patches/auto-instrument/vue.cjs — Phase 20 W1 A3.
2
- //
3
- // Nuxt 3 / H3 server handlers. Routes live at
4
- // `server/api/**.{ts,js}` (or `server/routes/**`) and are declared with:
5
- //
6
- // export default defineEventHandler(async (event) => { ... });
7
- //
8
- // We parse the file, find the `export default defineEventHandler(...)` call,
9
- // and inject `gurulu.track(...)` into the inner arrow/function body before
10
- // the last return.
11
-
12
- const fs = require('fs');
13
- const path = require('path');
14
-
15
- const ast = require('./ast-helper.cjs');
16
- const singleton = require('./singleton-helper.cjs');
17
-
18
- const NAME = 'auto-instrument-vue';
19
- const SUPPORTED = ['vue', 'nuxt'];
20
-
21
- function parseEventRoute(routeStr) {
22
- if (!routeStr || typeof routeStr !== 'string') return null;
23
- const m = routeStr.trim().match(/^([A-Z]+)\s+(\/.*)$/);
24
- if (!m) return null;
25
- return { method: m[1].toUpperCase(), urlPath: m[2].replace(/\/+$/, '') || '/' };
26
- }
27
-
28
- function routeToCandidates(urlPath, method) {
29
- // Nuxt files can be:
30
- // server/api/users.ts — any method
31
- // server/api/users.post.ts — POST only
32
- // server/api/users/index.ts — any method
33
- // server/api/users/[id].ts — any method (dynamic)
34
- const cleaned = urlPath.replace(/^\//, '').replace(/\/+$/, '').replace(/^api\//, '');
35
- const segments = cleaned.split('/').filter(Boolean);
36
- const exts = ['ts', 'js', 'mjs'];
37
- const out = [];
38
- const lowerMethod = method.toLowerCase();
39
- for (const ext of exts) {
40
- out.push(path.posix.join('server', 'api', ...segments) + `.${lowerMethod}.${ext}`);
41
- out.push(path.posix.join('server', 'api', ...segments) + `.${ext}`);
42
- out.push(path.posix.join('server', 'api', ...segments, `index.${ext}`));
43
- out.push(path.posix.join('server', 'routes', ...segments) + `.${ext}`);
44
- }
45
- return out;
46
- }
47
-
48
- /**
49
- * Locate the inner handler function inside `export default defineEventHandler(fn)`.
50
- * Returns `{ fn, body }` or null.
51
- */
52
- function findEventHandler(tree) {
53
- const t = ast.t;
54
- let found = null;
55
- ast.traverse(tree, {
56
- ExportDefaultDeclaration(p) {
57
- const decl = p.node.declaration;
58
- if (!t.isCallExpression(decl)) return;
59
- if (!t.isIdentifier(decl.callee) || decl.callee.name !== 'defineEventHandler') return;
60
- const handler = decl.arguments[0];
61
- if (!handler) return;
62
- if (
63
- (t.isArrowFunctionExpression(handler) || t.isFunctionExpression(handler)) &&
64
- t.isBlockStatement(handler.body)
65
- ) {
66
- found = { fn: handler, body: handler.body };
67
- }
68
- },
69
- });
70
- return found;
71
- }
72
-
73
- function astInstrumentFile(source, events, opts = {}) {
74
- const tree = ast.parseSource(source);
75
- const target = findEventHandler(tree);
76
- if (!target) return { ok: false, reason: 'event-handler-not-found' };
77
- if (ast.hasInstrumentedMarker(target.fn)) {
78
- return {
79
- ok: true,
80
- after: source,
81
- instrumented: [],
82
- skipped: events.map((e) => ({ event: e.name, reason: 'already-instrumented' })),
83
- changed: false,
84
- };
85
- }
86
- const stmts = events.map((e) => ast.buildTrackStatement(e.name, e.autoProperties));
87
- ast.injectTrackBeforeLastReturn(target.fn, target.body, stmts);
88
- // Sprint D / D4 — alias-aware import.
89
- const specifier = ast.resolveGuruluImportSpecifier(opts.repoRoot, opts.relPath);
90
- ast.ensureGuruluImport(tree, specifier);
91
- const after = ast.generateSource(tree, source);
92
- return {
93
- ok: true,
94
- after,
95
- instrumented: events.map((e) => e.name),
96
- skipped: [],
97
- changed: true,
98
- };
99
- }
100
-
101
- function instrumentEvents(ctx, events) {
102
- const helper = singleton.ensureSingletonHelper(ctx, 'vue');
103
- const changes = [...helper.changes];
104
- const notes = [...helper.notes];
105
- let eventsInstrumented = 0;
106
- let eventsSkipped = 0;
107
-
108
- if (helper.collision) {
109
- return {
110
- changes: [],
111
- notes,
112
- filesModified: 0,
113
- eventsInstrumented: 0,
114
- eventsSkipped: events ? events.length : 0,
115
- collision: true,
116
- };
117
- }
118
-
119
- const groups = new Map();
120
- for (const e of events || []) {
121
- const parsed = parseEventRoute(e && e.source && e.source.route);
122
- if (!parsed) {
123
- notes.push(`skip:${e && e.name}:no-source-route`);
124
- eventsSkipped++;
125
- continue;
126
- }
127
- const candidates = routeToCandidates(parsed.urlPath, parsed.method);
128
- const found = candidates.find((rel) => fs.existsSync(path.join(ctx.repoRoot, rel)));
129
- if (!found) {
130
- notes.push(`skip:${e.name}:route-file-not-found`);
131
- eventsSkipped++;
132
- continue;
133
- }
134
- const key = `${found}::${parsed.method}`;
135
- if (!groups.has(key)) groups.set(key, { relPath: found, events: [] });
136
- groups.get(key).events.push(e);
137
- }
138
-
139
- for (const group of groups.values()) {
140
- const abs = path.join(ctx.repoRoot, group.relPath);
141
- const before = fs.readFileSync(abs, 'utf8');
142
- // Sprint D / D4 — pass repoRoot + relPath to the import resolver.
143
- const fileOpts = { repoRoot: (ctx && ctx.repoRoot) || null, relPath: group.relPath };
144
- let res;
145
- try {
146
- res = astInstrumentFile(before, group.events, fileOpts);
147
- } catch (err) {
148
- const msg = (err && err.message) || String(err);
149
- // eslint-disable-next-line no-console
150
- console.warn(`[auto-instrument] patch.fallback ${group.relPath}: ${msg}`);
151
- notes.push(`patch.fallback:${group.relPath}:${msg}`);
152
- continue;
153
- }
154
- if (!res.ok) {
155
- for (const e of group.events) {
156
- notes.push(`skip:${e.name}:${res.reason}`);
157
- eventsSkipped++;
158
- }
159
- continue;
160
- }
161
- for (const s of res.skipped || []) {
162
- notes.push(`skip:${s.event}:${s.reason}`);
163
- eventsSkipped++;
164
- }
165
- if (res.changed) {
166
- changes.push({
167
- relPath: group.relPath,
168
- before,
169
- after: res.after,
170
- reason: `auto-instrument-vue`,
171
- type: 'auto-instrument',
172
- });
173
- eventsInstrumented += (res.instrumented || []).length;
174
- }
175
- }
176
-
177
- const filesModified = changes.filter((c) => c.type === 'auto-instrument').length;
178
- return { changes, notes, filesModified, eventsInstrumented, eventsSkipped, collision: false };
179
- }
180
-
181
- function ensureSingletonHelper(ctx) {
182
- return singleton.ensureSingletonHelper(ctx, 'vue');
183
- }
184
-
185
- module.exports = {
186
- name: NAME,
187
- supportedFrameworks: SUPPORTED,
188
- ensureSingletonHelper,
189
- instrumentEvents,
190
- _internals: {
191
- parseEventRoute,
192
- routeToCandidates,
193
- findEventHandler,
194
- astInstrumentFile,
195
- },
196
- };
@@ -1,99 +0,0 @@
1
- // express.patch.cjs — Phase 16 A1
2
- //
3
- // Detects an Express server (`app.js`, `server.js`, `src/index.js` with the
4
- // idiomatic `const app = express()` signature) and appends:
5
- // - A proxy route that serves /gurulu-tracker from the Gurulu CDN
6
- // - A `res.locals.guruluConfig` middleware exposing siteId / tenantId
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- const NAME = 'express';
12
- const CANDIDATES = [
13
- 'app.js',
14
- 'server.js',
15
- 'src/index.js',
16
- 'src/app.js',
17
- 'src/server.js',
18
- 'index.js',
19
- ];
20
-
21
- const MARKER = '/* gurulu:install */';
22
-
23
- function looksLikeExpress(source) {
24
- return (
25
- /require\s*\(\s*['"]express['"]\s*\)/.test(source) ||
26
- /from\s+['"]express['"]/.test(source) ||
27
- /express\s*\(\s*\)/.test(source)
28
- );
29
- }
30
-
31
- function detect(repoRoot) {
32
- for (const rel of CANDIDATES) {
33
- const abs = path.join(repoRoot, rel);
34
- if (!fs.existsSync(abs)) continue;
35
- const source = fs.readFileSync(abs, 'utf8');
36
- if (looksLikeExpress(source)) {
37
- return { framework: NAME, files: [rel] };
38
- }
39
- }
40
- return null;
41
- }
42
-
43
- function findAppIdentifier(source) {
44
- const m = source.match(/(?:const|let|var)\s+(\w+)\s*=\s*express\s*\(\s*\)/);
45
- return m ? m[1] : 'app';
46
- }
47
-
48
- function buildMiddleware(appVar, injection) {
49
- const pkField = injection.publishableKey
50
- ? ` publishableKey: ${JSON.stringify(injection.publishableKey)},\n`
51
- : '';
52
- return (
53
- `\n${MARKER}\n` +
54
- `${appVar}.use((req, res, next) => {\n` +
55
- ` res.locals.guruluConfig = {\n` +
56
- ` siteId: ${JSON.stringify(injection.siteId)},\n` +
57
- ` tenantId: ${JSON.stringify(injection.tenantId)},\n` +
58
- pkField +
59
- ` };\n` +
60
- ` next();\n` +
61
- `});\n` +
62
- `${appVar}.use('/gurulu-tracker', (req, res) => {\n` +
63
- ` res.set('Content-Type', 'application/javascript');\n` +
64
- ` res.send(\`// Gurulu tracker proxy — site=\${${JSON.stringify(injection.siteId)}} data-gurulu-publishable-key=\${${JSON.stringify(injection.publishableKey || '')}}\\n` +
65
- `window.gurulu = window.gurulu || { queue: [] };\`);\n` +
66
- `});\n`
67
- );
68
- }
69
-
70
- function inject(source, injection) {
71
- if (source.includes(MARKER)) return source;
72
- const appVar = findAppIdentifier(source);
73
- const mw = buildMiddleware(appVar, injection);
74
- // Insert before `app.listen(` if present, otherwise append.
75
- const listenRe = new RegExp(`${appVar}\\s*\\.\\s*listen\\s*\\(`);
76
- const idx = source.search(listenRe);
77
- if (idx !== -1) {
78
- const lineStart = source.lastIndexOf('\n', idx) + 1;
79
- return source.slice(0, lineStart) + mw + '\n' + source.slice(lineStart);
80
- }
81
- return source + mw;
82
- }
83
-
84
- function plan(ctx) {
85
- const detection = detect(ctx.repoRoot);
86
- if (!detection) return { changes: [], notes: ['no-express'] };
87
- const changes = [];
88
- for (const rel of detection.files) {
89
- const abs = path.join(ctx.repoRoot, rel);
90
- const before = fs.readFileSync(abs, 'utf8');
91
- const after = inject(before, ctx.injection);
92
- if (after !== before) {
93
- changes.push({ relPath: rel, before, after, reason: 'inject-middleware' });
94
- }
95
- }
96
- return { changes, notes: [] };
97
- }
98
-
99
- module.exports = { name: NAME, detect, plan, looksLikeExpress };
@@ -1,108 +0,0 @@
1
- // fastify.patch.cjs — Phase 18.7 A1
2
- //
3
- // Fastify is a server-side framework. If a static HTML shell is shipped
4
- // under `public/`, `static/`, `dist/`, or `build/`, we patch that.
5
- // Otherwise we emit a server-only note and rely on --auto-instrument
6
- // (Phase 18.7 Agent B) to wire server-side track calls.
7
-
8
- const fs = require('fs');
9
- const path = require('path');
10
-
11
- const NAME = 'fastify';
12
- const HTML_CANDIDATES = [
13
- 'public/index.html',
14
- 'static/index.html',
15
- 'dist/index.html',
16
- 'build/index.html',
17
- ];
18
- const SERVER_CANDIDATES = [
19
- 'server.js',
20
- 'app.js',
21
- 'src/server.js',
22
- 'src/app.js',
23
- 'src/server.ts',
24
- 'src/app.ts',
25
- 'index.js',
26
- ];
27
-
28
- const MARKER = 'data-gurulu-install="1"';
29
-
30
- function looksLikeFastify(repoRoot) {
31
- const pkgPath = path.join(repoRoot, 'package.json');
32
- if (fs.existsSync(pkgPath)) {
33
- try {
34
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
35
- const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
36
- if (deps['fastify']) return true;
37
- } catch {
38
- /* ignore */
39
- }
40
- }
41
- for (const rel of SERVER_CANDIDATES) {
42
- const abs = path.join(repoRoot, rel);
43
- if (!fs.existsSync(abs)) continue;
44
- const source = fs.readFileSync(abs, 'utf8');
45
- if (/require\s*\(\s*['"]fastify['"]\s*\)/.test(source) || /from\s+['"]fastify['"]/.test(source)) {
46
- return true;
47
- }
48
- }
49
- return false;
50
- }
51
-
52
- function detect(repoRoot) {
53
- if (!looksLikeFastify(repoRoot)) return null;
54
- for (const rel of HTML_CANDIDATES) {
55
- if (fs.existsSync(path.join(repoRoot, rel))) {
56
- return { framework: NAME, files: [rel] };
57
- }
58
- }
59
- return { framework: NAME, files: [], serverOnly: true };
60
- }
61
-
62
- function buildScriptTag(injection) {
63
- const pkAttr = injection.publishableKey
64
- ? ` data-gurulu-publishable-key="${injection.publishableKey}"`
65
- : '';
66
- return (
67
- ` <script\n` +
68
- ` src="${injection.scriptSrc}"\n` +
69
- ` data-gurulu-site-id="${injection.siteId}"\n` +
70
- ` data-gurulu-tenant-id="${injection.tenantId}"${pkAttr}\n` +
71
- ` data-features="errors,replay,advanced"\n` +
72
- ` ${MARKER}\n` +
73
- ` async\n` +
74
- ` ></script>\n`
75
- );
76
- }
77
-
78
- function injectScriptTag(source, injection) {
79
- if (source.includes(MARKER)) return source;
80
- const tag = buildScriptTag(injection);
81
- if (/<\/body>/.test(source)) {
82
- return source.replace(/<\/body>/, `${tag} </body>`);
83
- }
84
- return source + '\n' + tag;
85
- }
86
-
87
- function plan(ctx) {
88
- const detection = detect(ctx.repoRoot);
89
- if (!detection) return { changes: [], notes: ['no-fastify'] };
90
- if (detection.serverOnly) {
91
- return {
92
- changes: [],
93
- notes: ['server-only; use --auto-instrument for server-side tracking'],
94
- };
95
- }
96
- const changes = [];
97
- for (const rel of detection.files) {
98
- const abs = path.join(ctx.repoRoot, rel);
99
- const before = fs.readFileSync(abs, 'utf8');
100
- const after = injectScriptTag(before, ctx.injection);
101
- if (after !== before) {
102
- changes.push({ relPath: rel, before, after, reason: 'inject-script-tag' });
103
- }
104
- }
105
- return { changes, notes: [] };
106
- }
107
-
108
- module.exports = { name: NAME, detect, plan };