@gurulu/cli 1.6.5 → 1.6.7

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 CHANGED
@@ -39672,6 +39672,84 @@ var validateCmd = defineCommand({
39672
39672
  }
39673
39673
  });
39674
39674
 
39675
+ // src/wizard/verify.ts
39676
+ import { randomUUID } from "node:crypto";
39677
+ function deriveIngestEndpoint(apiEndpoint) {
39678
+ try {
39679
+ const u3 = new URL(apiEndpoint);
39680
+ if (u3.hostname.startsWith("api.")) {
39681
+ u3.hostname = `ingest.${u3.hostname.slice(4)}`;
39682
+ return u3.origin;
39683
+ }
39684
+ if (u3.hostname === "localhost" || u3.hostname === "127.0.0.1")
39685
+ return apiEndpoint;
39686
+ return u3.origin;
39687
+ } catch {
39688
+ return "https://ingest.gurulu.io";
39689
+ }
39690
+ }
39691
+ async function selfVerify(opts) {
39692
+ const fetchImpl2 = opts.fetchImpl ?? fetch;
39693
+ const nowMs = (opts.now ?? Date.now)();
39694
+ const anonymousId = `gurulu_verify_${nowMs.toString(36)}${Math.floor(nowMs % 1000)}`;
39695
+ const sessionId = randomUUID();
39696
+ const eventType = opts.eventType ?? "interaction";
39697
+ const payload = {
39698
+ event_key: opts.eventKey,
39699
+ event_type: eventType,
39700
+ occurred_at: new Date(nowMs).toISOString(),
39701
+ anonymous_id: anonymousId,
39702
+ session_id: sessionId,
39703
+ producer: opts.producer ?? "sdk_web",
39704
+ properties: { ...opts.properties ?? {}, gurulu_install_verify: true },
39705
+ test_mode: true
39706
+ };
39707
+ let resp;
39708
+ try {
39709
+ const r3 = await fetchImpl2(`${opts.ingestEndpoint}/v1/ingest/event`, {
39710
+ method: "POST",
39711
+ headers: {
39712
+ "Content-Type": "application/json",
39713
+ Authorization: `Bearer ${opts.writeKey}`
39714
+ },
39715
+ body: JSON.stringify(payload)
39716
+ });
39717
+ if (r3.status === 401 || r3.status === 403) {
39718
+ return mk("error", false, [`ingest auth reddetti (${r3.status}) — write key geçersiz`], opts, anonymousId);
39719
+ }
39720
+ if (r3.status >= 500) {
39721
+ return mk("error", false, [`ingest ${r3.status} — sunucu hatası`], opts, anonymousId);
39722
+ }
39723
+ resp = await r3.json().catch(() => ({}));
39724
+ } catch (e2) {
39725
+ const msg = e2 instanceof Error ? e2.message : String(e2);
39726
+ return mk("error", false, [`ingest erişilemedi: ${msg}`], opts, anonymousId);
39727
+ }
39728
+ const decision = resp.decision ?? "error";
39729
+ const ok = decision === "accept" || decision === "warn";
39730
+ const reasons = (resp.reasons ?? []).map((x2) => String(x2));
39731
+ const result = mk(decision, ok, reasons, opts, anonymousId);
39732
+ if (ok && opts.client) {
39733
+ result.landedInClickhouse = await pollRecent(opts.client, opts.eventKey, anonymousId);
39734
+ }
39735
+ return result;
39736
+ }
39737
+ function mk(decision, ok, reasons, opts, anonymousId) {
39738
+ return { ok, decision, reasons, eventKey: opts.eventKey, anonymousId };
39739
+ }
39740
+ async function pollRecent(client, eventKey, anonymousId, attempts = 8, delayMs = 3000) {
39741
+ for (let i2 = 0;i2 < attempts; i2++) {
39742
+ try {
39743
+ const res = await client.recentEvents({ event_key: eventKey, limit: 50 });
39744
+ if (res.events.some((e2) => e2.anonymous_id === anonymousId))
39745
+ return true;
39746
+ } catch {}
39747
+ if (i2 < attempts - 1)
39748
+ await new Promise((r3) => setTimeout(r3, delayMs));
39749
+ }
39750
+ return false;
39751
+ }
39752
+
39675
39753
  // src/commands/doctor.ts
39676
39754
  async function runDoctor(opts = {}) {
39677
39755
  const cwd = opts.cwd ?? process.cwd();
@@ -39780,6 +39858,39 @@ async function runDoctor(opts = {}) {
39780
39858
  message: `health overview unavailable: ${err instanceof Error ? err.message : String(err)}`
39781
39859
  });
39782
39860
  }
