execbro 1.10.16 → 1.10.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +60 -360
  2. package/build/__tests__/unit/flowpoints.test.d.ts +2 -0
  3. package/build/__tests__/unit/flowpoints.test.d.ts.map +1 -0
  4. package/build/__tests__/unit/flowpoints.test.js +126 -0
  5. package/build/__tests__/unit/flowpoints.test.js.map +1 -0
  6. package/build/__tests__/unit/flowpointsQuery.test.d.ts +2 -0
  7. package/build/__tests__/unit/flowpointsQuery.test.d.ts.map +1 -0
  8. package/build/__tests__/unit/flowpointsQuery.test.js +91 -0
  9. package/build/__tests__/unit/flowpointsQuery.test.js.map +1 -0
  10. package/build/__tests__/unit/licenseGrace.test.d.ts +2 -0
  11. package/build/__tests__/unit/licenseGrace.test.d.ts.map +1 -0
  12. package/build/__tests__/unit/licenseGrace.test.js +62 -0
  13. package/build/__tests__/unit/licenseGrace.test.js.map +1 -0
  14. package/build/__tests__/unit/telemetryModes.test.d.ts +2 -0
  15. package/build/__tests__/unit/telemetryModes.test.d.ts.map +1 -0
  16. package/build/__tests__/unit/telemetryModes.test.js +29 -0
  17. package/build/__tests__/unit/telemetryModes.test.js.map +1 -0
  18. package/build/__tests__/unit/usageGate.test.d.ts +2 -0
  19. package/build/__tests__/unit/usageGate.test.d.ts.map +1 -0
  20. package/build/__tests__/unit/usageGate.test.js +78 -0
  21. package/build/__tests__/unit/usageGate.test.js.map +1 -0
  22. package/build/__tests__/unit/usageNotifications.test.d.ts +2 -0
  23. package/build/__tests__/unit/usageNotifications.test.d.ts.map +1 -0
  24. package/build/__tests__/unit/usageNotifications.test.js +27 -0
  25. package/build/__tests__/unit/usageNotifications.test.js.map +1 -0
  26. package/build/__tests__/unit/verifyFlow.test.d.ts +2 -0
  27. package/build/__tests__/unit/verifyFlow.test.d.ts.map +1 -0
  28. package/build/__tests__/unit/verifyFlow.test.js +45 -0
  29. package/build/__tests__/unit/verifyFlow.test.js.map +1 -0
  30. package/build/core/guides.d.ts.map +1 -1
  31. package/build/core/guides.js +54 -10
  32. package/build/core/guides.js.map +1 -1
  33. package/build/core/license.d.ts +8 -0
  34. package/build/core/license.d.ts.map +1 -1
  35. package/build/core/license.js +60 -4
  36. package/build/core/license.js.map +1 -1
  37. package/build/core/register.d.ts.map +1 -1
  38. package/build/core/register.js +19 -3
  39. package/build/core/register.js.map +1 -1
  40. package/build/core/telemetry.d.ts +22 -0
  41. package/build/core/telemetry.d.ts.map +1 -1
  42. package/build/core/telemetry.js +122 -73
  43. package/build/core/telemetry.js.map +1 -1
  44. package/build/index.d.ts.map +1 -1
  45. package/build/index.js +2 -0
  46. package/build/index.js.map +1 -1
  47. package/build/pro/flowpointTools.d.ts +4 -0
  48. package/build/pro/flowpointTools.d.ts.map +1 -0
  49. package/build/pro/flowpointTools.js +266 -0
  50. package/build/pro/flowpointTools.js.map +1 -0
  51. package/build/pro/flowpoints.d.ts +86 -0
  52. package/build/pro/flowpoints.d.ts.map +1 -0
  53. package/build/pro/flowpoints.js +283 -0
  54. package/build/pro/flowpoints.js.map +1 -0
  55. package/build/pro/usageGate.d.ts +10 -0
  56. package/build/pro/usageGate.d.ts.map +1 -0
  57. package/build/pro/usageGate.js +72 -0
  58. package/build/pro/usageGate.js.map +1 -0
  59. package/build/pro/usageNotifications.d.ts +5 -0
  60. package/build/pro/usageNotifications.d.ts.map +1 -0
  61. package/build/pro/usageNotifications.js +88 -0
  62. package/build/pro/usageNotifications.js.map +1 -0
  63. package/build/tools/accountTools.d.ts.map +1 -1
  64. package/build/tools/accountTools.js +12 -1
  65. package/build/tools/accountTools.js.map +1 -1
  66. package/build/tools/metaTools.d.ts.map +1 -1
  67. package/build/tools/metaTools.js +23 -8
  68. package/build/tools/metaTools.js.map +1 -1
  69. package/package.json +1 -1
