@cleartrip/frontguard 0.1.6 → 0.1.8

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/index.d.ts CHANGED
@@ -108,12 +108,24 @@ interface FrontGuardConfig {
108
108
  };
109
109
  llm: {
110
110
  enabled: boolean;
111
- provider: 'openai' | 'anthropic';
111
+ provider: 'openai' | 'anthropic' | 'ollama';
112
112
  model: string;
113
- /** Environment variable name holding the API secret */
113
+ /** Environment variable name holding the API secret (OpenAI / Anthropic only) */
114
114
  apiKeyEnv: string;
115
115
  maxDiffChars: number;
116
116
  timeoutMs: number;
117
+ /**
118
+ * Ollama HTTP base URL — no API key (local or LAN inference).
119
+ * In Docker-based CI reaching a host Ollama: e.g. http://host.docker.internal:11434
120
+ */
121
+ ollamaUrl: string;
122
+ /**
123
+ * Ask the model for per-finding fix hints (Ollama only today). Disabled by default due to latency.
124
+ */
125
+ perFindingFixes: boolean;
126
+ maxFixSuggestions: number;
127
+ /** Max characters of file content sent as context per finding */
128
+ maxFileContextChars: number;
117
129
  };
118
130
  };
119
131
  }
package/dist/index.js CHANGED
@@ -123,7 +123,11 @@ var defaultConfig = {
123
123
  model: "gpt-4o-mini",
124
124
  apiKeyEnv: "OPENAI_API_KEY",
125
125
  maxDiffChars: 48e3,
126
- timeoutMs: 6e4
126
+ timeoutMs: 6e4,
127
+ ollamaUrl: "http://127.0.0.1:11434",
128
+ perFindingFixes: false,
129
+ maxFixSuggestions: 12,
130
+ maxFileContextChars: 24e3
127
131
  }
128
132
  }