39861
+ if (manifest && manifest.events.length > 0) {
39862
+ const candidate = manifest.events.find((e2) => e2.lifecycle === "active" && e2.type === "interaction" && e2.producerAllowed.includes("sdk_web"));
39863
+ if (candidate) {
39864
+ const props = {};
39865
+ for (const pr of candidate.properties) {
39866
+ if (!pr.required)
39867
+ continue;
39868
+ props[pr.name] = pr.type === "number" ? 1 : pr.type === "boolean" ? true : pr.type === "array" ? [] : pr.type === "json" ? {} : "e2e";
39869
+ }
39870
+ const vr = await selfVerify({
39871
+ ingestEndpoint: deriveIngestEndpoint(project.endpoint ?? cred.endpoint),
39872
+ writeKey: cred.apiKey,
39873
+ eventKey: candidate.key,
39874
+ eventType: candidate.type,
39875
+ producer: "sdk_web",
39876
+ properties: props,
39877
+ client
39878
+ });
39879
+ const landed = vr.landedInClickhouse;
39880
+ checks.push({
39881
+ name: "end_to_end",
39882
+ status: landed === true ? "pass" : vr.ok ? "warn" : "fail",
39883
+ message: landed === true ? "test event ingest→pipeline→dashboard ulaştı (uçtan uca çalışıyor)" : vr.ok ? "ingest kabul etti ama event ~24sn'de dashboard'a ULAŞMADI — pipeline gecikmesi/sorunu" : `test event '${candidate.key}' ${vr.decision}: ${vr.reasons[0] ?? "doğrulanamadı"}`,
39884
+ detail: { decision: vr.decision, landed_in_clickhouse: landed ?? null }
39885
+ });
39886
+ } else {
39887
+ checks.push({
39888
+ name: "end_to_end",
39889
+ status: "skip",
39890
+ message: "uygun test event yok (aktif interaction + sdk_web producer) — E2E atlandı"
39891
+ });
39892
+ }
39893
+ }
39783
39894
  try {
39784
39895
  const scan = runValidate({ cwd });
39785
39896
  const total = scan.deprecated_calls.length + scan.unknown_calls.length;
@@ -40223,7 +40334,7 @@ var pullCmd = defineCommand({
40223
40334
  });
40224
40335
 
40225
40336
  // src/lib/detect.ts
40226
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
40337
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
40227
40338
  import { dirname as dirname2, join as join5, parse } from "node:path";
40228
40339
  var COMPETITOR_DEP_MAP = {
40229
40340
  "posthog-js": "posthog",
@@ -40389,11 +40500,51 @@ function detectLlmFrameworks(pkg) {
40389
40500
  }
40390
40501
  return [...found];
40391
40502
  }
40503
+ function expandWorkspaceGlobs(rootDir, patterns) {
40504
+ const out = [];
40505
+ for (const pat of patterns) {
40506
+ if (pat.endsWith("/*")) {
40507
+ const base = join5(rootDir, pat.slice(0, -2));
40508
+ if (!existsSync4(base))
40509
+ continue;
40510
+ try {
40511
+ for (const name of readdirSync2(base)) {
40512
+ const d2 = join5(base, name);
40513
+ if (existsSync4(join5(d2, "package.json")))
40514
+ out.push(d2);
40515
+ }
40516
+ } catch {}
40517
+ } else {
40518
+ const d2 = join5(rootDir, pat);
40519
+ if (existsSync4(join5(d2, "package.json")))
40520
+ out.push(d2);
40521
+ }
40522
+ }
40523
+ return out;
40524
+ }
40525
+ function findWorkspaceFrameworkPackage(rootDir, pkg) {
40526
+ const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages ?? [];
40527
+ if (patterns.length === 0)
40528
+ return null;
40529
+ let fallback = null;
40530
+ for (const d2 of expandWorkspaceGlobs(rootDir, patterns)) {
40531
+ const p = readPackageJson(d2);
40532
+ if (!p)
40533
+ continue;
40534
+ const fw = detectFramework(p, d2);
40535
+ if (frameworkRuntime(fw) === "browser")
40536
+ return { dir: d2, framework: fw, pkg: p };
40537
+ if (fw !== "unknown" && !fallback)
40538
+ fallback = { dir: d2, framework: fw, pkg: p };
40539
+ }
40540
+ return fallback;
40541
+ }
40392
40542
  function detectProject(dir) {
40393
40543
  const pkg = readPackageJson(dir);
40394
40544
  let framework = detectFramework(pkg, dir);
40395
40545
  const { root, isMonorepo } = findWorkspaceRoot(dir);
40396
40546
  let effectivePkg = pkg;
40547
+ let appDir = dir;
40397
40548
  if (framework === "unknown" && isMonorepo && root !== dir) {
40398
40549
  const rootPkg = readPackageJson(root);
40399
40550
  const rootFw = detectFramework(rootPkg, root);
@@ -40402,19 +40553,28 @@ function detectProject(dir) {
40402
40553
  effectivePkg = pkg ?? rootPkg;
40403
40554
  }
40404
40555
  }
40556
+ if (framework === "unknown" && pkg?.workspaces) {
40557
+ const found = findWorkspaceFrameworkPackage(dir, pkg);
40558
+ if (found) {
40559
+ framework = found.framework;
40560
+ appDir = found.dir;
40561
+ effectivePkg = found.pkg;
40562
+ }
40563
+ }
40405
40564
  return {
40406
40565
  dir,
40407
40566
  hasPackageJson: pkg !== null,
40408
40567
  framework,
40409
40568
  runtime: frameworkRuntime(framework),
40410
40569
  packageManager: detectPackageManager(dir),
40411
- packageJson: pkg,
40412
- llmFrameworks: detectLlmFrameworks(pkg),
40570
+ packageJson: effectivePkg,
40571
+ llmFrameworks: detectLlmFrameworks(effectivePkg),
40413
40572
  moduleType: detectModuleType(effectivePkg),
40414
40573
  rootDir: root,
40574
+ appDir,
40415
40575
  isMonorepo,
40416
- competitors: detectCompetitors(pkg),
40417
- language: detectLanguage(dir)
40576
+ competitors: detectCompetitors(effectivePkg),
40577
+ language: detectLanguage(appDir)
40418
40578
  };
40419
40579
  }
40420
40580
 
@@ -41626,7 +41786,7 @@ function createCheckpoint(cwd, snapshots = new Map) {
41626
41786
  }
41627
41787
 
41628
41788
  // src/wizard/context.ts
41629
- import { readdirSync as readdirSync2, readFileSync as readFileSync9 } from "node:fs";
41789
+ import { readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
41630
41790
  import { extname as extname2, join as join10, relative as relative2 } from "node:path";
41631
41791
  var INCLUDE_EXT = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".vue", ".svelte", ".astro"]);