@@ -0,0 +1,126 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { FLOWPOINT_STORE_CAP, createFlowpointStore, applyDrain, buildDrainExpression, buildClearExpression, parseDrainResult, clearFlowpointStores, getFlowpointStore, } from "../../pro/flowpoints.js";
3
+ function makeEntry(seq, overrides = {}) {
4
+ return { seq, t: 1000 + seq, name: "add-to-cart", step: `step-${seq}`, run: "r1", level: "info", ...overrides };
5
+ }
6
+ describe("applyDrain", () => {
7
+ it("appends everything on first drain and sets cursor/contextId", () => {
8
+ const store = createFlowpointStore();
9
+ const added = applyDrain(store, { contextId: "ctx-a", entries: [makeEntry(1), makeEntry(2)] });
10
+ expect(added).toBe(2);
11
+ expect(store.entries).toHaveLength(2);
12
+ expect(store.cursor).toBe(2);
13
+ expect(store.contextId).toBe("ctx-a");
14
+ });
15
+ it("is idempotent for the same snapshot and incremental for grown snapshots", () => {
16
+ const store = createFlowpointStore();
17
+ applyDrain(store, { contextId: "ctx-a", entries: [makeEntry(1)] });
18
+ expect(applyDrain(store, { contextId: "ctx-a", entries: [makeEntry(1)] })).toBe(0);
19
+ expect(applyDrain(store, { contextId: "ctx-a", entries: [makeEntry(1), makeEntry(2)] })).toBe(1);
20
+ expect(store.entries.map((e) => e.seq)).toEqual([1, 2]);
21
+ });
22
+ it("treats a changed contextId as a reload: all snapshot entries are fresh", () => {
23
+ const store = createFlowpointStore();
24
+ applyDrain(store, { contextId: "ctx-a", entries: [makeEntry(1), makeEntry(2), makeEntry(3)] });
25
+ // reload: seq restarts, MORE entries than the old cursor — heuristics would miss this
26
+ const added = applyDrain(store, {
27
+ contextId: "ctx-b",
28
+ entries: [makeEntry(1), makeEntry(2), makeEntry(3), makeEntry(4)],
29
+ });
30
+ expect(added).toBe(4);
31
+ expect(store.entries).toHaveLength(7);
32
+ expect(store.cursor).toBe(4);
33
+ expect(store.contextId).toBe("ctx-b");
34
+ });
35
+ it("caps the store at FLOWPOINT_STORE_CAP, dropping oldest", () => {
36
+ const store = createFlowpointStore();
37
+ const entries = Array.from({ length: FLOWPOINT_STORE_CAP + 10 }, (_, i) => makeEntry(i + 1));
38
+ applyDrain(store, { contextId: "ctx-a", entries });
39
+ expect(store.entries).toHaveLength(FLOWPOINT_STORE_CAP);
40
+ expect(store.entries[0].seq).toBe(11);
41
+ });
42
+ });
43
+ describe("expression builders", () => {
44
+ it("drain expression is an IIFE reading getFlowpointSnapshot with dual-global fallback", () => {
45
+ const expr = buildDrainExpression();
46
+ expect(expr).toContain("__EXECBRO__");
47
+ expect(expr).toContain("__RN_AI_DEVTOOLS__");
48
+ expect(expr).toContain("getFlowpointSnapshot");
49
+ expect(expr).toContain("__missing");
50
+ expect(expr).toContain("meta not JSON-serializable");
51
+ expect(expr).toContain("__EXECBRO_FLOWPOINT_WARNED__");
52
+ expect(expr).toContain("console.warn");
53
+ expect(expr).toContain("outdated");
54
+ expect(expr).toContain("execbro-sdk@latest");
55
+ });
56
+ it("clear expression calls clearFlowpoints", () => {
57
+ expect(buildClearExpression()).toContain("clearFlowpoints");
58
+ });
59
+ });
60
+ describe("parseDrainResult", () => {
61
+ it("parses a snapshot", () => {
62
+ const parsed = parseDrainResult(JSON.stringify({ contextId: "c", entries: [makeEntry(1)] }));
63
+ expect(parsed).toEqual({ contextId: "c", entries: [makeEntry(1)] });
64
+ });
65
+ it("detects the SDK-missing marker, defaulting to uninitialized when no reason is given", () => {
66
+ expect(parseDrainResult(JSON.stringify({ __missing: true }))).toEqual({
67
+ missing: true,
68
+ reason: "uninitialized",
69
+ });
70
+ });
71
+ it("detects the outdated-SDK marker", () => {
72
+ expect(parseDrainResult(JSON.stringify({ __missing: true, __reason: "outdated" }))).toEqual({
73
+ missing: true,
74
+ reason: "outdated",
75
+ });
76
+ });
77
+ it("returns null on garbage", () => {
78
+ expect(parseDrainResult("not json")).toBeNull();
79
+ });
80
+ });
81
+ describe("clearFlowpointStores", () => {
82
+ it("device-scoped clear touches only that device's store", () => {
83
+ const deviceA = getFlowpointStore("clear-test-device-a");
84
+ const deviceB = getFlowpointStore("clear-test-device-b");
85
+ applyDrain(deviceA, { contextId: "ctx-a", entries: [makeEntry(1), makeEntry(2)] });
86
+ applyDrain(deviceB, { contextId: "ctx-b", entries: [makeEntry(1), makeEntry(2), makeEntry(3)] });
87
+ const removed = clearFlowpointStores(undefined, "clear-test-device-a");
88
+ expect(removed).toBe(2);
89
+ expect(deviceA.entries).toHaveLength(0);
90
+ expect(deviceB.entries).toHaveLength(3);
91
+ // cursor/contextId retained so cleared entries never re-drain
92
+ expect(deviceA.cursor).toBe(2);
93
+ expect(deviceA.contextId).toBe("ctx-a");
94
+ });
95
+ it("undefined deviceName clears all stores", () => {
96
+ // Reset any state left by other stores/tests sharing the module-level map.
97
+ clearFlowpointStores(undefined, undefined);
98
+ const deviceC = getFlowpointStore("clear-test-device-c");
99
+ const deviceD = getFlowpointStore("clear-test-device-d");
100
+ applyDrain(deviceC, { contextId: "ctx-c", entries: [makeEntry(1)] });
101
+ applyDrain(deviceD, { contextId: "ctx-d", entries: [makeEntry(1), makeEntry(2)] });
102
+ const removed = clearFlowpointStores(undefined, undefined);
103
+ expect(removed).toBe(3);
104
+ expect(deviceC.entries).toHaveLength(0);
105
+ expect(deviceD.entries).toHaveLength(0);
106
+ });
107
+ it("combines name filter with device scoping", () => {
108
+ const deviceE = getFlowpointStore("clear-test-device-e");
109
+ applyDrain(deviceE, {
110
+ contextId: "ctx-e",
111
+ entries: [
112
+ makeEntry(1, { name: "checkout" }),
113
+ makeEntry(2, { name: "onboarding" }),
114
+ makeEntry(3, { name: "checkout" }),
115
+ ],
116
+ });
117
+ const deviceF = getFlowpointStore("clear-test-device-f");
118
+ applyDrain(deviceF, { contextId: "ctx-f", entries: [makeEntry(1, { name: "checkout" })] });
119
+ const removed = clearFlowpointStores("checkout", "clear-test-device-e");
120
+ expect(removed).toBe(2);
121
+ expect(deviceE.entries.map((e) => e.name)).toEqual(["onboarding"]);
122
+ // other device's "checkout" entries untouched since deviceName scoped the clear
123
+ expect(deviceF.entries).toHaveLength(1);
124
+ });
125
+ });
126
+ //# sourceMappingURL=flowpoints.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowpoints.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/flowpoints.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=flowpointsQuery.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowpointsQuery.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/flowpointsQuery.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,91 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { formatMeta, matchesPoint, resolveLastRuns, filterFlowpoints, formatFlowpoints, } from "../../pro/flowpoints.js";
3
+ function e(seq, overrides = {}) {
4
+ return { seq, t: 1000 + seq, name: "add-to-cart", step: `s${seq}`, run: "r1", level: "info", ...overrides };
5
+ }
6
+ describe("formatMeta", () => {
7
+ it("passes strings through and stringifies objects", () => {
8
+ expect(formatMeta("start")).toBe("start");
9
+ expect(formatMeta({ removed: 3 })).toBe('{"removed":3}');
10
+ expect(formatMeta(undefined)).toBe("");
11
+ });
12
+ });
13
+ describe("matchesPoint", () => {
14
+ it("matches on step equality, level, and case-insensitive metaIncludes", () => {
15
+ const entry = e(1, { step: "failed", level: "error", meta: { reason: "Timeout" } });
16
+ expect(matchesPoint(entry, { step: "failed" })).toBe(true);
17
+ expect(matchesPoint(entry, { step: "other" })).toBe(false);
18
+ expect(matchesPoint(entry, { level: "error" })).toBe(true);
19
+ expect(matchesPoint(entry, { metaIncludes: "timeout" })).toBe(true);
20
+ expect(matchesPoint(entry, { step: "failed", level: "info" })).toBe(false);
21
+ });
22
+ });
23
+ describe("resolveLastRuns", () => {
24
+ it("returns the run of the chronologically last entry per flow", () => {
25
+ const entries = [
26
+ e(1, { run: "aaaa" }),
27
+ e(2, { run: "aaaa" }),
28
+ e(3, { run: "bbbb" }),
29
+ e(4, { name: "other", run: "cccc" }),
30
+ ];
31
+ const last = resolveLastRuns(entries);
32
+ expect(last.get("add-to-cart")).toBe("bbbb");
33
+ expect(last.get("other")).toBe("cccc");
34
+ });
35
+ });
36
+ describe("filterFlowpoints", () => {
37
+ const entries = [
38
+ e(1, { run: "aaaa", step: "start" }),
39
+ e(2, { run: "aaaa", step: "failed", level: "error", meta: { reason: "timeout" } }),
40
+ e(3, { run: "bbbb", step: "start" }),
41
+ e(4, { run: "bbbb", step: "done" }),
42
+ e(5, { name: "checkout", run: "cccc", step: "start" }),
43
+ ];
44
+ it("filters by name, step, and level", () => {
45
+ expect(filterFlowpoints(entries, { name: "checkout" })).toHaveLength(1);
46
+ expect(filterFlowpoints(entries, { step: "start" })).toHaveLength(3);
47
+ expect(filterFlowpoints(entries, { level: "error" })).toHaveLength(1);
48
+ });
49
+ it("filters by metaIncludes and since", () => {
50
+ expect(filterFlowpoints(entries, { metaIncludes: "TIMEOUT" })).toHaveLength(1);
51
+ expect(filterFlowpoints(entries, { since: 1003 })).toHaveLength(2);
52
+ });
53
+ it("resolves run: 'last' per flow and explicit run ids", () => {
54
+ const last = filterFlowpoints(entries, { run: "last" });
55
+ expect(last.map((x) => x.seq)).toEqual([3, 4, 5]);
56
+ expect(filterFlowpoints(entries, { run: "aaaa" })).toHaveLength(2);
57
+ });
58
+ it("applies limit keeping the newest entries", () => {
59
+ expect(filterFlowpoints(entries, { limit: 2 }).map((x) => x.seq)).toEqual([4, 5]);
60
+ });
61
+ });
62
+ describe("formatFlowpoints", () => {
63
+ it("formats a single-run flow with deltas, span, level prefix, and meta", () => {
64
+ const out = formatFlowpoints([
65
+ e(1, { step: "start", t: 1000 }),
66
+ e(2, { step: "cleared", t: 1006, meta: { removed: 3 } }),
67
+ e(3, { step: "failed", t: 1312, level: "error", meta: { reason: "timeout" } }),
68
+ ]);
69
+ expect(out).toContain('Flow "add-to-cart" run r1 — 3 points (312ms span):');
70
+ expect(out).toContain("+0ms");
71
+ expect(out).toContain("+6ms");
72
+ expect(out).toContain('cleared {"removed":3}');
73
+ expect(out).toContain("[error] failed");
74
+ });
75
+ it("groups multiple runs and labels the latest", () => {
76
+ const out = formatFlowpoints([e(1, { run: "aaaa", t: 1000 }), e(2, { run: "bbbb", t: 2000 })]);
77
+ expect(out).toContain('Flow "add-to-cart" — 2 runs');
78
+ expect(out).toContain("run aaaa — 1 points");
79
+ expect(out).toContain("run bbbb (latest) — 1 points");
80
+ });
81
+ it("labels (latest) by last activity, not first appearance, under interleaved runs", () => {
82
+ const out = formatFlowpoints([
83
+ e(1, { run: "aaaa", t: 1000 }),
84
+ e(2, { run: "bbbb", t: 1500 }),
85
+ e(3, { run: "aaaa", t: 2000 }),
86
+ ]);
87
+ expect(out).toContain("run aaaa (latest)");
88
+ expect(out).not.toContain("bbbb (latest)");
89
+ });
90
+ });
91
+ //# sourceMappingURL=flowpointsQuery.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flowpointsQuery.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/flowpointsQuery.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=licenseGrace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"licenseGrace.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/licenseGrace.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,62 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { computeOfflineUsage, GRACE_WINDOW_MS } from "../../core/license.js";
3
+ const NOW = 1_000_000_000_000;
4
+ // Default monthKey matches NOW's UTC month so existing tests (which aren't
5
+ // exercising month-rollover behavior) don't unintentionally cross a month
6
+ // boundary between the cached verdict and the "now" they're evaluated at.
7
+ function currentMonthKeyFor(ts) {
8
+ const d = new Date(ts);
9
+ return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}`;
10
+ }
11
+ function base(over = {}) {
12
+ return {
13
+ used: 100,
14
+ limit: 600,
15
+ monthKey: currentMonthKeyFor(NOW),
16
+ creditsRemaining: null,
17
+ canUse: true,
18
+ capActive: true,
19
+ ...over,
20
+ };
21
+ }
22
+ describe("computeOfflineUsage (fail-closed after grace)", () => {
23
+ it("null cache stays null (fail-open when never seen)", () => {
24
+ expect(computeOfflineUsage(null, NOW)).toBeNull();
25
+ });
26
+ it("within grace: trusts the cached verdict, including canUse:false", () => {
27
+ const cached = base({ used: 700, canUse: false, verdictFreshUntil: new Date(NOW + 1000).toISOString() });
28
+ const out = computeOfflineUsage(cached, NOW);
29
+ expect(out.canUse).toBe(false);
30
+ });
31
+ it("past grace + last-known under cap: allow", () => {
32
+ const cached = base({ used: 100, canUse: true, verdictFreshUntil: new Date(NOW - 1000).toISOString() });
33
+ expect(computeOfflineUsage(cached, NOW).canUse).toBe(true);
34
+ });
35
+ it("past grace + last-known over cap: block", () => {
36
+ const cached = base({ used: 650, canUse: false, verdictFreshUntil: new Date(NOW - 1000).toISOString() });
37
+ expect(computeOfflineUsage(cached, NOW).canUse).toBe(false);
38
+ });
39
+ it("past grace but capActive:false (deferred user): allow", () => {
40
+ const cached = base({
41
+ used: 900,
42
+ capActive: false,
43
+ canUse: true,
44
+ verdictFreshUntil: new Date(NOW - 1000).toISOString(),
45
+ });
46
+ expect(computeOfflineUsage(cached, NOW).canUse).toBe(true);
47
+ });
48
+ it("GRACE_WINDOW_MS is 72h", () => {
49
+ expect(GRACE_WINDOW_MS).toBe(72 * 60 * 60 * 1000);
50
+ });
51
+ it("past grace + over cap but month has rolled over: allow (monthly counter reset)", () => {
52
+ const cached = base({
53
+ used: 650,
54
+ canUse: false,
55
+ monthKey: "2026-05",
56
+ verdictFreshUntil: new Date(NOW - 1000).toISOString(),
57
+ });
58
+ const laterMonth = Date.UTC(2026, 5, 15); // June 2026, well past May
59
+ expect(computeOfflineUsage(cached, laterMonth).canUse).toBe(true);
60
+ });
61
+ });
62
+ //# sourceMappingURL=licenseGrace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"licenseGrace.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/licenseGrace.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=telemetryModes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetryModes.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/telemetryModes.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { resolveTelemetryModes } from "../../core/telemetry.js";
3
+ describe("resolveTelemetryModes", () => {
4
+ it("normal (configured, no opt-out, not dev): both on", () => {
5
+ const modes = resolveTelemetryModes({ envOptOut: false, devMode: false, endpointConfigured: true });
6
+ expect(modes).toEqual({ telemetryEnabled: true, meteringEnabled: true });
7
+ });
8
+ it("EXECBRO_TELEMETRY opt-out: analytics off, metering stays on", () => {
9
+ const modes = resolveTelemetryModes({ envOptOut: true, devMode: false, endpointConfigured: true });
10
+ expect(modes).toEqual({ telemetryEnabled: false, meteringEnabled: true });
11
+ });
12
+ it("dev mode: both off, even if opt-out is not set", () => {
13
+ const modes = resolveTelemetryModes({ envOptOut: false, devMode: true, endpointConfigured: true });
14
+ expect(modes).toEqual({ telemetryEnabled: false, meteringEnabled: false });
15
+ });
16
+ it("dev mode wins even when combined with opt-out", () => {
17
+ const modes = resolveTelemetryModes({ envOptOut: true, devMode: true, endpointConfigured: true });
18
+ expect(modes).toEqual({ telemetryEnabled: false, meteringEnabled: false });
19
+ });
20
+ it("unconfigured endpoint (placeholder): both off", () => {
21
+ const modes = resolveTelemetryModes({ envOptOut: false, devMode: false, endpointConfigured: false });
22
+ expect(modes).toEqual({ telemetryEnabled: false, meteringEnabled: false });
23
+ });
24
+ it("unconfigured endpoint wins even when combined with opt-out", () => {
25
+ const modes = resolveTelemetryModes({ envOptOut: true, devMode: false, endpointConfigured: false });
26
+ expect(modes).toEqual({ telemetryEnabled: false, meteringEnabled: false });
27
+ });
28
+ });
29
+ //# sourceMappingURL=telemetryModes.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetryModes.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/telemetryModes.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QACzD,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QACpG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACnE,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACtD,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QACnG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QAClG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACrD,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC;QACrG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QAClE,MAAM,KAAK,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC;QACpG,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=usageGate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usageGate.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/usageGate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,78 @@
1
+ import { describe, it, expect, beforeEach } from "@jest/globals";
2
+ import { freezeSessionVerdict, refreezeSessionVerdict, isToolBlocked, usageWarningLine, resetGateForTests, } from "../../pro/usageGate.js";
3
+ function usage(over = {}) {
4
+ return {
5
+ used: 100,
6
+ limit: 600,
7
+ monthKey: "2026-08",
8
+ creditsRemaining: null,
9
+ canUse: true,
10
+ capActive: true,
11
+ warnThreshold: 0.8,
12
+ resetsAt: "2026-09-01T00:00:00Z",
13
+ ...over,
14
+ };
15
+ }
16
+ beforeEach(() => resetGateForTests());
17
+ describe("session verdict freeze", () => {
18
+ it("under cap: nothing blocked", () => {
19
+ freezeSessionVerdict(usage({ used: 100, canUse: true }));
20
+ expect(isToolBlocked("tap").blocked).toBe(false);
21
+ });
22
+ it("over cap: non-exempt tools blocked with a relay message", () => {
23
+ freezeSessionVerdict(usage({ used: 600, canUse: false }));
24
+ const r = isToolBlocked("tap");
25
+ expect(r.blocked).toBe(true);
26
+ expect(r.message).toMatch(/limit reached|monthly limit/i);
27
+ expect(r.message).toMatch(/upgrade/);
28
+ });
29
+ it("exempt tools never blocked even over cap", () => {
30
+ freezeSessionVerdict(usage({ used: 600, canUse: false }));
31
+ expect(isToolBlocked("get_license_status").blocked).toBe(false);
32
+ expect(isToolBlocked("activate_license").blocked).toBe(false);
33
+ });
34
+ it("deferred user (capActive:false) never blocked", () => {
35
+ freezeSessionVerdict(usage({ used: 900, capActive: false, canUse: true }));
36
+ expect(isToolBlocked("tap").blocked).toBe(false);
37
+ });
38
+ it("before freeze (null verdict): allow", () => {
39
+ expect(isToolBlocked("tap").blocked).toBe(false);
40
+ });
41
+ it("freeze is idempotent — stays blocked", () => {
42
+ freezeSessionVerdict(usage({ used: 600, canUse: false }));
43
+ expect(isToolBlocked("tap").blocked).toBe(true);
44
+ freezeSessionVerdict(usage({ used: 100, canUse: true }));
45
+ expect(isToolBlocked("tap").blocked).toBe(true);
46
+ });
47
+ it("freeze is idempotent — stays unblocked", () => {
48
+ freezeSessionVerdict(usage({ used: 100, canUse: true }));
49
+ expect(isToolBlocked("tap").blocked).toBe(false);
50
+ freezeSessionVerdict(usage({ used: 600, canUse: false }));
51
+ expect(isToolBlocked("tap").blocked).toBe(false);
52
+ });
53
+ it("refreezeSessionVerdict lifts a frozen block after mid-session upgrade", () => {
54
+ freezeSessionVerdict(usage({ used: 600, canUse: false }));
55
+ expect(isToolBlocked("tap").blocked).toBe(true);
56
+ refreezeSessionVerdict(usage({ used: 0, canUse: true, limit: null }));
57
+ expect(isToolBlocked("tap").blocked).toBe(false);
58
+ });
59
+ });
60
+ describe("warning line", () => {
61
+ it("below 80%: no warning", () => {
62
+ expect(usageWarningLine(usage({ used: 100 }))).toBeNull();
63
+ });
64
+ it("at/above 80%: warning with remaining + reset date", () => {
65
+ const line = usageWarningLine(usage({ used: 500 }));
66
+ expect(line).toMatch(/100/); // 600-500 remaining
67
+ expect(line).toMatch(/ExecBro/);
68
+ });
69
+ it("deferred or uncapped: no warning", () => {
70
+ expect(usageWarningLine(usage({ capActive: false }))).toBeNull();
71
+ expect(usageWarningLine(usage({ limit: null }))).toBeNull();
72
+ });
73
+ it("invalid resetsAt falls back to 'next month'", () => {
74
+ const line = usageWarningLine(usage({ used: 500, resetsAt: "not-a-date" }));
75
+ expect(line).toMatch(/next month/);
76
+ });
77
+ });
78
+ //# sourceMappingURL=usageGate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usageGate.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/usageGate.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=usageNotifications.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usageNotifications.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/usageNotifications.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ import { describe, test, expect } from "@jest/globals";
2
+ import { nextThreshold } from "../../pro/usageNotifications.js";
3
+ function usage(over = {}) {
4
+ return {
5
+ used: 0,
6
+ limit: 600,
7
+ monthKey: "2026-08",
8
+ creditsRemaining: null,
9
+ canUse: true,
10
+ capActive: true,
11
+ warnThreshold: 0.8,
12
+ ...over,
13
+ };
14
+ }
15
+ describe("nextThreshold", () => {
16
+ test("below 80% → null", () => expect(nextThreshold(usage({ used: 100 }))).toBeNull());
17
+ test("80–99% → 80", () => expect(nextThreshold(usage({ used: 500 }))).toBe(80));
18
+ test("100%+ → 100", () => expect(nextThreshold(usage({ used: 600 }))).toBe(100));
19
+ test("exactly 80% boundary → 80", () => expect(nextThreshold(usage({ limit: 600, used: 480 }))).toBe(80));
20
+ test("just below 80% boundary → null", () => expect(nextThreshold(usage({ limit: 600, used: 479 }))).toBeNull());
21
+ test("exactly 100% boundary → 100", () => expect(nextThreshold(usage({ limit: 600, used: 600 }))).toBe(100));
22
+ test("deferred/uncapped → null", () => {
23
+ expect(nextThreshold(usage({ capActive: false, used: 600 }))).toBeNull();
24
+ expect(nextThreshold(usage({ limit: null, used: 9999 }))).toBeNull();
25
+ });
26
+ });
27
+ //# sourceMappingURL=usageNotifications.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usageNotifications.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/usageNotifications.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGhE,SAAS,KAAK,CAAC,OAA2B,EAAE;IACxC,OAAO;QACH,IAAI,EAAE,CAAC;QACP,KAAK,EAAE,GAAG;QACV,QAAQ,EAAE,SAAS;QACnB,gBAAgB,EAAE,IAAI;QACtB,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,IAAI;QACf,aAAa,EAAE,GAAG;QAClB,GAAG,IAAI;KACV,CAAC;AACN,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC3B,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACvF,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,IAAI,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACjF,IAAI,CAAC,2BAA2B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1G,IAAI,CAAC,gCAAgC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjH,IAAI,CAAC,6BAA6B,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7G,IAAI,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACzE,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACzE,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=verifyFlow.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifyFlow.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/verifyFlow.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import { verifyFlow } from "../../pro/flowpoints.js";
3
+ function e(seq, step, overrides = {}) {
4
+ return { seq, t: 1000 + seq * 10, name: "add-to-cart", step, run: "r1", level: "info", ...overrides };
5
+ }
6
+ const happy = [e(1, "start"), e(2, "cleared"), e(3, "item-added"), e(4, "done")];
7
+ describe("verifyFlow", () => {
8
+ it("passes when expected steps appear in order (subsequence: extras allowed)", () => {
9
+ const result = verifyFlow("add-to-cart", "r1", happy, ["start", "item-added", "done"], false);
10
+ expect(result.pass).toBe(true);
11
+ expect(result.text).toContain('PASS — flow "add-to-cart" run r1:');
12
+ expect(result.text).toContain("✓ start (+0ms)");
13
+ expect(result.text).toContain("✓ done (+30ms)");
14
+ });
15
+ it("fails and diffs a missing step", () => {
16
+ const result = verifyFlow("add-to-cart", "r1", [e(1, "start"), e(2, "cleared")], ["start", "item-added"], false);
17
+ expect(result.pass).toBe(false);
18
+ expect(result.text).toContain("✗ item-added — not seen");
19
+ });
20
+ it("fails on out-of-order steps", () => {
21
+ const result = verifyFlow("add-to-cart", "r1", happy, ["done", "start"], false);
22
+ expect(result.pass).toBe(false);
23
+ });
24
+ it("fails on an unexpected error point, with a ! line", () => {
25
+ const entries = [e(1, "start"), e(2, "failed", { level: "error", meta: { reason: "timeout" } })];
26
+ const result = verifyFlow("add-to-cart", "r1", entries, ["start"], false);
27
+ expect(result.pass).toBe(false);
28
+ expect(result.text).toContain('! failed [error] (+10ms) {"reason":"timeout"} — unexpected error point');
29
+ });
30
+ it("allowErrors tolerates unexpected error points", () => {
31
+ const entries = [e(1, "start"), e(2, "failed", { level: "error" })];
32
+ expect(verifyFlow("add-to-cart", "r1", entries, ["start"], true).pass).toBe(true);
33
+ });
34
+ it("an explicitly expected error step passes", () => {
35
+ const entries = [e(1, "start"), e(2, "failed", { level: "error" })];
36
+ const result = verifyFlow("add-to-cart", "r1", entries, ["start", { step: "failed", level: "error" }], false);
37
+ expect(result.pass).toBe(true);
38
+ });
39
+ it("object matchers can constrain meta", () => {
40
+ const entries = [e(1, "done", { meta: { count: 5 } })];
41
+ expect(verifyFlow("f", "r1", entries, [{ step: "done", metaIncludes: '"count":5' }], false).pass).toBe(true);
42
+ expect(verifyFlow("f", "r1", entries, [{ step: "done", metaIncludes: 'count":9' }], false).pass).toBe(false);
43
+ });
44
+ });
45
+ //# sourceMappingURL=verifyFlow.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifyFlow.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/verifyFlow.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"guides.d.ts","sourceRoot":"","sources":["../../src/core/guides.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,KAAK;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB;AAiWD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAchB,CAAC;AAEb,wBAAgB,gBAAgB,IAAI,MAAM,CAWzC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI5D;AAED,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C"}
1
+ {"version":3,"file":"guides.d.ts","sourceRoot":"","sources":["../../src/core/guides.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,KAAK;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACnB;AA4YD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,MAehB,CAAC;AAEb,wBAAgB,gBAAgB,IAAI,MAAM,CAWzC;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI5D;AAED,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAE7C"}