129
133
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../node_modules/defu/dist/defu.mjs","../src/config/defaults.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["function isPlainObject(value) {\n if (value === null || typeof value !== \"object\") {\n return false;\n }\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {\n return false;\n }\n if (Symbol.iterator in value) {\n return false;\n }\n if (Symbol.toStringTag in value) {\n return Object.prototype.toString.call(value) === \"[object Module]\";\n }\n return true;\n}\n\nfunction _defu(baseObject, defaults, namespace = \".\", merger) {\n if (!isPlainObject(defaults)) {\n return _defu(baseObject, {}, namespace, merger);\n }\n const object = { ...defaults };\n for (const key of Object.keys(baseObject)) {\n if (key === \"__proto__\" || key === \"constructor\") {\n continue;\n }\n const value = baseObject[key];\n if (value === null || value === void 0) {\n continue;\n }\n if (merger && merger(object, key, value, namespace)) {\n continue;\n }\n if (Array.isArray(value) && Array.isArray(object[key])) {\n object[key] = [...value, ...object[key]];\n } else if (isPlainObject(value) && isPlainObject(object[key])) {\n object[key] = _defu(\n value,\n object[key],\n (namespace ? `${namespace}.` : \"\") + key.toString(),\n merger\n );\n } else {\n object[key] = value;\n }\n }\n return object;\n}\nfunction createDefu(merger) {\n return (...arguments_) => (\n // eslint-disable-next-line unicorn/no-array-reduce\n arguments_.reduce((p, c) => _defu(p, c, \"\", merger), {})\n );\n}\nconst defu = createDefu();\nconst defuFn = createDefu((object, key, currentValue) => {\n if (object[key] !== void 0 && typeof currentValue === \"function\") {\n object[key] = currentValue(object[key]);\n return true;\n }\n});\nconst defuArrayFn = createDefu((object, key, currentValue) => {\n if (Array.isArray(object[key]) && typeof currentValue === \"function\") {\n object[key] = currentValue(object[key]);\n return true;\n }\n});\n\nexport { createDefu, defu as default, defu, defuArrayFn, defuFn };\n","import type { FrontGuardConfig } from './schema.js'\n\nexport const defaultConfig: FrontGuardConfig = {\n mode: 'warn',\n rules: {},\n checks: {\n eslint: { enabled: true, glob: '**/*.{js,cjs,mjs,jsx,ts,tsx}' },\n prettier: {\n enabled: true,\n glob: '**/*.{js,cjs,mjs,jsx,ts,tsx,json,md,css,scss,yml,yaml}',\n },\n typescript: { enabled: true },\n secrets: { enabled: true },\n prHygiene: {\n enabled: true,\n minBodyLength: 80,\n requireSections: false,\n sectionHints: ['what', 'why', 'test', 'how to test', 'screenshot'],\n requireAiDisclosureSection: true,\n gateWhenAiDisclosureAmbiguous: 'warn',\n },\n aiAssistedReview: {\n enabled: true,\n gate: 'warn',\n escalate: {\n secretFindingsToBlock: true,\n tsAnyDeltaToBlock: true,\n },\n },\n prSize: { warnLines: 400, softBlockLines: 800 },\n\n tsAnyDelta: {\n enabled: true,\n gate: 'warn',\n baseRef: 'main',\n maxAdded: 0,\n },\n cycles: {\n enabled: false,\n gate: 'warn',\n entries: ['src'],\n extraArgs: [],\n },\n deadCode: {\n enabled: false,\n gate: 'info',\n extraArgs: [],\n maxReportLines: 80,\n },\n bundle: {\n enabled: false,\n gate: 'warn',\n runBuild: true,\n buildCommand: 'npm run build',\n measureGlobs: ['dist/**/*', 'build/static/**/*', '.next/static/**/*'],\n baselinePath: '.frontguard/bundle-baseline.json',\n maxDeltaBytes: null,\n maxTotalBytes: null,\n },\n cwv: {\n enabled: true,\n gate: 'warn',\n scanGlobs: ['app/**/*.{tsx,jsx}', 'pages/**/*.{tsx,jsx}', 'src/**/*.{tsx,jsx}'],\n maxFileBytes: 400_000,\n },\n llm: {\n enabled: false,\n provider: 'openai',\n model: 'gpt-4o-mini',\n apiKeyEnv: 'OPENAI_API_KEY',\n maxDiffChars: 48_000,\n timeoutMs: 60_000,\n },\n },\n}\n","import defu from 'defu'\nimport type { FrontGuardConfig } from './config/schema.js'\nimport { defaultConfig } from './config/defaults.js'\n\nexport type {\n FrontGuardConfig,\n GuardMode,\n CheckGate,\n RulesMap,\n CustomRuleDefinition,\n RuleFileContext,\n} from './config/schema.js'\nexport { defaultConfig } from './config/defaults.js'\n\n/**\n * Define repo-level config; shallow-merged over built-in defaults.\n * Org packages can be referenced via `extends` in config file.\n */\nexport function defineConfig(partial: Partial<FrontGuardConfig>): FrontGuardConfig {\n return defu(partial, defaultConfig) as FrontGuardConfig\n}\n"]}
1
+ {"version":3,"sources":["../node_modules/defu/dist/defu.mjs","../src/config/defaults.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["function isPlainObject(value) {\n if (value === null || typeof value !== \"object\") {\n return false;\n }\n const prototype = Object.getPrototypeOf(value);\n if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) {\n return false;\n }\n if (Symbol.iterator in value) {\n return false;\n }\n if (Symbol.toStringTag in value) {\n return Object.prototype.toString.call(value) === \"[object Module]\";\n }\n return true;\n}\n\nfunction _defu(baseObject, defaults, namespace = \".\", merger) {\n if (!isPlainObject(defaults)) {\n return _defu(baseObject, {}, namespace, merger);\n }\n const object = { ...defaults };\n for (const key of Object.keys(baseObject)) {\n if (key === \"__proto__\" || key === \"constructor\") {\n continue;\n }\n const value = baseObject[key];\n if (value === null || value === void 0) {\n continue;\n }\n if (merger && merger(object, key, value, namespace)) {\n continue;\n }\n if (Array.isArray(value) && Array.isArray(object[key])) {\n object[key] = [...value, ...object[key]];\n } else if (isPlainObject(value) && isPlainObject(object[key])) {\n object[key] = _defu(\n value,\n object[key],\n (namespace ? `${namespace}.` : \"\") + key.toString(),\n merger\n );\n } else {\n object[key] = value;\n }\n }\n return object;\n}\nfunction createDefu(merger) {\n return (...arguments_) => (\n // eslint-disable-next-line unicorn/no-array-reduce\n arguments_.reduce((p, c) => _defu(p, c, \"\", merger), {})\n );\n}\nconst defu = createDefu();\nconst defuFn = createDefu((object, key, currentValue) => {\n if (object[key] !== void 0 && typeof currentValue === \"function\") {\n object[key] = currentValue(object[key]);\n return true;\n }\n});\nconst defuArrayFn = createDefu((object, key, currentValue) => {\n if (Array.isArray(object[key]) && typeof currentValue === \"function\") {\n object[key] = currentValue(object[key]);\n return true;\n }\n});\n\nexport { createDefu, defu as default, defu, defuArrayFn, defuFn };\n","import type { FrontGuardConfig } from './schema.js'\n\nexport const defaultConfig: FrontGuardConfig = {\n mode: 'warn',\n rules: {},\n checks: {\n eslint: { enabled: true, glob: '**/*.{js,cjs,mjs,jsx,ts,tsx}' },\n prettier: {\n enabled: true,\n glob: '**/*.{js,cjs,mjs,jsx,ts,tsx,json,md,css,scss,yml,yaml}',\n },\n typescript: { enabled: true },\n secrets: { enabled: true },\n prHygiene: {\n enabled: true,\n minBodyLength: 80,\n requireSections: false,\n sectionHints: ['what', 'why', 'test', 'how to test', 'screenshot'],\n requireAiDisclosureSection: true,\n gateWhenAiDisclosureAmbiguous: 'warn',\n },\n aiAssistedReview: {\n enabled: true,\n gate: 'warn',\n escalate: {\n secretFindingsToBlock: true,\n tsAnyDeltaToBlock: true,\n },\n },\n prSize: { warnLines: 400, softBlockLines: 800 },\n\n tsAnyDelta: {\n enabled: true,\n gate: 'warn',\n baseRef: 'main',\n maxAdded: 0,\n },\n cycles: {\n enabled: false,\n gate: 'warn',\n entries: ['src'],\n extraArgs: [],\n },\n deadCode: {\n enabled: false,\n gate: 'info',\n extraArgs: [],\n maxReportLines: 80,\n },\n bundle: {\n enabled: false,\n gate: 'warn',\n runBuild: true,\n buildCommand: 'npm run build',\n measureGlobs: ['dist/**/*', 'build/static/**/*', '.next/static/**/*'],\n baselinePath: '.frontguard/bundle-baseline.json',\n maxDeltaBytes: null,\n maxTotalBytes: null,\n },\n cwv: {\n enabled: true,\n gate: 'warn',\n scanGlobs: ['app/**/*.{tsx,jsx}', 'pages/**/*.{tsx,jsx}', 'src/**/*.{tsx,jsx}'],\n maxFileBytes: 400_000,\n },\n llm: {\n enabled: false,\n provider: 'openai',\n model: 'gpt-4o-mini',\n apiKeyEnv: 'OPENAI_API_KEY',\n maxDiffChars: 48_000,\n timeoutMs: 60_000,\n ollamaUrl: 'http://127.0.0.1:11434',\n perFindingFixes: false,\n maxFixSuggestions: 12,\n maxFileContextChars: 24_000,\n },\n },\n}\n","import defu from 'defu'\nimport type { FrontGuardConfig } from './config/schema.js'\nimport { defaultConfig } from './config/defaults.js'\n\nexport type {\n FrontGuardConfig,\n GuardMode,\n CheckGate,\n RulesMap,\n CustomRuleDefinition,\n RuleFileContext,\n} from './config/schema.js'\nexport { defaultConfig } from './config/defaults.js'\n\n/**\n * Define repo-level config; shallow-merged over built-in defaults.\n * Org packages can be referenced via `extends` in config file.\n */\nexport function defineConfig(partial: Partial<FrontGuardConfig>): FrontGuardConfig {\n return defu(partial, defaultConfig) as FrontGuardConfig\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleartrip/frontguard",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Org-wide frontend PR guardrails: linting, hygiene, any-delta, cycles, dead code, bundle/CWV hints, custom rules, optional LLM brief",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,11 +7,43 @@
7
7
  # 2. Add @cleartrip/frontguard as a devDependency and commit this as bitbucket-pipelines.yml
8
8
  # at the repository root, or merge the pull-requests section into your existing pipelines file.
9
9
  #
10
- # URL shape matches your example; workspace/repo comes from BITBUCKET_REPO_FULL_NAME so the same
11
- # file works for cleartrip/ct-pwa2.0 and every other repo.
10
+ # API hostname MUST be api.bitbucket.org (Bitbucket Cloud). Do not substitute DOCKER_*,
11
+ # NPM registry, or other host variables here — wrong host → 401/403 and requests never hit Bitbucket.
12
+ # Self-hosted Bitbucket Server uses a different REST path; this template is Cloud-only.
13
+ #
14
+ # BITBUCKET_REPO_FULL_NAME = workspace/slug (e.g. myworkspace/myrepo). BITBUCKET_PR_ID is numeric.
12
15
  #
13
16
  # Duplicate comments: each run adds a new comment. To upsert one comment, add a script that
14
17
  # GETs /pullrequests/{id}/comments and DELETEs or updates the previous FrontGuard comment.
18
+ #
19
+ # ⚠️ CRITICAL — What to POST as the PR comment:
20
+ # Use ONLY `frontguard-pr-comment.md` (short, plain summary + pipeline URL for the HTML artifact).
21
+ # NEVER POST `frontguard-report.md` or the raw `yarn frontguard run` stdout — that is the full
22
+ # markdown report and will look like a wall of `##` / `|` if something goes wrong, and is
23
+ # unreadable in a PR thread. The interactive HTML is `frontguard-report.html` (Artifacts).
24
+ #
25
+ # API: body MUST be `{"content":{"raw":"<text>"}}` only. Do not add `content.markup` — Bitbucket
26
+ # returns 400 "extra keys not allowed". Markdown in `raw` is still rendered when possible.
27
+ #
28
+ # PR comments: Bitbucket Markdown is limited (no raw HTML). To share the rich HTML report:
29
+ #
30
+ # A) Default (no extra setup): the comment links to **this pipeline’s results page**. That URL works for
31
+ # anyone with repo access who is **logged into Bitbucket**. They click **Artifacts** →
32
+ # **frontguard-report.html** → download → open the file locally (full UI: accordions, colors, findings).
33
+ # Bitbucket does **not** offer a stable public “open HTML inline in one click” URL for step artifacts.
34
+ #
35
+ # B) One-click link: upload `frontguard-report.html` somewhere HTTPS and set before `frontguard run`:
36
+ # export FRONTGUARD_PUBLIC_REPORT_URL='https://…/frontguard-report.html'
37
+ # (e.g. S3 static website, your CDN, or a prior script step). The PR comment will use that link.
38
+ #
39
+ # C) Repo **Downloads** (optional): uncomment the block below. Your API token needs permission to upload
40
+ # downloads (`repository:write`). The comment then points at **Repository → Downloads** + filename.
41
+ #
42
+ # Optional full Markdown log: `--markdown > frontguard-report.md` in the same command.
43
+ #
44
+ # Ollama in Cloud-hosted runners is usually impractical (no daemon). Use `checks.llm.provider:
45
+ # "ollama"` + `perFindingFixes: true` on self-hosted runners or locally; otherwise use cloud
46
+ # keys or `--append` with pasted review notes.
15
47
 
16
48
  image: node:20
17
49
 
@@ -22,14 +54,29 @@ pipelines:
22
54
  name: FrontGuard – post review on PR
23
55
  caches:
24
56
  - node
57
+ artifacts:
58
+ - frontguard-report.html
59
+ - frontguard-report.md
25
60
  script:
26
61
  - corepack enable
27
62
  - yarn install --immutable || yarn install
28
- - yarn frontguard run --markdown > frontguard-report.md
63
+ - |
64
+ yarn frontguard run --markdown \
65
+ --htmlOut frontguard-report.html \
66
+ --prCommentOut frontguard-pr-comment.md \
67
+ > frontguard-report.md
68
+ - grep -q "FrontGuard report (short summary)" frontguard-pr-comment.md || { echo "frontguard-pr-comment.md missing or wrong FrontGuard version; upgrade @cleartrip/frontguard and keep --prCommentOut"; exit 1; }
69
+ # Optional: upload HTML to repo Downloads, then append a second link to the comment file.
70
+ # Requires a token with permission to POST /downloads (often `repository:write` in addition to PR comment).
71
+ # - FG_REPORT_NAME="frontguard-pr-${BITBUCKET_PR_ID}-b${BITBUCKET_BUILD_NUMBER}.html"
72
+ # - cp frontguard-report.html "$FG_REPORT_NAME"
73
+ # - curl -f -sS -X POST "https://api.bitbucket.org/2.0/repositories/${BITBUCKET_REPO_FULL_NAME}/downloads" \
74
+ # -H "Authorization: Bearer ${BITBUCKET_ACCESS_TOKEN}" -F "files=@${FG_REPORT_NAME}"
75
+ # - printf '\n\n**HTML report:** [Repository downloads](https://bitbucket.org/%s/downloads/) — look for `%s`.\n' "${BITBUCKET_REPO_FULL_NAME}" "$FG_REPORT_NAME" >> frontguard-pr-comment.md
29
76
  - |
30
77
  python3 << 'PY'
31
78
  import json
32
- with open("frontguard-report.md", encoding="utf-8") as f:
79
+ with open("frontguard-pr-comment.md", encoding="utf-8") as f:
33
80
  body = f.read()
34
81
  with open("frontguard-payload.json", "w", encoding="utf-8") as out:
35
82
  json.dump({"content": {"raw": body}}, out, ensure_ascii=False)