41632
41792
  var SECRET_PATTERNS = [
@@ -41709,7 +41869,7 @@ function collectCandidates(dir, base, depth, maxDepth, out) {
41709
41869
  return;
41710
41870
  let entries;
41711
41871
  try {
41712
- entries = readdirSync2(dir, { withFileTypes: true });
41872
+ entries = readdirSync3(dir, { withFileTypes: true });
41713
41873
  } catch {
41714
41874
  return;
41715
41875
  }
@@ -41931,81 +42091,6 @@ async function renderPlan(plan) {
41931
42091
  return plan.events;
41932
42092
  }
41933
42093
 
41934
- // src/wizard/verify.ts
41935
- function deriveIngestEndpoint(apiEndpoint) {
41936
- try {
41937
- const u3 = new URL(apiEndpoint);
41938
- if (u3.hostname.startsWith("api.")) {
41939
- u3.hostname = `ingest.${u3.hostname.slice(4)}`;
41940
- return u3.origin;
41941
- }
41942
- if (u3.hostname === "localhost" || u3.hostname === "127.0.0.1")
41943
- return apiEndpoint;
41944
- return u3.origin;
41945
- } catch {
41946
- return "https://ingest.gurulu.io";
41947
- }
41948
- }
41949
- async function selfVerify(opts) {
41950
- const fetchImpl2 = opts.fetchImpl ?? fetch;
41951
- const nowMs = (opts.now ?? Date.now)();
41952
- const anonymousId = `gurulu_verify_${nowMs.toString(36)}${Math.floor(nowMs % 1000)}`;
41953
- const eventType = opts.eventType ?? "interaction";
41954
- const payload = {
41955
- event_key: opts.eventKey,
41956
- event_type: eventType,
41957
- occurred_at: new Date(nowMs).toISOString(),
41958
- anonymous_id: anonymousId,
41959
- producer: "sdk",
41960
- properties: { gurulu_install_verify: true },
41961
- test_mode: true
41962
- };
41963
- let resp;
41964
- try {
41965
- const r3 = await fetchImpl2(`${opts.ingestEndpoint}/v1/ingest/event`, {
41966
- method: "POST",
41967
- headers: {
41968
- "Content-Type": "application/json",
41969
- Authorization: `Bearer ${opts.writeKey}`
41970
- },
41971
- body: JSON.stringify(payload)
41972
- });
41973
- if (r3.status === 401 || r3.status === 403) {
41974
- return mk("error", false, [`ingest auth reddetti (${r3.status}) — write key geçersiz`], opts, anonymousId);
41975
- }
41976
- if (r3.status >= 500) {
41977
- return mk("error", false, [`ingest ${r3.status} — sunucu hatası`], opts, anonymousId);
41978
- }
41979
- resp = await r3.json().catch(() => ({}));
41980
- } catch (e2) {
41981
- const msg = e2 instanceof Error ? e2.message : String(e2);
41982
- return mk("error", false, [`ingest erişilemedi: ${msg}`], opts, anonymousId);
41983
- }
41984
- const decision = resp.decision ?? "error";
41985
- const ok = decision === "accept" || decision === "warn";
41986
- const reasons = (resp.reasons ?? []).map((x2) => String(x2));
41987
- const result = mk(decision, ok, reasons, opts, anonymousId);
41988
- if (ok && opts.client) {
41989
- result.landedInClickhouse = await pollRecent(opts.client, opts.eventKey, anonymousId);
41990
- }
41991
- return result;
41992
- }
41993
- function mk(decision, ok, reasons, opts, anonymousId) {
41994
- return { ok, decision, reasons, eventKey: opts.eventKey, anonymousId };
41995
- }
41996
- async function pollRecent(client, eventKey, anonymousId, attempts = 4, delayMs = 1500) {
41997
- for (let i2 = 0;i2 < attempts; i2++) {
41998
- try {
41999
- const res = await client.recentEvents({ event_key: eventKey, limit: 50 });
42000
- if (res.events.some((e2) => e2.anonymous_id === anonymousId))
42001
- return true;
42002
- } catch {}
42003
- if (i2 < attempts - 1)
42004
- await new Promise((r3) => setTimeout(r3, delayMs));
42005
- }
42006
- return false;
42007
- }
42008
-
42009
42094
  // src/wizard/wire.ts
42010
42095
  import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "node:fs";
42011
42096
 
@@ -42381,6 +42466,10 @@ async function runWizard(opts) {
42381
42466
  const { workspaceId, writeKey } = await resolveWorkspace(client, auth.workspaceId, opts);
42382
42467
  phase(3, "Projeni tanıyoruz", "Kodun okunuyor; hangi kullanıcı olaylarını (ör. kayıt, satın alma) izleyeceğimiz planlanıyor.");
42383
42468
  const detected = detectProject(opts.cwd);
42469
+ if (detected.appDir !== opts.cwd) {
42470
+ p4.log.info(c3.dim(`Monorepo: kurulum ${detected.framework} app'ine → ${detected.appDir}`));
42471
+ opts.cwd = detected.appDir;
42472
+ }
42384
42473
  let framework = detected.framework;
42385
42474
  if (opts.framework && FRAMEWORKS.includes(opts.framework)) {
42386
42475
  framework = opts.framework;
@@ -42684,7 +42773,13 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
42684
42773
  if (inj.strategy === "manual")
42685
42774
  p4.log.message(plan.initSnippet);
42686
42775
  }
42687
- p4.outro(`${c3.neon("\uD83E\uDD89 Hazır!")} ${c3.dim("Doğrula:")} ${c3.bold("gurulu doctor")} ${c3.dim("·")} ${c3.dim("Dashboard:")} ${c3.cyan("https://dashboard.gurulu.io/app?onboard=done")}`);
42776
+ const e2eOk = verifyResult?.landedInClickhouse === true;
42777
+ const e2eFailed = verifyResult != null && (verifyResult.landedInClickhouse === false || !verifyResult.ok);
42778
+ if (e2eFailed) {
42779
+ p4.outro(`${c3.bold("⚠ Kurulum yapıldı ama UÇTAN UCA DOĞRULANAMADI")} — kod + kayıt tamam, ama test event dashboard'a ulaşmadı (ingest kabul etti/pipeline'da kayboldu). ${c3.dim("Tekrar:")} ${c3.bold("gurulu doctor")}`);
42780
+ } else {
42781
+ p4.outro(`${c3.neon("\uD83E\uDD89 Hazır!")}${e2eOk ? c3.dim(" · uçtan uca doğrulandı") : ""} ${c3.dim("Doğrula:")} ${c3.bold("gurulu doctor")} ${c3.dim("·")} ${c3.dim("Dashboard:")} ${c3.cyan("https://dashboard.gurulu.io/app?onboard=done")}`);
42782
+ }
42688
42783
  }
42689
42784
  async function resolveWorkspace(client, authWorkspaceId, opts) {
42690
42785
  if (opts.writeKey) {
@@ -42722,7 +42817,13 @@ function verifyLabel(v2) {
42722
42817
  switch (v2.decision) {
42723
42818
  case "accept":
42724
42819
  case "warn":
42725
- return `✓ test event kabul edildi (${v2.decision}) pipeline doğrulandı`;
42820
+ if (v2.landedInClickhouse === false) {
42821
+ return "⚠ ingest kabul etti ama event dashboard'a ULAŞMADI (~24sn) — pipeline gecikmesi/sorunu; `gurulu doctor` ile tekrar dene";
42822
+ }
42823
+ if (v2.landedInClickhouse === true) {
42824
+ return "✓ uçtan uca doğrulandı — test event dashboard'a ulaştı";
42825
+ }
42826
+ return `✓ test event kabul edildi (${v2.decision})`;
42726
42827
  case "quarantine":
42727
42828
  return `⚠ event kayıtlı ama eksik prop (quarantine) — Gurulu'ya bağlısın`;
42728
42829
  case "reject":
@@ -43133,7 +43234,7 @@ var uninstallCmd = defineCommand({
43133
43234
  });
43134
43235
 
43135
43236
  // src/index.ts
43136
- var VERSION = "1.6.5";
43237
+ var VERSION = "1.6.7";
43137
43238
  var mainCmd = defineCommand({
43138
43239
  meta: {
43139
43240
  name: "gurulu",
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAiK/E;AA2BD,eAAO,MAAM,SAAS,qDAkBpB,CAAC"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACrE;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,SAAS,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuN/E;AA2BD,eAAO,MAAM,SAAS,qDAkBpB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const VERSION = "1.6.5";
1
+ export declare const VERSION = "1.6.7";
2
2
  declare const mainCmd: import("citty").CommandDef<{
3
3
  workspace: {
4
4
  type: "string";
package/dist/index.js CHANGED
@@ -39249,6 +39249,84 @@ var validateCmd = defineCommand({
39249
39249
  }
39250
39250
  });
39251
39251
 
39252
+ // src/wizard/verify.ts
39253
+ import { randomUUID } from "node:crypto";
39254
+ function deriveIngestEndpoint(apiEndpoint) {
39255
+ try {
39256
+ const u3 = new URL(apiEndpoint);
39257
+ if (u3.hostname.startsWith("api.")) {
39258
+ u3.hostname = `ingest.${u3.hostname.slice(4)}`;
39259
+ return u3.origin;
39260
+ }
39261
+ if (u3.hostname === "localhost" || u3.hostname === "127.0.0.1")
39262
+ return apiEndpoint;
39263
+ return u3.origin;
39264
+ } catch {
39265
+ return "https://ingest.gurulu.io";
39266
+ }
39267
+ }
39268
+ async function selfVerify(opts) {
39269
+ const fetchImpl2 = opts.fetchImpl ?? fetch;
39270
+ const nowMs = (opts.now ?? Date.now)();
39271
+ const anonymousId = `gurulu_verify_${nowMs.toString(36)}${Math.floor(nowMs % 1000)}`;
39272
+ const sessionId = randomUUID();
39273
+ const eventType = opts.eventType ?? "interaction";
39274
+ const payload = {
39275
+ event_key: opts.eventKey,
39276
+ event_type: eventType,
39277
+ occurred_at: new Date(nowMs).toISOString(),
39278
+ anonymous_id: anonymousId,
39279
+ session_id: sessionId,
39280
+ producer: opts.producer ?? "sdk_web",
39281
+ properties: { ...opts.properties ?? {}, gurulu_install_verify: true },
39282
+ test_mode: true
39283
+ };
39284
+ let resp;
39285
+ try {
39286
+ const r3 = await fetchImpl2(`${opts.ingestEndpoint}/v1/ingest/event`, {
39287
+ method: "POST",
39288
+ headers: {
39289
+ "Content-Type": "application/json",
39290
+ Authorization: `Bearer ${opts.writeKey}`
39291
+ },
39292
+ body: JSON.stringify(payload)
39293
+ });
39294
+ if (r3.status === 401 || r3.status === 403) {
39295
+ return mk("error", false, [`ingest auth reddetti (${r3.status}) — write key geçersiz`], opts, anonymousId);
39296
+ }
39297
+ if (r3.status >= 500) {
39298
+ return mk("error", false, [`ingest ${r3.status} — sunucu hatası`], opts, anonymousId);
39299
+ }
39300
+ resp = await r3.json().catch(() => ({}));
39301
+ } catch (e2) {
39302
+ const msg = e2 instanceof Error ? e2.message : String(e2);
39303
+ return mk("error", false, [`ingest erişilemedi: ${msg}`], opts, anonymousId);
39304
+ }
39305
+ const decision = resp.decision ?? "error";
39306
+ const ok = decision === "accept" || decision === "warn";
39307
+ const reasons = (resp.reasons ?? []).map((x2) => String(x2));
39308
+ const result = mk(decision, ok, reasons, opts, anonymousId);
39309
+ if (ok && opts.client) {
39310
+ result.landedInClickhouse = await pollRecent(opts.client, opts.eventKey, anonymousId);
39311
+ }
39312
+ return result;
39313
+ }
39314
+ function mk(decision, ok, reasons, opts, anonymousId) {
39315
+ return { ok, decision, reasons, eventKey: opts.eventKey, anonymousId };
39316
+ }
39317
+ async function pollRecent(client, eventKey, anonymousId, attempts = 8, delayMs = 3000) {
39318
+ for (let i2 = 0;i2 < attempts; i2++) {
39319
+ try {
39320
+ const res = await client.recentEvents({ event_key: eventKey, limit: 50 });
39321
+ if (res.events.some((e2) => e2.anonymous_id === anonymousId))
39322
+ return true;
39323
+ } catch {}
39324
+ if (i2 < attempts - 1)
39325
+ await new Promise((r3) => setTimeout(r3, delayMs));
39326
+ }
39327
+ return false;
39328
+ }
39329
+
39252
39330
  // src/commands/doctor.ts
39253
39331
  async function runDoctor(opts = {}) {
39254
39332
  const cwd = opts.cwd ?? process.cwd();
@@ -39357,6 +39435,39 @@ async function runDoctor(opts = {}) {
39357
39435
  message: `health overview unavailable: ${err instanceof Error ? err.message : String(err)}`
39358
39436
  });
39359
39437
  }
39438
+ if (manifest && manifest.events.length > 0) {
39439
+ const candidate = manifest.events.find((e2) => e2.lifecycle === "active" && e2.type === "interaction" && e2.producerAllowed.includes("sdk_web"));
39440
+ if (candidate) {
39441
+ const props = {};
39442
+ for (const pr of candidate.properties) {
39443
+ if (!pr.required)
39444
+ continue;
39445
+ props[pr.name] = pr.type === "number" ? 1 : pr.type === "boolean" ? true : pr.type === "array" ? [] : pr.type === "json" ? {} : "e2e";
39446
+ }
39447
+ const vr = await selfVerify({
39448
+ ingestEndpoint: deriveIngestEndpoint(project.endpoint ?? cred.endpoint),
39449
+ writeKey: cred.apiKey,
39450
+ eventKey: candidate.key,
39451
+ eventType: candidate.type,
39452
+ producer: "sdk_web",
39453
+ properties: props,
39454
+ client
39455
+ });
39456
+ const landed = vr.landedInClickhouse;
39457
+ checks.push({
39458
+ name: "end_to_end",
39459
+ status: landed === true ? "pass" : vr.ok ? "warn" : "fail",
39460
+ message: landed === true ? "test event ingest→pipeline→dashboard ulaştı (uçtan uca çalışıyor)" : vr.ok ? "ingest kabul etti ama event ~24sn'de dashboard'a ULAŞMADI — pipeline gecikmesi/sorunu" : `test event '${candidate.key}' ${vr.decision}: ${vr.reasons[0] ?? "doğrulanamadı"}`,
39461
+ detail: { decision: vr.decision, landed_in_clickhouse: landed ?? null }
39462
+ });
39463
+ } else {
39464
+ checks.push({
39465
+ name: "end_to_end",
39466
+ status: "skip",
39467
+ message: "uygun test event yok (aktif interaction + sdk_web producer) — E2E atlandı"
39468
+ });
39469
+ }
39470
+ }
39360
39471
  try {
39361
39472
  const scan = runValidate({ cwd });
39362
39473
  const total = scan.deprecated_calls.length + scan.unknown_calls.length;
@@ -39800,7 +39911,7 @@ var pullCmd = defineCommand({
39800
39911
  });
39801
39912
 
39802
39913
  // src/lib/detect.ts
39803
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
39914
+ import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync4 } from "node:fs";
39804
39915
  import { dirname as dirname2, join as join5, parse } from "node:path";
39805
39916
  var COMPETITOR_DEP_MAP = {
39806
39917
  "posthog-js": "posthog",
@@ -39966,11 +40077,51 @@ function detectLlmFrameworks(pkg) {
39966
40077
  }
39967
40078
  return [...found];
39968
40079
  }
40080
+ function expandWorkspaceGlobs(rootDir, patterns) {
40081
+ const out = [];
40082
+ for (const pat of patterns) {
40083
+ if (pat.endsWith("/*")) {
40084
+ const base = join5(rootDir, pat.slice(0, -2));
40085
+ if (!existsSync4(base))
40086
+ continue;
40087
+ try {
40088
+ for (const name of readdirSync2(base)) {
40089
+ const d2 = join5(base, name);
40090
+ if (existsSync4(join5(d2, "package.json")))
40091
+ out.push(d2);
40092
+ }
40093
+ } catch {}
40094
+ } else {
40095
+ const d2 = join5(rootDir, pat);
40096
+ if (existsSync4(join5(d2, "package.json")))
40097
+ out.push(d2);
40098
+ }
40099
+ }
40100
+ return out;
40101
+ }
40102
+ function findWorkspaceFrameworkPackage(rootDir, pkg) {
40103
+ const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages ?? [];
40104
+ if (patterns.length === 0)
40105
+ return null;
40106
+ let fallback = null;
40107
+ for (const d2 of expandWorkspaceGlobs(rootDir, patterns)) {
40108
+ const p = readPackageJson(d2);
40109
+ if (!p)
40110
+ continue;
40111
+ const fw = detectFramework(p, d2);
40112
+ if (frameworkRuntime(fw) === "browser")
40113
+ return { dir: d2, framework: fw, pkg: p };
40114
+ if (fw !== "unknown" && !fallback)
40115
+ fallback = { dir: d2, framework: fw, pkg: p };
40116
+ }
40117
+ return fallback;
40118
+ }
39969
40119
  function detectProject(dir) {
39970
40120
  const pkg = readPackageJson(dir);
39971
40121
  let framework = detectFramework(pkg, dir);
39972
40122
  const { root, isMonorepo } = findWorkspaceRoot(dir);
39973
40123
  let effectivePkg = pkg;
40124
+ let appDir = dir;
39974
40125
  if (framework === "unknown" && isMonorepo && root !== dir) {
39975
40126
  const rootPkg = readPackageJson(root);
39976
40127
  const rootFw = detectFramework(rootPkg, root);
@@ -39979,19 +40130,28 @@ function detectProject(dir) {
39979
40130
  effectivePkg = pkg ?? rootPkg;
39980
40131
  }
39981
40132
  }
40133
+ if (framework === "unknown" && pkg?.workspaces) {
40134
+ const found = findWorkspaceFrameworkPackage(dir, pkg);
40135
+ if (found) {
40136
+ framework = found.framework;
40137
+ appDir = found.dir;
40138
+ effectivePkg = found.pkg;
40139
+ }
40140
+ }
39982
40141
  return {
39983
40142
  dir,
39984
40143
  hasPackageJson: pkg !== null,
39985
40144
  framework,
39986
40145
  runtime: frameworkRuntime(framework),
39987
40146
  packageManager: detectPackageManager(dir),
39988
- packageJson: pkg,
39989
- llmFrameworks: detectLlmFrameworks(pkg),
40147
+ packageJson: effectivePkg,
40148
+ llmFrameworks: detectLlmFrameworks(effectivePkg),
39990
40149
  moduleType: detectModuleType(effectivePkg),
39991
40150
  rootDir: root,
40151
+ appDir,
39992
40152
  isMonorepo,
39993
- competitors: detectCompetitors(pkg),
39994
- language: detectLanguage(dir)
40153
+ competitors: detectCompetitors(effectivePkg),
40154
+ language: detectLanguage(appDir)
39995
40155
  };
39996
40156
  }
39997
40157
 
@@ -41203,7 +41363,7 @@ function createCheckpoint(cwd, snapshots = new Map) {
41203
41363
  }
41204
41364
 
41205
41365
  // src/wizard/context.ts
41206
- import { readdirSync as readdirSync2, readFileSync as readFileSync9 } from "node:fs";
41366
+ import { readdirSync as readdirSync3, readFileSync as readFileSync9 } from "node:fs";
41207
41367
  import { extname as extname2, join as join10, relative as relative2 } from "node:path";
41208
41368
  var INCLUDE_EXT = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".vue", ".svelte", ".astro"]);
41209
41369
  var SECRET_PATTERNS = [
@@ -41286,7 +41446,7 @@ function collectCandidates(dir, base, depth, maxDepth, out) {
41286
41446
  return;
41287
41447
  let entries;
41288
41448
  try {
41289
- entries = readdirSync2(dir, { withFileTypes: true });
41449
+ entries = readdirSync3(dir, { withFileTypes: true });
41290
41450
  } catch {
41291
41451
  return;
41292
41452
  }
@@ -41508,81 +41668,6 @@ async function renderPlan(plan) {
41508
41668
  return plan.events;
41509
41669
  }
41510
41670
 
41511
- // src/wizard/verify.ts
41512
- function deriveIngestEndpoint(apiEndpoint) {
41513
- try {
41514
- const u3 = new URL(apiEndpoint);
41515
- if (u3.hostname.startsWith("api.")) {
41516
- u3.hostname = `ingest.${u3.hostname.slice(4)}`;
41517
- return u3.origin;
41518
- }
41519
- if (u3.hostname === "localhost" || u3.hostname === "127.0.0.1")
41520
- return apiEndpoint;
41521
- return u3.origin;
41522
- } catch {
41523
- return "https://ingest.gurulu.io";
41524
- }
41525
- }
41526
- async function selfVerify(opts) {
41527
- const fetchImpl2 = opts.fetchImpl ?? fetch;
41528
- const nowMs = (opts.now ?? Date.now)();
41529
- const anonymousId = `gurulu_verify_${nowMs.toString(36)}${Math.floor(nowMs % 1000)}`;
41530
- const eventType = opts.eventType ?? "interaction";
41531
- const payload = {
41532
- event_key: opts.eventKey,
41533
- event_type: eventType,
41534
- occurred_at: new Date(nowMs).toISOString(),
41535
- anonymous_id: anonymousId,
41536
- producer: "sdk",
41537
- properties: { gurulu_install_verify: true },
41538
- test_mode: true
41539
- };
41540
- let resp;
41541
- try {
41542
- const r3 = await fetchImpl2(`${opts.ingestEndpoint}/v1/ingest/event`, {
41543
- method: "POST",
41544
- headers: {
41545
- "Content-Type": "application/json",
41546
- Authorization: `Bearer ${opts.writeKey}`
41547
- },
41548
- body: JSON.stringify(payload)
41549
- });
41550
- if (r3.status === 401 || r3.status === 403) {
41551
- return mk("error", false, [`ingest auth reddetti (${r3.status}) — write key geçersiz`], opts, anonymousId);
41552
- }
41553
- if (r3.status >= 500) {
41554
- return mk("error", false, [`ingest ${r3.status} — sunucu hatası`], opts, anonymousId);
41555
- }
41556
- resp = await r3.json().catch(() => ({}));
41557
- } catch (e2) {
41558
- const msg = e2 instanceof Error ? e2.message : String(e2);
41559
- return mk("error", false, [`ingest erişilemedi: ${msg}`], opts, anonymousId);
41560
- }
41561
- const decision = resp.decision ?? "error";
41562
- const ok = decision === "accept" || decision === "warn";
41563
- const reasons = (resp.reasons ?? []).map((x2) => String(x2));
41564
- const result = mk(decision, ok, reasons, opts, anonymousId);
41565
- if (ok && opts.client) {
41566
- result.landedInClickhouse = await pollRecent(opts.client, opts.eventKey, anonymousId);
41567
- }
41568
- return result;
41569
- }
41570
- function mk(decision, ok, reasons, opts, anonymousId) {
41571
- return { ok, decision, reasons, eventKey: opts.eventKey, anonymousId };
41572
- }
41573
- async function pollRecent(client, eventKey, anonymousId, attempts = 4, delayMs = 1500) {
41574
- for (let i2 = 0;i2 < attempts; i2++) {
41575
- try {
41576
- const res = await client.recentEvents({ event_key: eventKey, limit: 50 });
41577
- if (res.events.some((e2) => e2.anonymous_id === anonymousId))
41578
- return true;
41579
- } catch {}
41580
- if (i2 < attempts - 1)
41581
- await new Promise((r3) => setTimeout(r3, delayMs));
41582
- }
41583
- return false;
41584
- }
41585
-
41586
41671
  // src/wizard/wire.ts
41587
41672
  import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "node:fs";
41588
41673
 
@@ -41958,6 +42043,10 @@ async function runWizard(opts) {
41958
42043
  const { workspaceId, writeKey } = await resolveWorkspace(client, auth.workspaceId, opts);
41959
42044
  phase(3, "Projeni tanıyoruz", "Kodun okunuyor; hangi kullanıcı olaylarını (ör. kayıt, satın alma) izleyeceğimiz planlanıyor.");
41960
42045
  const detected = detectProject(opts.cwd);
42046
+ if (detected.appDir !== opts.cwd) {
42047
+ p4.log.info(c3.dim(`Monorepo: kurulum ${detected.framework} app'ine → ${detected.appDir}`));
42048
+ opts.cwd = detected.appDir;
42049
+ }
41961
42050
  let framework = detected.framework;
41962
42051
  if (opts.framework && FRAMEWORKS.includes(opts.framework)) {
41963
42052
  framework = opts.framework;
@@ -42261,7 +42350,13 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
42261
42350
  if (inj.strategy === "manual")
42262
42351
  p4.log.message(plan.initSnippet);
42263
42352
  }
42264
- p4.outro(`${c3.neon("\uD83E\uDD89 Hazır!")} ${c3.dim("Doğrula:")} ${c3.bold("gurulu doctor")} ${c3.dim("·")} ${c3.dim("Dashboard:")} ${c3.cyan("https://dashboard.gurulu.io/app?onboard=done")}`);
42353
+ const e2eOk = verifyResult?.landedInClickhouse === true;
42354
+ const e2eFailed = verifyResult != null && (verifyResult.landedInClickhouse === false || !verifyResult.ok);
42355
+ if (e2eFailed) {
42356
+ p4.outro(`${c3.bold("⚠ Kurulum yapıldı ama UÇTAN UCA DOĞRULANAMADI")} — kod + kayıt tamam, ama test event dashboard'a ulaşmadı (ingest kabul etti/pipeline'da kayboldu). ${c3.dim("Tekrar:")} ${c3.bold("gurulu doctor")}`);
42357
+ } else {
42358
+ p4.outro(`${c3.neon("\uD83E\uDD89 Hazır!")}${e2eOk ? c3.dim(" · uçtan uca doğrulandı") : ""} ${c3.dim("Doğrula:")} ${c3.bold("gurulu doctor")} ${c3.dim("·")} ${c3.dim("Dashboard:")} ${c3.cyan("https://dashboard.gurulu.io/app?onboard=done")}`);
42359
+ }
42265
42360
  }
42266
42361
  async function resolveWorkspace(client, authWorkspaceId, opts) {
42267
42362
  if (opts.writeKey) {
@@ -42299,7 +42394,13 @@ function verifyLabel(v2) {
42299
42394
  switch (v2.decision) {
42300
42395
  case "accept":
42301
42396
  case "warn":
42302
- return `✓ test event kabul edildi (${v2.decision}) pipeline doğrulandı`;
42397
+ if (v2.landedInClickhouse === false) {
42398
+ return "⚠ ingest kabul etti ama event dashboard'a ULAŞMADI (~24sn) — pipeline gecikmesi/sorunu; `gurulu doctor` ile tekrar dene";
42399
+ }
42400
+ if (v2.landedInClickhouse === true) {
42401
+ return "✓ uçtan uca doğrulandı — test event dashboard'a ulaştı";
42402
+ }
42403
+ return `✓ test event kabul edildi (${v2.decision})`;
42303
42404
  case "quarantine":
42304
42405
  return `⚠ event kayıtlı ama eksik prop (quarantine) — Gurulu'ya bağlısın`;
42305
42406
  case "reject":
@@ -42710,7 +42811,7 @@ var uninstallCmd = defineCommand({
42710
42811
  });
42711
42812
 
42712
42813
  // src/index.ts
42713
- var VERSION = "1.6.5";
42814
+ var VERSION = "1.6.7";
42714
42815
  var mainCmd = defineCommand({
42715
42816
  meta: {
42716
42817
  name: "gurulu",
@@ -22,6 +22,12 @@ export interface DetectedProject {
22
22
  moduleType: ModuleType;
23
23
  /** Workspace root (lockfile dizini); cwd ile aynıysa monorepo değil. */
24
24
  rootDir: string;
25
+ /**
26
+ * Framework app'inin gerçek dizini. Genelde = dir; ama monorepo ROOT'unda
27
+ * çalışıp framework bir alt-pakette (ör. apps/dashboard) ise o alt-paket dizini.
28
+ * `.env` + kod-wire + SDK install BURAYA hedeflenir (root'a değil).
29
+ */
30
+ appDir: string;
25
31
  /** cwd bir monorepo paketi mi (root farklı + workspaces sinyali). */
26
32
  isMonorepo: boolean;
27
33
  /** Tespit edilen rakip analytics SDK'ları (augmentation/twin için, boş = yok). */
@@ -1 +1 @@
1
- {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  // src/lib/detect.ts
2
- import { existsSync, readFileSync } from "node:fs";
2
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { dirname, join, parse } from "node:path";
4
4
  var COMPETITOR_DEP_MAP = {
5
5
  "posthog-js": "posthog",
@@ -165,11 +165,51 @@ function detectLlmFrameworks(pkg) {
165
165
  }
166
166
  return [...found];
167
167
  }
168
+ function expandWorkspaceGlobs(rootDir, patterns) {
169
+ const out = [];
170
+ for (const pat of patterns) {
171
+ if (pat.endsWith("/*")) {
172
+ const base = join(rootDir, pat.slice(0, -2));
173
+ if (!existsSync(base))
174
+ continue;
175
+ try {
176
+ for (const name of readdirSync(base)) {
177
+ const d = join(base, name);
178
+ if (existsSync(join(d, "package.json")))
179
+ out.push(d);
180
+ }
181
+ } catch {}
182
+ } else {
183
+ const d = join(rootDir, pat);
184
+ if (existsSync(join(d, "package.json")))
185
+ out.push(d);
186
+ }
187
+ }
188
+ return out;
189
+ }
190
+ function findWorkspaceFrameworkPackage(rootDir, pkg) {
191
+ const patterns = Array.isArray(pkg.workspaces) ? pkg.workspaces : pkg.workspaces?.packages ?? [];
192
+ if (patterns.length === 0)
193
+ return null;
194
+ let fallback = null;
195
+ for (const d of expandWorkspaceGlobs(rootDir, patterns)) {
196
+ const p = readPackageJson(d);
197
+ if (!p)
198
+ continue;
199
+ const fw = detectFramework(p, d);
200
+ if (frameworkRuntime(fw) === "browser")
201
+ return { dir: d, framework: fw, pkg: p };
202
+ if (fw !== "unknown" && !fallback)
203
+ fallback = { dir: d, framework: fw, pkg: p };
204
+ }
205
+ return fallback;
206
+ }
168
207
  function detectProject(dir) {
169
208
  const pkg = readPackageJson(dir);
170
209
  let framework = detectFramework(pkg, dir);
171
210
  const { root, isMonorepo } = findWorkspaceRoot(dir);
172
211
  let effectivePkg = pkg;
212
+ let appDir = dir;
173
213
  if (framework === "unknown" && isMonorepo && root !== dir) {
174
214
  const rootPkg = readPackageJson(root);
175
215
  const rootFw = detectFramework(rootPkg, root);
@@ -178,19 +218,28 @@ function detectProject(dir) {
178
218
  effectivePkg = pkg ?? rootPkg;
179
219
  }
180
220
  }
221
+ if (framework === "unknown" && pkg?.workspaces) {
222
+ const found = findWorkspaceFrameworkPackage(dir, pkg);
223
+ if (found) {
224
+ framework = found.framework;
225
+ appDir = found.dir;
226
+ effectivePkg = found.pkg;
227
+ }
228
+ }
181
229
  return {
182
230
  dir,
183
231
  hasPackageJson: pkg !== null,
184
232
  framework,
185
233
  runtime: frameworkRuntime(framework),
186
234
  packageManager: detectPackageManager(dir),
187
- packageJson: pkg,
188
- llmFrameworks: detectLlmFrameworks(pkg),
235
+ packageJson: effectivePkg,
236
+ llmFrameworks: detectLlmFrameworks(effectivePkg),
189
237
  moduleType: detectModuleType(effectivePkg),
190
238
  rootDir: root,
239
+ appDir,
191
240
  isMonorepo,
192
- competitors: detectCompetitors(pkg),
193
- language: detectLanguage(dir)
241
+ competitors: detectCompetitors(effectivePkg),
242
+ language: detectLanguage(appDir)
194
243
  };
195
244
  }
196
245
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/wizard/run.ts"],"names":[],"mappings":"AAuCA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,4CAA4C;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,sFAAsF;IACtF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA0DD,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAidlE"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/wizard/run.ts"],"names":[],"mappings":"AAuCA,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,4CAA4C;IAC5C,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,sFAAsF;IACtF,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA0DD,wBAAsB,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAmelE"}
@@ -17,6 +17,10 @@ export interface SelfVerifyOptions {
17
17
  writeKey: string;
18
18
  eventKey: string;
19
19
  eventType?: 'interaction' | 'intent' | 'outcome';
20
+ /** Event'in izin verdiği producer (default 'sdk_web'). Yanlış producer → reject. */
21
+ producer?: string;
22
+ /** Zorunlu prop'ları doldur → quarantine yerine accept (E2E poll CH'ye ulaşsın). */
23
+ properties?: Record<string, unknown>;
20
24
  /** CH'de de doğrula (recentEvents poll). Default false — senkron decision yeter. */
21
25
  client?: ApiClient;
22
26
  /** Test-injection (fetch mock). */
@@ -1 +1 @@
1
- {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/wizard/verify.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC;AAEjG,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,cAAc,CAAC;IACzB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AASD,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAahE;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAC;IACjD,oFAAoF;IACpF,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuD/E"}
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/wizard/verify.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC;AAEjG,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,EAAE,cAAc,CAAC;IACzB,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AASD,mFAAmF;AACnF,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAahE;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAC;IACjD,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,oFAAoF;IACpF,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,mCAAmC;IACnC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CA4D/E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gurulu/cli",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "private": false,
5
5
  "license": "BUSL-1.1",
6
6
  "publishConfig": {