@gurulu/cli 1.0.0 → 1.0.4
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/bin.js +363 -14
- package/dist/commands/init.d.ts +8 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +363 -14
- package/dist/lib/detect.d.ts +27 -0
- package/dist/lib/detect.d.ts.map +1 -0
- package/dist/lib/detect.js +106 -0
- package/dist/lib/exec-install.d.ts +21 -0
- package/dist/lib/exec-install.d.ts.map +1 -0
- package/dist/lib/install-plan.d.ts +25 -0
- package/dist/lib/install-plan.d.ts.map +1 -0
- package/dist/lib/install-plan.js +169 -0
- package/package.json +13 -2
package/dist/bin.js
CHANGED
|
@@ -25055,8 +25055,332 @@ var doctorCmd = defineCommand({
|
|
|
25055
25055
|
});
|
|
25056
25056
|
|
|
25057
25057
|
// src/commands/init.ts
|
|
25058
|
-
import { existsSync as
|
|
25059
|
-
import { dirname as
|
|
25058
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
25059
|
+
import { dirname as dirname3 } from "node:path";
|
|
25060
|
+
|
|
25061
|
+
// src/lib/detect.ts
|
|
25062
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
25063
|
+
import { dirname as dirname2, join as join4, parse } from "node:path";
|
|
25064
|
+
function readPackageJson(dir) {
|
|
25065
|
+
const p = join4(dir, "package.json");
|
|
25066
|
+
if (!existsSync4(p))
|
|
25067
|
+
return null;
|
|
25068
|
+
try {
|
|
25069
|
+
return JSON.parse(readFileSync4(p, "utf8"));
|
|
25070
|
+
} catch {
|
|
25071
|
+
return null;
|
|
25072
|
+
}
|
|
25073
|
+
}
|
|
25074
|
+
function hasDep(pkg, name) {
|
|
25075
|
+
if (!pkg)
|
|
25076
|
+
return false;
|
|
25077
|
+
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
25078
|
+
}
|
|
25079
|
+
function detectPackageManager(dir) {
|
|
25080
|
+
const root = parse(dir).root;
|
|
25081
|
+
let cur = dir;
|
|
25082
|
+
for (let i2 = 0;i2 < 6; i2++) {
|
|
25083
|
+
if (existsSync4(join4(cur, "bun.lock")) || existsSync4(join4(cur, "bun.lockb")))
|
|
25084
|
+
return "bun";
|
|
25085
|
+
if (existsSync4(join4(cur, "pnpm-lock.yaml")))
|
|
25086
|
+
return "pnpm";
|
|
25087
|
+
if (existsSync4(join4(cur, "yarn.lock")))
|
|
25088
|
+
return "yarn";
|
|
25089
|
+
if (existsSync4(join4(cur, "package-lock.json")))
|
|
25090
|
+
return "npm";
|
|
25091
|
+
if (cur === root)
|
|
25092
|
+
break;
|
|
25093
|
+
const parent = dirname2(cur);
|
|
25094
|
+
if (parent === cur)
|
|
25095
|
+
break;
|
|
25096
|
+
cur = parent;
|
|
25097
|
+
}
|
|
25098
|
+
return "npm";
|
|
25099
|
+
}
|
|
25100
|
+
function detectFramework(pkg, dir) {
|
|
25101
|
+
if (!pkg)
|
|
25102
|
+
return "unknown";
|
|
25103
|
+
if (hasDep(pkg, "next"))
|
|
25104
|
+
return "next";
|
|
25105
|
+
if (hasDep(pkg, "nuxt"))
|
|
25106
|
+
return "nuxt";
|
|
25107
|
+
if (hasDep(pkg, "@sveltejs/kit") || hasDep(pkg, "svelte"))
|
|
25108
|
+
return "svelte";
|
|
25109
|
+
if (hasDep(pkg, "astro"))
|
|
25110
|
+
return "astro";
|
|
25111
|
+
if (hasDep(pkg, "vite") && (hasDep(pkg, "vue") || hasDep(pkg, "react")))
|
|
25112
|
+
return "vite";
|
|
25113
|
+
if (hasDep(pkg, "vue"))
|
|
25114
|
+
return "vue";
|
|
25115
|
+
if (hasDep(pkg, "react"))
|
|
25116
|
+
return "react";
|
|
25117
|
+
if (hasDep(pkg, "hono"))
|
|
25118
|
+
return "hono";
|
|
25119
|
+
if (hasDep(pkg, "fastify"))
|
|
25120
|
+
return "fastify";
|
|
25121
|
+
if (hasDep(pkg, "express"))
|
|
25122
|
+
return "express";
|
|
25123
|
+
if (hasDep(pkg, "koa"))
|
|
25124
|
+
return "koa";
|
|
25125
|
+
if (existsSync4(join4(dir, "server.js")) || existsSync4(join4(dir, "server.ts")))
|
|
25126
|
+
return "node-server";
|
|
25127
|
+
return "unknown";
|
|
25128
|
+
}
|
|
25129
|
+
function frameworkRuntime(fw) {
|
|
25130
|
+
switch (fw) {
|
|
25131
|
+
case "next":
|
|
25132
|
+
case "react":
|
|
25133
|
+
case "vue":
|
|
25134
|
+
case "nuxt":
|
|
25135
|
+
case "svelte":
|
|
25136
|
+
case "astro":
|
|
25137
|
+
case "vite":
|
|
25138
|
+
return "browser";
|
|
25139
|
+
case "express":
|
|
25140
|
+
case "fastify":
|
|
25141
|
+
case "hono":
|
|
25142
|
+
case "koa":
|
|
25143
|
+
case "node-server":
|
|
25144
|
+
return "node";
|
|
25145
|
+
default:
|
|
25146
|
+
return "unknown";
|
|
25147
|
+
}
|
|
25148
|
+
}
|
|
25149
|
+
function detectProject(dir) {
|
|
25150
|
+
const pkg = readPackageJson(dir);
|
|
25151
|
+
const framework = detectFramework(pkg, dir);
|
|
25152
|
+
return {
|
|
25153
|
+
dir,
|
|
25154
|
+
hasPackageJson: pkg !== null,
|
|
25155
|
+
framework,
|
|
25156
|
+
runtime: frameworkRuntime(framework),
|
|
25157
|
+
packageManager: detectPackageManager(dir),
|
|
25158
|
+
packageJson: pkg
|
|
25159
|
+
};
|
|
25160
|
+
}
|
|
25161
|
+
|
|
25162
|
+
// src/lib/exec-install.ts
|
|
25163
|
+
import { spawn } from "node:child_process";
|
|
25164
|
+
async function execInstall(plan, opts) {
|
|
25165
|
+
const [bin, ...args] = plan.installCommand.split(/\s+/);
|
|
25166
|
+
if (!bin) {
|
|
25167
|
+
return {
|
|
25168
|
+
ok: false,
|
|
25169
|
+
exitCode: null,
|
|
25170
|
+
durationMs: 0,
|
|
25171
|
+
command: plan.installCommand,
|
|
25172
|
+
stderr: "empty install command"
|
|
25173
|
+
};
|
|
25174
|
+
}
|
|
25175
|
+
const started = Date.now();
|
|
25176
|
+
return new Promise((resolve) => {
|
|
25177
|
+
const child = spawn(bin, args, {
|
|
25178
|
+
cwd: opts.cwd,
|
|
25179
|
+
stdio: opts.silent ? ["ignore", "pipe", "pipe"] : ["inherit", "inherit", "pipe"],
|
|
25180
|
+
shell: process.platform === "win32"
|
|
25181
|
+
});
|
|
25182
|
+
let stderr = "";
|
|
25183
|
+
if (child.stderr) {
|
|
25184
|
+
child.stderr.on("data", (chunk) => {
|
|
25185
|
+
const text = chunk.toString("utf8");
|
|
25186
|
+
stderr += text;
|
|
25187
|
+
if (!opts.silent) {
|
|
25188
|
+
process.stderr.write(text);
|
|
25189
|
+
}
|
|
25190
|
+
});
|
|
25191
|
+
}
|
|
25192
|
+
const timeout = setTimeout(() => {
|
|
25193
|
+
child.kill("SIGTERM");
|
|
25194
|
+
}, opts.timeoutMs ?? 120000);
|
|
25195
|
+
child.on("error", (err) => {
|
|
25196
|
+
clearTimeout(timeout);
|
|
25197
|
+
resolve({
|
|
25198
|
+
ok: false,
|
|
25199
|
+
exitCode: null,
|
|
25200
|
+
durationMs: Date.now() - started,
|
|
25201
|
+
command: plan.installCommand,
|
|
25202
|
+
stderr: err.message
|
|
25203
|
+
});
|
|
25204
|
+
});
|
|
25205
|
+
child.on("close", (code) => {
|
|
25206
|
+
clearTimeout(timeout);
|
|
25207
|
+
resolve({
|
|
25208
|
+
ok: code === 0,
|
|
25209
|
+
exitCode: code,
|
|
25210
|
+
durationMs: Date.now() - started,
|
|
25211
|
+
command: plan.installCommand,
|
|
25212
|
+
...stderr ? { stderr } : {}
|
|
25213
|
+
});
|
|
25214
|
+
});
|
|
25215
|
+
});
|
|
25216
|
+
}
|
|
25217
|
+
|
|
25218
|
+
// src/lib/install-plan.ts
|
|
25219
|
+
function installCmdFor(pm, pkg) {
|
|
25220
|
+
switch (pm) {
|
|
25221
|
+
case "bun":
|
|
25222
|
+
return `bun add ${pkg}`;
|
|
25223
|
+
case "pnpm":
|
|
25224
|
+
return `pnpm add ${pkg}`;
|
|
25225
|
+
case "yarn":
|
|
25226
|
+
return `yarn add ${pkg}`;
|
|
25227
|
+
default:
|
|
25228
|
+
return `npm install ${pkg}`;
|
|
25229
|
+
}
|
|
25230
|
+
}
|
|
25231
|
+
function sdkFor(framework) {
|
|
25232
|
+
switch (framework) {
|
|
25233
|
+
case "express":
|
|
25234
|
+
case "fastify":
|
|
25235
|
+
case "hono":
|
|
25236
|
+
case "koa":
|
|
25237
|
+
case "node-server":
|
|
25238
|
+
return "@gurulu/node";
|
|
25239
|
+
default:
|
|
25240
|
+
return "@gurulu/web";
|
|
25241
|
+
}
|
|
25242
|
+
}
|
|
25243
|
+
function snippetWeb(workspaceKey) {
|
|
25244
|
+
return `import gurulu from '@gurulu/web';
|
|
25245
|
+
|
|
25246
|
+
gurulu.init({
|
|
25247
|
+
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
25248
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
|
|
25249
|
+
});`;
|
|
25250
|
+
}
|
|
25251
|
+
function snippetNext(workspaceKey) {
|
|
25252
|
+
return `// src/app/gurulu-provider.tsx
|
|
25253
|
+
'use client';
|
|
25254
|
+
import { useEffect } from 'react';
|
|
25255
|
+
import gurulu from '@gurulu/web';
|
|
25256
|
+
|
|
25257
|
+
export function GuruluProvider({ children }: { children: React.ReactNode }) {
|
|
25258
|
+
useEffect(() => {
|
|
25259
|
+
gurulu.init({
|
|
25260
|
+
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
25261
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT,
|
|
25262
|
+
});
|
|
25263
|
+
}, []);
|
|
25264
|
+
return <>{children}</>;
|
|
25265
|
+
}
|
|
25266
|
+
|
|
25267
|
+
// Then wrap your root in app/layout.tsx:
|
|
25268
|
+
// <GuruluProvider>{children}</GuruluProvider>`;
|
|
25269
|
+
}
|
|
25270
|
+
function snippetNode(workspaceKey) {
|
|
25271
|
+
return `import { createGurulu } from '@gurulu/node';
|
|
25272
|
+
|
|
25273
|
+
const gurulu = createGurulu({
|
|
25274
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
25275
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
25276
|
+
endpoint: process.env.GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
|
|
25277
|
+
});
|
|
25278
|
+
|
|
25279
|
+
// In your handler:
|
|
25280
|
+
await gurulu.track('purchase_completed', {
|
|
25281
|
+
user_id: 'u_42',
|
|
25282
|
+
order_id: 'o_123',
|
|
25283
|
+
total: 49.99,
|
|
25284
|
+
});`;
|
|
25285
|
+
}
|
|
25286
|
+
function snippetExpress(workspaceKey) {
|
|
25287
|
+
return `import express from 'express';
|
|
25288
|
+
import { guruluMiddleware } from '@gurulu/node/middleware/express';
|
|
25289
|
+
|
|
25290
|
+
const app = express();
|
|
25291
|
+
app.use(guruluMiddleware({
|
|
25292
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
25293
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
25294
|
+
endpoint: process.env.GURULU_ENDPOINT,
|
|
25295
|
+
}));`;
|
|
25296
|
+
}
|
|
25297
|
+
function snippetHono(workspaceKey) {
|
|
25298
|
+
return `import { Hono } from 'hono';
|
|
25299
|
+
import { createGurulu } from '@gurulu/node';
|
|
25300
|
+
|
|
25301
|
+
const gurulu = createGurulu({
|
|
25302
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
25303
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
25304
|
+
endpoint: process.env.GURULU_ENDPOINT,
|
|
25305
|
+
});
|
|
25306
|
+
|
|
25307
|
+
const app = new Hono();
|
|
25308
|
+
app.post('/checkout/complete', async (c) => {
|
|
25309
|
+
await gurulu.track('purchase_completed', await c.req.json());
|
|
25310
|
+
return c.json({ ok: true });
|
|
25311
|
+
});`;
|
|
25312
|
+
}
|
|
25313
|
+
function placementHintFor(framework) {
|
|
25314
|
+
switch (framework) {
|
|
25315
|
+
case "next":
|
|
25316
|
+
return "Add the GuruluProvider to app/layout.tsx (App Router) or pages/_app.tsx (Pages Router) and wrap children.";
|
|
25317
|
+
case "nuxt":
|
|
25318
|
+
return "Add to plugins/gurulu.client.ts and register in nuxt.config.ts.";
|
|
25319
|
+
case "svelte":
|
|
25320
|
+
return "Add to src/routes/+layout.svelte inside onMount().";
|
|
25321
|
+
case "astro":
|
|
25322
|
+
return "Add to src/layouts/Base.astro inside a <script> tag (or use a hydrated component).";
|
|
25323
|
+
case "vite":
|
|
25324
|
+
case "react":
|
|
25325
|
+
case "vue":
|
|
25326
|
+
return "Add to src/main.ts (or your entry file) before mounting the app.";
|
|
25327
|
+
case "express":
|
|
25328
|
+
return "Register guruluMiddleware before your route handlers in your app entry file.";
|
|
25329
|
+
case "fastify":
|
|
25330
|
+
return "Register the Gurulu plugin via fastify.register() before route definitions.";
|
|
25331
|
+
case "hono":
|
|
25332
|
+
return "Initialize the SDK at the top of your app file; call track() inside route handlers.";
|
|
25333
|
+
case "koa":
|
|
25334
|
+
return "Add as Koa middleware (app.use) before your routes.";
|
|
25335
|
+
case "node-server":
|
|
25336
|
+
return "Initialize the SDK at startup; call track() from your handlers.";
|
|
25337
|
+
default:
|
|
25338
|
+
return "Initialize the SDK at your app entry point.";
|
|
25339
|
+
}
|
|
25340
|
+
}
|
|
25341
|
+
function initSnippetFor(framework, sdk, workspaceKey) {
|
|
25342
|
+
if (sdk === "@gurulu/node") {
|
|
25343
|
+
if (framework === "express")
|
|
25344
|
+
return snippetExpress(workspaceKey);
|
|
25345
|
+
if (framework === "hono")
|
|
25346
|
+
return snippetHono(workspaceKey);
|
|
25347
|
+
return snippetNode(workspaceKey);
|
|
25348
|
+
}
|
|
25349
|
+
if (framework === "next")
|
|
25350
|
+
return snippetNext(workspaceKey);
|
|
25351
|
+
return snippetWeb(workspaceKey);
|
|
25352
|
+
}
|
|
25353
|
+
function envKeysFor(sdk, framework) {
|
|
25354
|
+
if (sdk === "@gurulu/node") {
|
|
25355
|
+
return [
|
|
25356
|
+
{ key: "GURULU_WORKSPACE", example: "pk_live_xxxxxxxxxxxx", required: true },
|
|
25357
|
+
{ key: "GURULU_API_KEY", example: "sk_live_xxxxxxxxxxxx", required: true },
|
|
25358
|
+
{
|
|
25359
|
+
key: "GURULU_ENDPOINT",
|
|
25360
|
+
example: "https://ingest.gurulu.io",
|
|
25361
|
+
required: false
|
|
25362
|
+
}
|
|
25363
|
+
];
|
|
25364
|
+
}
|
|
25365
|
+
const prefix = framework === "next" ? "NEXT_PUBLIC_GURULU" : "VITE_GURULU";
|
|
25366
|
+
return [
|
|
25367
|
+
{ key: `${prefix}_WORKSPACE`, example: "pk_live_xxxxxxxxxxxx", required: false },
|
|
25368
|
+
{ key: `${prefix}_ENDPOINT`, example: "https://ingest.gurulu.io", required: false }
|
|
25369
|
+
];
|
|
25370
|
+
}
|
|
25371
|
+
function buildInstallPlan(detected, ctx = {}) {
|
|
25372
|
+
const sdk = sdkFor(detected.framework);
|
|
25373
|
+
const workspaceKey = ctx.writeKey ?? "pk_xxxxxxxxxxxx";
|
|
25374
|
+
return {
|
|
25375
|
+
sdk,
|
|
25376
|
+
packageManager: detected.packageManager,
|
|
25377
|
+
installCommand: installCmdFor(detected.packageManager, sdk),
|
|
25378
|
+
initSnippet: initSnippetFor(detected.framework, sdk, workspaceKey),
|
|
25379
|
+
envKeys: envKeysFor(sdk, detected.framework),
|
|
25380
|
+
placementHint: placementHintFor(detected.framework),
|
|
25381
|
+
framework: detected.framework
|
|
25382
|
+
};
|
|
25383
|
+
}
|
|
25060
25384
|
|
|
25061
25385
|
// src/commands/pull.ts
|
|
25062
25386
|
import { writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -25206,7 +25530,9 @@ var initCmd = defineCommand({
|
|
|
25206
25530
|
args: {
|
|
25207
25531
|
workspace: { type: "string", description: "Workspace ID (uuid)" },
|
|
25208
25532
|
endpoint: { type: "string", description: "API endpoint", default: DEFAULT_ENDPOINT },
|
|
25209
|
-
sdk: { type: "string", description: "SDK preference (web|node|
|
|
25533
|
+
sdk: { type: "string", description: "SDK preference (web|node|auto)", default: "auto" },
|
|
25534
|
+
"write-key": { type: "string", description: "Workspace write key (pk_xxx) for init snippet" },
|
|
25535
|
+
"no-install": { type: "boolean", description: "Skip SDK install (config files only)" },
|
|
25210
25536
|
"no-pull": { type: "boolean", description: "Skip first registry pull" },
|
|
25211
25537
|
force: { type: "boolean", description: "Overwrite existing config" }
|
|
25212
25538
|
},
|
|
@@ -25222,40 +25548,63 @@ var initCmd = defineCommand({
|
|
|
25222
25548
|
console.error("[gurulu] --workspace <uuid> required (workspace ID from dashboard)");
|
|
25223
25549
|
process.exit(1);
|
|
25224
25550
|
}
|
|
25225
|
-
const
|
|
25226
|
-
if (!["web", "node", "
|
|
25227
|
-
console.error(`[gurulu] invalid --sdk: ${
|
|
25551
|
+
const sdkArg = String(args.sdk ?? "auto");
|
|
25552
|
+
if (!["web", "node", "auto"].includes(sdkArg)) {
|
|
25553
|
+
console.error(`[gurulu] invalid --sdk: ${sdkArg} (use web|node|auto)`);
|
|
25228
25554
|
process.exit(1);
|
|
25229
25555
|
}
|
|
25556
|
+
const detected = detectProject(cwd);
|
|
25557
|
+
const writeKey = String(args["write-key"] ?? "").trim() || "pk_xxxxxxxxxxxx";
|
|
25558
|
+
const plan = buildInstallPlan(detected, { writeKey, workspaceId });
|
|
25559
|
+
const effectiveSdkPref = sdkArg === "auto" ? plan.sdk === "@gurulu/node" ? "node" : "web" : sdkArg;
|
|
25230
25560
|
const config = {
|
|
25231
25561
|
workspace_id: workspaceId,
|
|
25232
25562
|
endpoint: String(args.endpoint),
|
|
25233
|
-
sdk_preference:
|
|
25563
|
+
sdk_preference: effectiveSdkPref,
|
|
25234
25564
|
registry_path: ".gurulu/event-registry.json",
|
|
25235
25565
|
generated_path: ".gurulu/generated.d.ts",
|
|
25236
25566
|
manifest_lock_path: ".gurulu/manifest.lock",
|
|
25237
25567
|
auto_pull_on_init: true
|
|
25238
25568
|
};
|
|
25239
25569
|
writeProjectConfig(config, cwd);
|
|
25240
|
-
const dir =
|
|
25241
|
-
if (!
|
|
25570
|
+
const dir = dirname3(projectConfigPath(cwd));
|
|
25571
|
+
if (!existsSync5(dir))
|
|
25242
25572
|
mkdirSync2(dir, { recursive: true });
|
|
25243
|
-
if (!
|
|
25573
|
+
if (!existsSync5(projectRegistryPath(cwd))) {
|
|
25244
25574
|
writeFileSync3(projectRegistryPath(cwd), `{}
|
|
25245
25575
|
`, "utf-8");
|
|
25246
25576
|
}
|
|
25247
|
-
if (!
|
|
25577
|
+
if (!existsSync5(projectGeneratedPath(cwd))) {
|
|
25248
25578
|
writeFileSync3(projectGeneratedPath(cwd), "// Run `gurulu pull` to populate typed events.\n", "utf-8");
|
|
25249
25579
|
}
|
|
25250
|
-
if (!
|
|
25580
|
+
if (!existsSync5(projectManifestLockPath(cwd))) {
|
|
25251
25581
|
writeFileSync3(projectManifestLockPath(cwd), `0.0.0
|
|
25252
25582
|
`, "utf-8");
|
|
25253
25583
|
}
|
|
25254
25584
|
console.log(`[gurulu] initialized ${projectConfigPath(cwd)}`);
|
|
25585
|
+
if (args["no-install"]) {
|
|
25586
|
+
console.log(`[gurulu] skipping SDK install (--no-install).`);
|
|
25587
|
+
console.log(` manual: ${plan.installCommand}`);
|
|
25588
|
+
} else if (!detected.hasPackageJson) {
|
|
25589
|
+
console.log(`[gurulu] no package.json found in ${cwd} — skipping SDK install.`);
|
|
25590
|
+
console.log(` use the script tag: <script src="https://cdn.gurulu.io/t.js" data-workspace="${writeKey}"></script>`);
|
|
25591
|
+
} else {
|
|
25592
|
+
console.log(`[gurulu] detected ${detected.framework} (${detected.packageManager}) — installing ${plan.sdk}…`);
|
|
25593
|
+
const result = await execInstall(plan, { cwd });
|
|
25594
|
+
if (result.ok) {
|
|
25595
|
+
console.log(`[gurulu] ${plan.sdk} installed (${(result.durationMs / 1000).toFixed(1)}s).`);
|
|
25596
|
+
} else {
|
|
25597
|
+
console.warn(`[gurulu] install failed (exit ${result.exitCode ?? "n/a"}) — run manually: ${plan.installCommand}`);
|
|
25598
|
+
}
|
|
25599
|
+
}
|
|
25600
|
+
console.log("");
|
|
25255
25601
|
console.log("[gurulu] next steps:");
|
|
25256
25602
|
console.log(" 1. gurulu login — authenticate (or store API key)");
|
|
25257
25603
|
console.log(" 2. gurulu pull — fetch registry + code-gen");
|
|
25258
|
-
console.log(
|
|
25604
|
+
console.log(` 3. ${plan.placementHint}`);
|
|
25605
|
+
console.log("");
|
|
25606
|
+
console.log("[gurulu] init snippet:");
|
|
25607
|
+
console.log(plan.initSnippet);
|
|
25259
25608
|
if (!args["no-pull"]) {
|
|
25260
25609
|
try {
|
|
25261
25610
|
await runPull({ cwd });
|
|
@@ -25365,7 +25714,7 @@ var pushCmd = defineCommand({
|
|
|
25365
25714
|
});
|
|
25366
25715
|
|
|
25367
25716
|
// src/index.ts
|
|
25368
|
-
var VERSION = "1.0.
|
|
25717
|
+
var VERSION = "1.0.4";
|
|
25369
25718
|
var mcpCmd = defineCommand({
|
|
25370
25719
|
meta: {
|
|
25371
25720
|
name: "mcp",
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -13,6 +13,14 @@ export declare const initCmd: import("citty").CommandDef<{
|
|
|
13
13
|
description: string;
|
|
14
14
|
default: string;
|
|
15
15
|
};
|
|
16
|
+
'write-key': {
|
|
17
|
+
type: "string";
|
|
18
|
+
description: string;
|
|
19
|
+
};
|
|
20
|
+
'no-install': {
|
|
21
|
+
type: "boolean";
|
|
22
|
+
description: string;
|
|
23
|
+
};
|
|
16
24
|
'no-pull': {
|
|
17
25
|
type: "boolean";
|
|
18
26
|
description: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgJlB,CAAC"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -24632,8 +24632,332 @@ var doctorCmd = defineCommand({
|
|
|
24632
24632
|
});
|
|
24633
24633
|
|
|
24634
24634
|
// src/commands/init.ts
|
|
24635
|
-
import { existsSync as
|
|
24636
|
-
import { dirname as
|
|
24635
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
24636
|
+
import { dirname as dirname3 } from "node:path";
|
|
24637
|
+
|
|
24638
|
+
// src/lib/detect.ts
|
|
24639
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
24640
|
+
import { dirname as dirname2, join as join4, parse } from "node:path";
|
|
24641
|
+
function readPackageJson(dir) {
|
|
24642
|
+
const p = join4(dir, "package.json");
|
|
24643
|
+
if (!existsSync4(p))
|
|
24644
|
+
return null;
|
|
24645
|
+
try {
|
|
24646
|
+
return JSON.parse(readFileSync4(p, "utf8"));
|
|
24647
|
+
} catch {
|
|
24648
|
+
return null;
|
|
24649
|
+
}
|
|
24650
|
+
}
|
|
24651
|
+
function hasDep(pkg, name) {
|
|
24652
|
+
if (!pkg)
|
|
24653
|
+
return false;
|
|
24654
|
+
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
24655
|
+
}
|
|
24656
|
+
function detectPackageManager(dir) {
|
|
24657
|
+
const root = parse(dir).root;
|
|
24658
|
+
let cur = dir;
|
|
24659
|
+
for (let i2 = 0;i2 < 6; i2++) {
|
|
24660
|
+
if (existsSync4(join4(cur, "bun.lock")) || existsSync4(join4(cur, "bun.lockb")))
|
|
24661
|
+
return "bun";
|
|
24662
|
+
if (existsSync4(join4(cur, "pnpm-lock.yaml")))
|
|
24663
|
+
return "pnpm";
|
|
24664
|
+
if (existsSync4(join4(cur, "yarn.lock")))
|
|
24665
|
+
return "yarn";
|
|
24666
|
+
if (existsSync4(join4(cur, "package-lock.json")))
|
|
24667
|
+
return "npm";
|
|
24668
|
+
if (cur === root)
|
|
24669
|
+
break;
|
|
24670
|
+
const parent = dirname2(cur);
|
|
24671
|
+
if (parent === cur)
|
|
24672
|
+
break;
|
|
24673
|
+
cur = parent;
|
|
24674
|
+
}
|
|
24675
|
+
return "npm";
|
|
24676
|
+
}
|
|
24677
|
+
function detectFramework(pkg, dir) {
|
|
24678
|
+
if (!pkg)
|
|
24679
|
+
return "unknown";
|
|
24680
|
+
if (hasDep(pkg, "next"))
|
|
24681
|
+
return "next";
|
|
24682
|
+
if (hasDep(pkg, "nuxt"))
|
|
24683
|
+
return "nuxt";
|
|
24684
|
+
if (hasDep(pkg, "@sveltejs/kit") || hasDep(pkg, "svelte"))
|
|
24685
|
+
return "svelte";
|
|
24686
|
+
if (hasDep(pkg, "astro"))
|
|
24687
|
+
return "astro";
|
|
24688
|
+
if (hasDep(pkg, "vite") && (hasDep(pkg, "vue") || hasDep(pkg, "react")))
|
|
24689
|
+
return "vite";
|
|
24690
|
+
if (hasDep(pkg, "vue"))
|
|
24691
|
+
return "vue";
|
|
24692
|
+
if (hasDep(pkg, "react"))
|
|
24693
|
+
return "react";
|
|
24694
|
+
if (hasDep(pkg, "hono"))
|
|
24695
|
+
return "hono";
|
|
24696
|
+
if (hasDep(pkg, "fastify"))
|
|
24697
|
+
return "fastify";
|
|
24698
|
+
if (hasDep(pkg, "express"))
|
|
24699
|
+
return "express";
|
|
24700
|
+
if (hasDep(pkg, "koa"))
|
|
24701
|
+
return "koa";
|
|
24702
|
+
if (existsSync4(join4(dir, "server.js")) || existsSync4(join4(dir, "server.ts")))
|
|
24703
|
+
return "node-server";
|
|
24704
|
+
return "unknown";
|
|
24705
|
+
}
|
|
24706
|
+
function frameworkRuntime(fw) {
|
|
24707
|
+
switch (fw) {
|
|
24708
|
+
case "next":
|
|
24709
|
+
case "react":
|
|
24710
|
+
case "vue":
|
|
24711
|
+
case "nuxt":
|
|
24712
|
+
case "svelte":
|
|
24713
|
+
case "astro":
|
|
24714
|
+
case "vite":
|
|
24715
|
+
return "browser";
|
|
24716
|
+
case "express":
|
|
24717
|
+
case "fastify":
|
|
24718
|
+
case "hono":
|
|
24719
|
+
case "koa":
|
|
24720
|
+
case "node-server":
|
|
24721
|
+
return "node";
|
|
24722
|
+
default:
|
|
24723
|
+
return "unknown";
|
|
24724
|
+
}
|
|
24725
|
+
}
|
|
24726
|
+
function detectProject(dir) {
|
|
24727
|
+
const pkg = readPackageJson(dir);
|
|
24728
|
+
const framework = detectFramework(pkg, dir);
|
|
24729
|
+
return {
|
|
24730
|
+
dir,
|
|
24731
|
+
hasPackageJson: pkg !== null,
|
|
24732
|
+
framework,
|
|
24733
|
+
runtime: frameworkRuntime(framework),
|
|
24734
|
+
packageManager: detectPackageManager(dir),
|
|
24735
|
+
packageJson: pkg
|
|
24736
|
+
};
|
|
24737
|
+
}
|
|
24738
|
+
|
|
24739
|
+
// src/lib/exec-install.ts
|
|
24740
|
+
import { spawn } from "node:child_process";
|
|
24741
|
+
async function execInstall(plan, opts) {
|
|
24742
|
+
const [bin, ...args] = plan.installCommand.split(/\s+/);
|
|
24743
|
+
if (!bin) {
|
|
24744
|
+
return {
|
|
24745
|
+
ok: false,
|
|
24746
|
+
exitCode: null,
|
|
24747
|
+
durationMs: 0,
|
|
24748
|
+
command: plan.installCommand,
|
|
24749
|
+
stderr: "empty install command"
|
|
24750
|
+
};
|
|
24751
|
+
}
|
|
24752
|
+
const started = Date.now();
|
|
24753
|
+
return new Promise((resolve) => {
|
|
24754
|
+
const child = spawn(bin, args, {
|
|
24755
|
+
cwd: opts.cwd,
|
|
24756
|
+
stdio: opts.silent ? ["ignore", "pipe", "pipe"] : ["inherit", "inherit", "pipe"],
|
|
24757
|
+
shell: process.platform === "win32"
|
|
24758
|
+
});
|
|
24759
|
+
let stderr = "";
|
|
24760
|
+
if (child.stderr) {
|
|
24761
|
+
child.stderr.on("data", (chunk) => {
|
|
24762
|
+
const text = chunk.toString("utf8");
|
|
24763
|
+
stderr += text;
|
|
24764
|
+
if (!opts.silent) {
|
|
24765
|
+
process.stderr.write(text);
|
|
24766
|
+
}
|
|
24767
|
+
});
|
|
24768
|
+
}
|
|
24769
|
+
const timeout = setTimeout(() => {
|
|
24770
|
+
child.kill("SIGTERM");
|
|
24771
|
+
}, opts.timeoutMs ?? 120000);
|
|
24772
|
+
child.on("error", (err) => {
|
|
24773
|
+
clearTimeout(timeout);
|
|
24774
|
+
resolve({
|
|
24775
|
+
ok: false,
|
|
24776
|
+
exitCode: null,
|
|
24777
|
+
durationMs: Date.now() - started,
|
|
24778
|
+
command: plan.installCommand,
|
|
24779
|
+
stderr: err.message
|
|
24780
|
+
});
|
|
24781
|
+
});
|
|
24782
|
+
child.on("close", (code) => {
|
|
24783
|
+
clearTimeout(timeout);
|
|
24784
|
+
resolve({
|
|
24785
|
+
ok: code === 0,
|
|
24786
|
+
exitCode: code,
|
|
24787
|
+
durationMs: Date.now() - started,
|
|
24788
|
+
command: plan.installCommand,
|
|
24789
|
+
...stderr ? { stderr } : {}
|
|
24790
|
+
});
|
|
24791
|
+
});
|
|
24792
|
+
});
|
|
24793
|
+
}
|
|
24794
|
+
|
|
24795
|
+
// src/lib/install-plan.ts
|
|
24796
|
+
function installCmdFor(pm, pkg) {
|
|
24797
|
+
switch (pm) {
|
|
24798
|
+
case "bun":
|
|
24799
|
+
return `bun add ${pkg}`;
|
|
24800
|
+
case "pnpm":
|
|
24801
|
+
return `pnpm add ${pkg}`;
|
|
24802
|
+
case "yarn":
|
|
24803
|
+
return `yarn add ${pkg}`;
|
|
24804
|
+
default:
|
|
24805
|
+
return `npm install ${pkg}`;
|
|
24806
|
+
}
|
|
24807
|
+
}
|
|
24808
|
+
function sdkFor(framework) {
|
|
24809
|
+
switch (framework) {
|
|
24810
|
+
case "express":
|
|
24811
|
+
case "fastify":
|
|
24812
|
+
case "hono":
|
|
24813
|
+
case "koa":
|
|
24814
|
+
case "node-server":
|
|
24815
|
+
return "@gurulu/node";
|
|
24816
|
+
default:
|
|
24817
|
+
return "@gurulu/web";
|
|
24818
|
+
}
|
|
24819
|
+
}
|
|
24820
|
+
function snippetWeb(workspaceKey) {
|
|
24821
|
+
return `import gurulu from '@gurulu/web';
|
|
24822
|
+
|
|
24823
|
+
gurulu.init({
|
|
24824
|
+
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
24825
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
|
|
24826
|
+
});`;
|
|
24827
|
+
}
|
|
24828
|
+
function snippetNext(workspaceKey) {
|
|
24829
|
+
return `// src/app/gurulu-provider.tsx
|
|
24830
|
+
'use client';
|
|
24831
|
+
import { useEffect } from 'react';
|
|
24832
|
+
import gurulu from '@gurulu/web';
|
|
24833
|
+
|
|
24834
|
+
export function GuruluProvider({ children }: { children: React.ReactNode }) {
|
|
24835
|
+
useEffect(() => {
|
|
24836
|
+
gurulu.init({
|
|
24837
|
+
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
24838
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT,
|
|
24839
|
+
});
|
|
24840
|
+
}, []);
|
|
24841
|
+
return <>{children}</>;
|
|
24842
|
+
}
|
|
24843
|
+
|
|
24844
|
+
// Then wrap your root in app/layout.tsx:
|
|
24845
|
+
// <GuruluProvider>{children}</GuruluProvider>`;
|
|
24846
|
+
}
|
|
24847
|
+
function snippetNode(workspaceKey) {
|
|
24848
|
+
return `import { createGurulu } from '@gurulu/node';
|
|
24849
|
+
|
|
24850
|
+
const gurulu = createGurulu({
|
|
24851
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
24852
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
24853
|
+
endpoint: process.env.GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
|
|
24854
|
+
});
|
|
24855
|
+
|
|
24856
|
+
// In your handler:
|
|
24857
|
+
await gurulu.track('purchase_completed', {
|
|
24858
|
+
user_id: 'u_42',
|
|
24859
|
+
order_id: 'o_123',
|
|
24860
|
+
total: 49.99,
|
|
24861
|
+
});`;
|
|
24862
|
+
}
|
|
24863
|
+
function snippetExpress(workspaceKey) {
|
|
24864
|
+
return `import express from 'express';
|
|
24865
|
+
import { guruluMiddleware } from '@gurulu/node/middleware/express';
|
|
24866
|
+
|
|
24867
|
+
const app = express();
|
|
24868
|
+
app.use(guruluMiddleware({
|
|
24869
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
24870
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
24871
|
+
endpoint: process.env.GURULU_ENDPOINT,
|
|
24872
|
+
}));`;
|
|
24873
|
+
}
|
|
24874
|
+
function snippetHono(workspaceKey) {
|
|
24875
|
+
return `import { Hono } from 'hono';
|
|
24876
|
+
import { createGurulu } from '@gurulu/node';
|
|
24877
|
+
|
|
24878
|
+
const gurulu = createGurulu({
|
|
24879
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
24880
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
24881
|
+
endpoint: process.env.GURULU_ENDPOINT,
|
|
24882
|
+
});
|
|
24883
|
+
|
|
24884
|
+
const app = new Hono();
|
|
24885
|
+
app.post('/checkout/complete', async (c) => {
|
|
24886
|
+
await gurulu.track('purchase_completed', await c.req.json());
|
|
24887
|
+
return c.json({ ok: true });
|
|
24888
|
+
});`;
|
|
24889
|
+
}
|
|
24890
|
+
function placementHintFor(framework) {
|
|
24891
|
+
switch (framework) {
|
|
24892
|
+
case "next":
|
|
24893
|
+
return "Add the GuruluProvider to app/layout.tsx (App Router) or pages/_app.tsx (Pages Router) and wrap children.";
|
|
24894
|
+
case "nuxt":
|
|
24895
|
+
return "Add to plugins/gurulu.client.ts and register in nuxt.config.ts.";
|
|
24896
|
+
case "svelte":
|
|
24897
|
+
return "Add to src/routes/+layout.svelte inside onMount().";
|
|
24898
|
+
case "astro":
|
|
24899
|
+
return "Add to src/layouts/Base.astro inside a <script> tag (or use a hydrated component).";
|
|
24900
|
+
case "vite":
|
|
24901
|
+
case "react":
|
|
24902
|
+
case "vue":
|
|
24903
|
+
return "Add to src/main.ts (or your entry file) before mounting the app.";
|
|
24904
|
+
case "express":
|
|
24905
|
+
return "Register guruluMiddleware before your route handlers in your app entry file.";
|
|
24906
|
+
case "fastify":
|
|
24907
|
+
return "Register the Gurulu plugin via fastify.register() before route definitions.";
|
|
24908
|
+
case "hono":
|
|
24909
|
+
return "Initialize the SDK at the top of your app file; call track() inside route handlers.";
|
|
24910
|
+
case "koa":
|
|
24911
|
+
return "Add as Koa middleware (app.use) before your routes.";
|
|
24912
|
+
case "node-server":
|
|
24913
|
+
return "Initialize the SDK at startup; call track() from your handlers.";
|
|
24914
|
+
default:
|
|
24915
|
+
return "Initialize the SDK at your app entry point.";
|
|
24916
|
+
}
|
|
24917
|
+
}
|
|
24918
|
+
function initSnippetFor(framework, sdk, workspaceKey) {
|
|
24919
|
+
if (sdk === "@gurulu/node") {
|
|
24920
|
+
if (framework === "express")
|
|
24921
|
+
return snippetExpress(workspaceKey);
|
|
24922
|
+
if (framework === "hono")
|
|
24923
|
+
return snippetHono(workspaceKey);
|
|
24924
|
+
return snippetNode(workspaceKey);
|
|
24925
|
+
}
|
|
24926
|
+
if (framework === "next")
|
|
24927
|
+
return snippetNext(workspaceKey);
|
|
24928
|
+
return snippetWeb(workspaceKey);
|
|
24929
|
+
}
|
|
24930
|
+
function envKeysFor(sdk, framework) {
|
|
24931
|
+
if (sdk === "@gurulu/node") {
|
|
24932
|
+
return [
|
|
24933
|
+
{ key: "GURULU_WORKSPACE", example: "pk_live_xxxxxxxxxxxx", required: true },
|
|
24934
|
+
{ key: "GURULU_API_KEY", example: "sk_live_xxxxxxxxxxxx", required: true },
|
|
24935
|
+
{
|
|
24936
|
+
key: "GURULU_ENDPOINT",
|
|
24937
|
+
example: "https://ingest.gurulu.io",
|
|
24938
|
+
required: false
|
|
24939
|
+
}
|
|
24940
|
+
];
|
|
24941
|
+
}
|
|
24942
|
+
const prefix = framework === "next" ? "NEXT_PUBLIC_GURULU" : "VITE_GURULU";
|
|
24943
|
+
return [
|
|
24944
|
+
{ key: `${prefix}_WORKSPACE`, example: "pk_live_xxxxxxxxxxxx", required: false },
|
|
24945
|
+
{ key: `${prefix}_ENDPOINT`, example: "https://ingest.gurulu.io", required: false }
|
|
24946
|
+
];
|
|
24947
|
+
}
|
|
24948
|
+
function buildInstallPlan(detected, ctx = {}) {
|
|
24949
|
+
const sdk = sdkFor(detected.framework);
|
|
24950
|
+
const workspaceKey = ctx.writeKey ?? "pk_xxxxxxxxxxxx";
|
|
24951
|
+
return {
|
|
24952
|
+
sdk,
|
|
24953
|
+
packageManager: detected.packageManager,
|
|
24954
|
+
installCommand: installCmdFor(detected.packageManager, sdk),
|
|
24955
|
+
initSnippet: initSnippetFor(detected.framework, sdk, workspaceKey),
|
|
24956
|
+
envKeys: envKeysFor(sdk, detected.framework),
|
|
24957
|
+
placementHint: placementHintFor(detected.framework),
|
|
24958
|
+
framework: detected.framework
|
|
24959
|
+
};
|
|
24960
|
+
}
|
|
24637
24961
|
|
|
24638
24962
|
// src/commands/pull.ts
|
|
24639
24963
|
import { writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -24783,7 +25107,9 @@ var initCmd = defineCommand({
|
|
|
24783
25107
|
args: {
|
|
24784
25108
|
workspace: { type: "string", description: "Workspace ID (uuid)" },
|
|
24785
25109
|
endpoint: { type: "string", description: "API endpoint", default: DEFAULT_ENDPOINT },
|
|
24786
|
-
sdk: { type: "string", description: "SDK preference (web|node|
|
|
25110
|
+
sdk: { type: "string", description: "SDK preference (web|node|auto)", default: "auto" },
|
|
25111
|
+
"write-key": { type: "string", description: "Workspace write key (pk_xxx) for init snippet" },
|
|
25112
|
+
"no-install": { type: "boolean", description: "Skip SDK install (config files only)" },
|
|
24787
25113
|
"no-pull": { type: "boolean", description: "Skip first registry pull" },
|
|
24788
25114
|
force: { type: "boolean", description: "Overwrite existing config" }
|
|
24789
25115
|
},
|
|
@@ -24799,40 +25125,63 @@ var initCmd = defineCommand({
|
|
|
24799
25125
|
console.error("[gurulu] --workspace <uuid> required (workspace ID from dashboard)");
|
|
24800
25126
|
process.exit(1);
|
|
24801
25127
|
}
|
|
24802
|
-
const
|
|
24803
|
-
if (!["web", "node", "
|
|
24804
|
-
console.error(`[gurulu] invalid --sdk: ${
|
|
25128
|
+
const sdkArg = String(args.sdk ?? "auto");
|
|
25129
|
+
if (!["web", "node", "auto"].includes(sdkArg)) {
|
|
25130
|
+
console.error(`[gurulu] invalid --sdk: ${sdkArg} (use web|node|auto)`);
|
|
24805
25131
|
process.exit(1);
|
|
24806
25132
|
}
|
|
25133
|
+
const detected = detectProject(cwd);
|
|
25134
|
+
const writeKey = String(args["write-key"] ?? "").trim() || "pk_xxxxxxxxxxxx";
|
|
25135
|
+
const plan = buildInstallPlan(detected, { writeKey, workspaceId });
|
|
25136
|
+
const effectiveSdkPref = sdkArg === "auto" ? plan.sdk === "@gurulu/node" ? "node" : "web" : sdkArg;
|
|
24807
25137
|
const config = {
|
|
24808
25138
|
workspace_id: workspaceId,
|
|
24809
25139
|
endpoint: String(args.endpoint),
|
|
24810
|
-
sdk_preference:
|
|
25140
|
+
sdk_preference: effectiveSdkPref,
|
|
24811
25141
|
registry_path: ".gurulu/event-registry.json",
|
|
24812
25142
|
generated_path: ".gurulu/generated.d.ts",
|
|
24813
25143
|
manifest_lock_path: ".gurulu/manifest.lock",
|
|
24814
25144
|
auto_pull_on_init: true
|
|
24815
25145
|
};
|
|
24816
25146
|
writeProjectConfig(config, cwd);
|
|
24817
|
-
const dir =
|
|
24818
|
-
if (!
|
|
25147
|
+
const dir = dirname3(projectConfigPath(cwd));
|
|
25148
|
+
if (!existsSync5(dir))
|
|
24819
25149
|
mkdirSync2(dir, { recursive: true });
|
|
24820
|
-
if (!
|
|
25150
|
+
if (!existsSync5(projectRegistryPath(cwd))) {
|
|
24821
25151
|
writeFileSync3(projectRegistryPath(cwd), `{}
|
|
24822
25152
|
`, "utf-8");
|
|
24823
25153
|
}
|
|
24824
|
-
if (!
|
|
25154
|
+
if (!existsSync5(projectGeneratedPath(cwd))) {
|
|
24825
25155
|
writeFileSync3(projectGeneratedPath(cwd), "// Run `gurulu pull` to populate typed events.\n", "utf-8");
|
|
24826
25156
|
}
|
|
24827
|
-
if (!
|
|
25157
|
+
if (!existsSync5(projectManifestLockPath(cwd))) {
|
|
24828
25158
|
writeFileSync3(projectManifestLockPath(cwd), `0.0.0
|
|
24829
25159
|
`, "utf-8");
|
|
24830
25160
|
}
|
|
24831
25161
|
console.log(`[gurulu] initialized ${projectConfigPath(cwd)}`);
|
|
25162
|
+
if (args["no-install"]) {
|
|
25163
|
+
console.log(`[gurulu] skipping SDK install (--no-install).`);
|
|
25164
|
+
console.log(` manual: ${plan.installCommand}`);
|
|
25165
|
+
} else if (!detected.hasPackageJson) {
|
|
25166
|
+
console.log(`[gurulu] no package.json found in ${cwd} — skipping SDK install.`);
|
|
25167
|
+
console.log(` use the script tag: <script src="https://cdn.gurulu.io/t.js" data-workspace="${writeKey}"></script>`);
|
|
25168
|
+
} else {
|
|
25169
|
+
console.log(`[gurulu] detected ${detected.framework} (${detected.packageManager}) — installing ${plan.sdk}…`);
|
|
25170
|
+
const result = await execInstall(plan, { cwd });
|
|
25171
|
+
if (result.ok) {
|
|
25172
|
+
console.log(`[gurulu] ${plan.sdk} installed (${(result.durationMs / 1000).toFixed(1)}s).`);
|
|
25173
|
+
} else {
|
|
25174
|
+
console.warn(`[gurulu] install failed (exit ${result.exitCode ?? "n/a"}) — run manually: ${plan.installCommand}`);
|
|
25175
|
+
}
|
|
25176
|
+
}
|
|
25177
|
+
console.log("");
|
|
24832
25178
|
console.log("[gurulu] next steps:");
|
|
24833
25179
|
console.log(" 1. gurulu login — authenticate (or store API key)");
|
|
24834
25180
|
console.log(" 2. gurulu pull — fetch registry + code-gen");
|
|
24835
|
-
console.log(
|
|
25181
|
+
console.log(` 3. ${plan.placementHint}`);
|
|
25182
|
+
console.log("");
|
|
25183
|
+
console.log("[gurulu] init snippet:");
|
|
25184
|
+
console.log(plan.initSnippet);
|
|
24836
25185
|
if (!args["no-pull"]) {
|
|
24837
25186
|
try {
|
|
24838
25187
|
await runPull({ cwd });
|
|
@@ -24942,7 +25291,7 @@ var pushCmd = defineCommand({
|
|
|
24942
25291
|
});
|
|
24943
25292
|
|
|
24944
25293
|
// src/index.ts
|
|
24945
|
-
var VERSION = "1.0.
|
|
25294
|
+
var VERSION = "1.0.4";
|
|
24946
25295
|
var mcpCmd = defineCommand({
|
|
24947
25296
|
meta: {
|
|
24948
25297
|
name: "mcp",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type Framework = 'next' | 'react' | 'vue' | 'nuxt' | 'svelte' | 'astro' | 'vite' | 'express' | 'fastify' | 'hono' | 'koa' | 'node-server' | 'unknown';
|
|
2
|
+
export type Runtime = 'browser' | 'node' | 'unknown';
|
|
3
|
+
export type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';
|
|
4
|
+
export interface DetectedProject {
|
|
5
|
+
dir: string;
|
|
6
|
+
hasPackageJson: boolean;
|
|
7
|
+
framework: Framework;
|
|
8
|
+
runtime: Runtime;
|
|
9
|
+
packageManager: PackageManager;
|
|
10
|
+
packageJson: PackageJsonShape | null;
|
|
11
|
+
}
|
|
12
|
+
interface PackageJsonShape {
|
|
13
|
+
name?: string;
|
|
14
|
+
type?: 'module' | 'commonjs';
|
|
15
|
+
dependencies?: Record<string, string>;
|
|
16
|
+
devDependencies?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Walk up the directory tree (max 6 levels) looking for a lockfile.
|
|
20
|
+
* Monorepo workspaces typically have the lockfile at the repo root.
|
|
21
|
+
*/
|
|
22
|
+
export declare function detectPackageManager(dir: string): PackageManager;
|
|
23
|
+
export declare function detectFramework(pkg: PackageJsonShape | null, dir: string): Framework;
|
|
24
|
+
export declare function frameworkRuntime(fw: Framework): Runtime;
|
|
25
|
+
export declare function detectProject(dir: string): DetectedProject;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=detect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,GACT,SAAS,GACT,MAAM,GACN,KAAK,GACL,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACtC;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAiBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAchE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAkBpF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAmBvD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAW1D"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// src/lib/detect.ts
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, parse } from "node:path";
|
|
4
|
+
function readPackageJson(dir) {
|
|
5
|
+
const p = join(dir, "package.json");
|
|
6
|
+
if (!existsSync(p))
|
|
7
|
+
return null;
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function hasDep(pkg, name) {
|
|
15
|
+
if (!pkg)
|
|
16
|
+
return false;
|
|
17
|
+
return Boolean(pkg.dependencies?.[name] ?? pkg.devDependencies?.[name]);
|
|
18
|
+
}
|
|
19
|
+
function detectPackageManager(dir) {
|
|
20
|
+
const root = parse(dir).root;
|
|
21
|
+
let cur = dir;
|
|
22
|
+
for (let i = 0;i < 6; i++) {
|
|
23
|
+
if (existsSync(join(cur, "bun.lock")) || existsSync(join(cur, "bun.lockb")))
|
|
24
|
+
return "bun";
|
|
25
|
+
if (existsSync(join(cur, "pnpm-lock.yaml")))
|
|
26
|
+
return "pnpm";
|
|
27
|
+
if (existsSync(join(cur, "yarn.lock")))
|
|
28
|
+
return "yarn";
|
|
29
|
+
if (existsSync(join(cur, "package-lock.json")))
|
|
30
|
+
return "npm";
|
|
31
|
+
if (cur === root)
|
|
32
|
+
break;
|
|
33
|
+
const parent = dirname(cur);
|
|
34
|
+
if (parent === cur)
|
|
35
|
+
break;
|
|
36
|
+
cur = parent;
|
|
37
|
+
}
|
|
38
|
+
return "npm";
|
|
39
|
+
}
|
|
40
|
+
function detectFramework(pkg, dir) {
|
|
41
|
+
if (!pkg)
|
|
42
|
+
return "unknown";
|
|
43
|
+
if (hasDep(pkg, "next"))
|
|
44
|
+
return "next";
|
|
45
|
+
if (hasDep(pkg, "nuxt"))
|
|
46
|
+
return "nuxt";
|
|
47
|
+
if (hasDep(pkg, "@sveltejs/kit") || hasDep(pkg, "svelte"))
|
|
48
|
+
return "svelte";
|
|
49
|
+
if (hasDep(pkg, "astro"))
|
|
50
|
+
return "astro";
|
|
51
|
+
if (hasDep(pkg, "vite") && (hasDep(pkg, "vue") || hasDep(pkg, "react")))
|
|
52
|
+
return "vite";
|
|
53
|
+
if (hasDep(pkg, "vue"))
|
|
54
|
+
return "vue";
|
|
55
|
+
if (hasDep(pkg, "react"))
|
|
56
|
+
return "react";
|
|
57
|
+
if (hasDep(pkg, "hono"))
|
|
58
|
+
return "hono";
|
|
59
|
+
if (hasDep(pkg, "fastify"))
|
|
60
|
+
return "fastify";
|
|
61
|
+
if (hasDep(pkg, "express"))
|
|
62
|
+
return "express";
|
|
63
|
+
if (hasDep(pkg, "koa"))
|
|
64
|
+
return "koa";
|
|
65
|
+
if (existsSync(join(dir, "server.js")) || existsSync(join(dir, "server.ts")))
|
|
66
|
+
return "node-server";
|
|
67
|
+
return "unknown";
|
|
68
|
+
}
|
|
69
|
+
function frameworkRuntime(fw) {
|
|
70
|
+
switch (fw) {
|
|
71
|
+
case "next":
|
|
72
|
+
case "react":
|
|
73
|
+
case "vue":
|
|
74
|
+
case "nuxt":
|
|
75
|
+
case "svelte":
|
|
76
|
+
case "astro":
|
|
77
|
+
case "vite":
|
|
78
|
+
return "browser";
|
|
79
|
+
case "express":
|
|
80
|
+
case "fastify":
|
|
81
|
+
case "hono":
|
|
82
|
+
case "koa":
|
|
83
|
+
case "node-server":
|
|
84
|
+
return "node";
|
|
85
|
+
default:
|
|
86
|
+
return "unknown";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function detectProject(dir) {
|
|
90
|
+
const pkg = readPackageJson(dir);
|
|
91
|
+
const framework = detectFramework(pkg, dir);
|
|
92
|
+
return {
|
|
93
|
+
dir,
|
|
94
|
+
hasPackageJson: pkg !== null,
|
|
95
|
+
framework,
|
|
96
|
+
runtime: frameworkRuntime(framework),
|
|
97
|
+
packageManager: detectPackageManager(dir),
|
|
98
|
+
packageJson: pkg
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
frameworkRuntime,
|
|
103
|
+
detectProject,
|
|
104
|
+
detectPackageManager,
|
|
105
|
+
detectFramework
|
|
106
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { InstallPlan } from './install-plan.ts';
|
|
2
|
+
export interface ExecInstallOptions {
|
|
3
|
+
cwd: string;
|
|
4
|
+
/** Suppress stdout/stderr forwarding (still captured in result). */
|
|
5
|
+
silent?: boolean;
|
|
6
|
+
/** Hard timeout in ms (default 120s). */
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ExecInstallResult {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
exitCode: number | null;
|
|
12
|
+
durationMs: number;
|
|
13
|
+
command: string;
|
|
14
|
+
stderr?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Spawn the package manager install command for the planned SDK.
|
|
18
|
+
* Streams output to the parent process (unless `silent: true`).
|
|
19
|
+
*/
|
|
20
|
+
export declare function execInstall(plan: InstallPlan, opts: ExecInstallOptions): Promise<ExecInstallResult>;
|
|
21
|
+
//# sourceMappingURL=exec-install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec-install.d.ts","sourceRoot":"","sources":["../../src/lib/exec-install.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,oEAAoE;IACpE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,iBAAiB,CAAC,CA6D5B"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DetectedProject, Framework, PackageManager } from './detect.ts';
|
|
2
|
+
export type SdkPackage = '@gurulu/web' | '@gurulu/node';
|
|
3
|
+
export interface InstallPlanContext {
|
|
4
|
+
/** Workspace key (pk_xxx) — script tag + init() içine inject. */
|
|
5
|
+
writeKey?: string;
|
|
6
|
+
/** Workspace UUID — CLI/MCP referansı. */
|
|
7
|
+
workspaceId?: string;
|
|
8
|
+
/** Server-side projeler için API key (sk_xxx). */
|
|
9
|
+
apiKey?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface InstallPlan {
|
|
12
|
+
sdk: SdkPackage;
|
|
13
|
+
packageManager: PackageManager;
|
|
14
|
+
installCommand: string;
|
|
15
|
+
initSnippet: string;
|
|
16
|
+
envKeys: Array<{
|
|
17
|
+
key: string;
|
|
18
|
+
example: string;
|
|
19
|
+
required: boolean;
|
|
20
|
+
}>;
|
|
21
|
+
placementHint: string;
|
|
22
|
+
framework: Framework;
|
|
23
|
+
}
|
|
24
|
+
export declare function buildInstallPlan(detected: DetectedProject, ctx?: InstallPlanContext): InstallPlan;
|
|
25
|
+
//# sourceMappingURL=install-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-plan.d.ts","sourceRoot":"","sources":["../../src/lib/install-plan.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE9E,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,cAAc,CAAC;AAExD,MAAM,WAAW,kBAAkB;IACjC,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,UAAU,CAAC;IAChB,cAAc,EAAE,cAAc,CAAC;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACpE,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;CACtB;AAwKD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,eAAe,EACzB,GAAG,GAAE,kBAAuB,GAC3B,WAAW,CAYb"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// src/lib/install-plan.ts
|
|
2
|
+
function installCmdFor(pm, pkg) {
|
|
3
|
+
switch (pm) {
|
|
4
|
+
case "bun":
|
|
5
|
+
return `bun add ${pkg}`;
|
|
6
|
+
case "pnpm":
|
|
7
|
+
return `pnpm add ${pkg}`;
|
|
8
|
+
case "yarn":
|
|
9
|
+
return `yarn add ${pkg}`;
|
|
10
|
+
default:
|
|
11
|
+
return `npm install ${pkg}`;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function sdkFor(framework) {
|
|
15
|
+
switch (framework) {
|
|
16
|
+
case "express":
|
|
17
|
+
case "fastify":
|
|
18
|
+
case "hono":
|
|
19
|
+
case "koa":
|
|
20
|
+
case "node-server":
|
|
21
|
+
return "@gurulu/node";
|
|
22
|
+
default:
|
|
23
|
+
return "@gurulu/web";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function snippetWeb(workspaceKey) {
|
|
27
|
+
return `import gurulu from '@gurulu/web';
|
|
28
|
+
|
|
29
|
+
gurulu.init({
|
|
30
|
+
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
31
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
|
|
32
|
+
});`;
|
|
33
|
+
}
|
|
34
|
+
function snippetNext(workspaceKey) {
|
|
35
|
+
return `// src/app/gurulu-provider.tsx
|
|
36
|
+
'use client';
|
|
37
|
+
import { useEffect } from 'react';
|
|
38
|
+
import gurulu from '@gurulu/web';
|
|
39
|
+
|
|
40
|
+
export function GuruluProvider({ children }: { children: React.ReactNode }) {
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
gurulu.init({
|
|
43
|
+
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
44
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT,
|
|
45
|
+
});
|
|
46
|
+
}, []);
|
|
47
|
+
return <>{children}</>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Then wrap your root in app/layout.tsx:
|
|
51
|
+
// <GuruluProvider>{children}</GuruluProvider>`;
|
|
52
|
+
}
|
|
53
|
+
function snippetNode(workspaceKey) {
|
|
54
|
+
return `import { createGurulu } from '@gurulu/node';
|
|
55
|
+
|
|
56
|
+
const gurulu = createGurulu({
|
|
57
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
58
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
59
|
+
endpoint: process.env.GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// In your handler:
|
|
63
|
+
await gurulu.track('purchase_completed', {
|
|
64
|
+
user_id: 'u_42',
|
|
65
|
+
order_id: 'o_123',
|
|
66
|
+
total: 49.99,
|
|
67
|
+
});`;
|
|
68
|
+
}
|
|
69
|
+
function snippetExpress(workspaceKey) {
|
|
70
|
+
return `import express from 'express';
|
|
71
|
+
import { guruluMiddleware } from '@gurulu/node/middleware/express';
|
|
72
|
+
|
|
73
|
+
const app = express();
|
|
74
|
+
app.use(guruluMiddleware({
|
|
75
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
76
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
77
|
+
endpoint: process.env.GURULU_ENDPOINT,
|
|
78
|
+
}));`;
|
|
79
|
+
}
|
|
80
|
+
function snippetHono(workspaceKey) {
|
|
81
|
+
return `import { Hono } from 'hono';
|
|
82
|
+
import { createGurulu } from '@gurulu/node';
|
|
83
|
+
|
|
84
|
+
const gurulu = createGurulu({
|
|
85
|
+
workspaceKey: process.env.GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
86
|
+
apiKey: process.env.GURULU_API_KEY,
|
|
87
|
+
endpoint: process.env.GURULU_ENDPOINT,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const app = new Hono();
|
|
91
|
+
app.post('/checkout/complete', async (c) => {
|
|
92
|
+
await gurulu.track('purchase_completed', await c.req.json());
|
|
93
|
+
return c.json({ ok: true });
|
|
94
|
+
});`;
|
|
95
|
+
}
|
|
96
|
+
function placementHintFor(framework) {
|
|
97
|
+
switch (framework) {
|
|
98
|
+
case "next":
|
|
99
|
+
return "Add the GuruluProvider to app/layout.tsx (App Router) or pages/_app.tsx (Pages Router) and wrap children.";
|
|
100
|
+
case "nuxt":
|
|
101
|
+
return "Add to plugins/gurulu.client.ts and register in nuxt.config.ts.";
|
|
102
|
+
case "svelte":
|
|
103
|
+
return "Add to src/routes/+layout.svelte inside onMount().";
|
|
104
|
+
case "astro":
|
|
105
|
+
return "Add to src/layouts/Base.astro inside a <script> tag (or use a hydrated component).";
|
|
106
|
+
case "vite":
|
|
107
|
+
case "react":
|
|
108
|
+
case "vue":
|
|
109
|
+
return "Add to src/main.ts (or your entry file) before mounting the app.";
|
|
110
|
+
case "express":
|
|
111
|
+
return "Register guruluMiddleware before your route handlers in your app entry file.";
|
|
112
|
+
case "fastify":
|
|
113
|
+
return "Register the Gurulu plugin via fastify.register() before route definitions.";
|
|
114
|
+
case "hono":
|
|
115
|
+
return "Initialize the SDK at the top of your app file; call track() inside route handlers.";
|
|
116
|
+
case "koa":
|
|
117
|
+
return "Add as Koa middleware (app.use) before your routes.";
|
|
118
|
+
case "node-server":
|
|
119
|
+
return "Initialize the SDK at startup; call track() from your handlers.";
|
|
120
|
+
default:
|
|
121
|
+
return "Initialize the SDK at your app entry point.";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function initSnippetFor(framework, sdk, workspaceKey) {
|
|
125
|
+
if (sdk === "@gurulu/node") {
|
|
126
|
+
if (framework === "express")
|
|
127
|
+
return snippetExpress(workspaceKey);
|
|
128
|
+
if (framework === "hono")
|
|
129
|
+
return snippetHono(workspaceKey);
|
|
130
|
+
return snippetNode(workspaceKey);
|
|
131
|
+
}
|
|
132
|
+
if (framework === "next")
|
|
133
|
+
return snippetNext(workspaceKey);
|
|
134
|
+
return snippetWeb(workspaceKey);
|
|
135
|
+
}
|
|
136
|
+
function envKeysFor(sdk, framework) {
|
|
137
|
+
if (sdk === "@gurulu/node") {
|
|
138
|
+
return [
|
|
139
|
+
{ key: "GURULU_WORKSPACE", example: "pk_live_xxxxxxxxxxxx", required: true },
|
|
140
|
+
{ key: "GURULU_API_KEY", example: "sk_live_xxxxxxxxxxxx", required: true },
|
|
141
|
+
{
|
|
142
|
+
key: "GURULU_ENDPOINT",
|
|
143
|
+
example: "https://ingest.gurulu.io",
|
|
144
|
+
required: false
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
const prefix = framework === "next" ? "NEXT_PUBLIC_GURULU" : "VITE_GURULU";
|
|
149
|
+
return [
|
|
150
|
+
{ key: `${prefix}_WORKSPACE`, example: "pk_live_xxxxxxxxxxxx", required: false },
|
|
151
|
+
{ key: `${prefix}_ENDPOINT`, example: "https://ingest.gurulu.io", required: false }
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
function buildInstallPlan(detected, ctx = {}) {
|
|
155
|
+
const sdk = sdkFor(detected.framework);
|
|
156
|
+
const workspaceKey = ctx.writeKey ?? "pk_xxxxxxxxxxxx";
|
|
157
|
+
return {
|
|
158
|
+
sdk,
|
|
159
|
+
packageManager: detected.packageManager,
|
|
160
|
+
installCommand: installCmdFor(detected.packageManager, sdk),
|
|
161
|
+
initSnippet: initSnippetFor(detected.framework, sdk, workspaceKey),
|
|
162
|
+
envKeys: envKeysFor(sdk, detected.framework),
|
|
163
|
+
placementHint: placementHintFor(detected.framework),
|
|
164
|
+
framework: detected.framework
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
export {
|
|
168
|
+
buildInstallPlan
|
|
169
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gurulu/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"publishConfig": {
|
|
@@ -26,6 +26,16 @@
|
|
|
26
26
|
"import": "./dist/index.js",
|
|
27
27
|
"default": "./dist/index.js"
|
|
28
28
|
},
|
|
29
|
+
"./detect": {
|
|
30
|
+
"types": "./dist/lib/detect.d.ts",
|
|
31
|
+
"import": "./dist/lib/detect.js",
|
|
32
|
+
"default": "./dist/lib/detect.js"
|
|
33
|
+
},
|
|
34
|
+
"./install-plan": {
|
|
35
|
+
"types": "./dist/lib/install-plan.d.ts",
|
|
36
|
+
"import": "./dist/lib/install-plan.js",
|
|
37
|
+
"default": "./dist/lib/install-plan.js"
|
|
38
|
+
},
|
|
29
39
|
"./package.json": "./package.json"
|
|
30
40
|
},
|
|
31
41
|
"files": [
|
|
@@ -34,8 +44,9 @@
|
|
|
34
44
|
"LICENSE"
|
|
35
45
|
],
|
|
36
46
|
"scripts": {
|
|
37
|
-
"build": "rm -rf dist && bun run build:js && bun run build:types && bun run build:bin",
|
|
47
|
+
"build": "rm -rf dist && bun run build:js && bun run build:lib && bun run build:types && bun run build:bin",
|
|
38
48
|
"build:js": "bun build ./src/index.ts --outdir ./dist --target node --format=esm",
|
|
49
|
+
"build:lib": "bun build ./src/lib/detect.ts ./src/lib/install-plan.ts --outdir ./dist/lib --target node --format=esm",
|
|
39
50
|
"build:bin": "bun build ./src/bin.ts --outdir ./dist --target node --format=esm && chmod +x dist/bin.js",
|
|
40
51
|
"build:types": "tsc -p tsconfig.build.json",
|
|
41
52
|
"typecheck": "tsc --noEmit",
|