@yesprasad/fluent-graph 0.2.4 → 0.2.5

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/README.md CHANGED
@@ -118,6 +118,82 @@ Target: x_1566039_seventh_customer
118
118
 
119
119
  ---
120
120
 
121
+ ### Validate — catch structural issues before deploy
122
+
123
+ ```bash
124
+ fluent-graph validate
125
+ ```
126
+
127
+ Runs a set of structural checks across the whole project and reports issues by severity:
128
+
129
+ ```
130
+ 🔍 STRUCTURAL VALIDATION
131
+ ════════════════════════════════════════════
132
+
133
+ ERROR [FG-001] "cs_incident_onload" references field "priorty" on table
134
+ "incident" but no such field exists
135
+ File: client-scripts/cs-incident-onload.now.ts
136
+ Suggestion: Did you mean "priority"?
137
+
138
+ WARNING [FG-002] cs_a and cs_b (order: 100) both fire "onChange" on
139
+ "incident" — nondeterministic execution order
140
+
141
+ ════════════════════════════════════════════
142
+ 1 error, 1 warning
143
+ ```
144
+
145
+ Checks include:
146
+ - **FG-001** — script references a field that doesn't exist on the target table (with Levenshtein-based "did you mean" suggestions)
147
+ - **FG-002** — two scripts on the same table/event/order, so execution order is nondeterministic
148
+ - **FG-003** — a reference points to a table that isn't in the local project or known platform tables
149
+ - **FG-004** — two artifacts with the same name on the same table — the last one defined silently wins
150
+ - **FG-005** — an artifact is marked for deletion but other active artifacts still depend on it
151
+
152
+ `fluent-graph validate` exits non-zero on errors, so it's a natural CI gate:
153
+
154
+ ```bash
155
+ fluent-graph validate --severity error
156
+ ```
157
+
158
+ Use `--format json` for machine-readable output and `--severity <error|warning|info>` to control the noise floor.
159
+
160
+ ---
161
+
162
+ ### Context — minimal, AI-ready context for a table
163
+
164
+ ```bash
165
+ fluent-graph context <table_name>
166
+ ```
167
+
168
+ Extracts just what an LLM (or a human) needs to reason about one table — its fields, attached scripts, business rules, what depends on it, what it depends on, and a blast-radius summary — instead of dumping the entire `fluent-graph.json`:
169
+
170
+ ```
171
+ 🧠 CONTEXT FOR: incident
172
+ ════════════════════════════════════════════════
173
+
174
+ 📋 Fields (3)
175
+ 📋 Attached scripts (4)
176
+ 🔗 Dependents (2 tables reference incident)
177
+ ↗ References from incident (1)
178
+ 🔥 Blast radius: 4 direct, 2 transitive — MEDIUM
179
+
180
+ 📊 TOKEN METRICS
181
+ This context 1,204 tokens
182
+ Full graph 18,340 tokens
183
+ Saved 17,136 tokens
184
+ Reduction 93%
185
+ ```
186
+
187
+ This is meant to be piped straight into an AI coding assistant or agent prompt — `fluent-graph context incident --format json` gives you a compact, structured payload instead of pasting the full graph and burning context window.
188
+
189
+ Options:
190
+ - `--depth <n>` — how many hops of dependents/references to include (default 1)
191
+ - `--no-fields` / `--no-scripts` / `--no-blast` — trim sections you don't need
192
+ - `--show-metrics` — see the per-section token breakdown and the exact JSON payload that would be sent to an LLM
193
+ - `-f, --format <table|json>`
194
+
195
+ ---
196
+
121
197
  ## CI/CD Integration
122
198
 
123
199
  ```yaml
@@ -125,6 +201,9 @@ Target: x_1566039_seventh_customer
125
201
  - name: Analyze lineage
126
202
  run: npx @yesprasad/fluent-graph analyze
127
203
 
204
+ - name: Validate structural issues
205
+ run: npx @yesprasad/fluent-graph validate --severity error
206
+
128
207
  - name: Fail on identity conflicts
129
208
  run: |
130
209
  if grep -q '"action": "CONFLICT"' fluent-graph.json; then
package/dist/cli/index.js CHANGED
@@ -1,32 +1,37 @@
1
- "use strict";var ue=Object.create;var H=Object.defineProperty;var me=Object.getOwnPropertyDescriptor;var he=Object.getOwnPropertyNames;var ye=Object.getPrototypeOf,be=Object.prototype.hasOwnProperty;var Se=(o,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of he(e))!be.call(o,n)&&n!==t&&H(o,n,{get:()=>e[n],enumerable:!(r=me(e,n))||r.enumerable});return o};var E=(o,e,t)=>(t=o!=null?ue(ye(o)):{},Se(e||!o||!o.__esModule?H(t,"default",{value:o,enumerable:!0}):t,o));var pe=require("commander"),ge=E(require("path")),fe=E(require("fs"));var ae=require("commander"),M=E(require("path")),w=E(require("chalk")),R=E(require("ora"));var F=E(require("path")),$=E(require("fs")),P=async o=>{let e=F.default.join(o,"node_modules");if(!$.default.existsSync(e))return{found:!1,error:"node_modules not found. Run npm install first."};let t=F.default.join(e,"@servicenow","sdk");return $.default.existsSync(t)?{found:!0,sdkPath:t}:{found:!1,error:"@servicenow/sdk not found. This is not a ServiceNow Fluent project."}};var J=require("module"),U=E(require("path"));var _=require("module"),q=E(require("path")),I=class{constructor(){this.tables=new Map}load(e){try{let r=(0,_.createRequire)(q.default.join(e,"package.json"))("@servicenow/sdk-build-plugins");for(let[,n]of Object.entries(r)){let i=n;if(!i?.config?.name)continue;let p=i.config.name,h=Object.keys(i.config.records||{}).filter(s=>s!=="*");if(h.length!==0){for(let s of h)this.register(s,p);try{let s=i.getRelationships();s&&typeof s=="object"&&this.parseRelationships(s,p)}catch{}}}}catch{}}register(e,t){this.tables.has(e)||this.tables.set(e,{table:e,pluginName:t,isChild:!1,children:[]})}parseRelationships(e,t){for(let[r,n]of Object.entries(e))this.register(r,t),n&&typeof n=="object"&&this.processChildren(n,t,r)}processChildren(e,t,r){for(let[n,i]of Object.entries(e)){if(n==="via"||n==="descendant"||n==="relationships"||n===r)continue;let p=n;this.register(p,t);let h=this.tables.get(p);h.isChild||(h.isChild=!0,h.parentTable=r,i&&typeof i=="object"&&typeof i.via=="string"&&(h.parentVia=i.via));let s=this.tables.get(r);s&&!s.children.includes(p)&&s.children.push(p),i&&typeof i=="object"&&i.relationships&&typeof i.relationships=="object"&&this.processChildren(i.relationships,t,p)}}get(e){return this.tables.get(e)}getPluginName(e){return this.tables.get(e)?.pluginName??"Record"}getDisplayGroupName(e){let t=this.tables.get(e);return t?t.pluginName.replace(/Plugin$/,"").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/^./,r=>r.toUpperCase()).trim():"Other"}isChild(e){return this.tables.get(e)?.isChild??!1}getParentTable(e){return this.tables.get(e)?.parentTable}getChildren(e){return this.tables.get(e)?.children??[]}getTablesByPlugin(e){return[...this.tables.values()].filter(t=>t.pluginName===e).map(t=>t.table)}getAllPluginNames(){return[...new Set([...this.tables.values()].map(e=>e.pluginName))].sort()}getAllTables(){return[...this.tables.keys()]}isKnown(e){return this.tables.has(e)}};function B(o){let e=(0,J.createRequire)(U.default.join(o,"package.json")),t=new I;return t.load(o),{buildCore:e("@servicenow/sdk-build-core"),sdkapi:e("@servicenow/sdk-api"),registry:t}}var z=E(require("path"));function ve(o){let e={};for(let[t,r]of Object.entries(o))if(r)try{let n=typeof r.getValue=="function"?r.getValue():r;(typeof n!="object"||n===null||Array.isArray(n))&&(e[t]=n)}catch{continue}return e}function X(o,e){let t=[];for(let r of o.query()){let n=r.setProperties||{},i=r.getId()?.getValue()?.toString()||"",p=r.getTable()?.toString()||"",h=n.name?.getValue?.()?.toString()||n.label?.getValue?.()?.toString()||n.sys_name?.getValue?.()?.toString()||i,s=r.getOriginalFilePath()?.toString()||"",y=s?z.default.relative(e,s):"";t.push({id:i,table:p,action:r.getAction(),name:h,attributes:ve(n),source:{file:y,type:r.getOriginalSource()?.getKindName?.()||"SourceFile"}})}return t}function Y(o,e,t){let r=[];for(let n of o.query()){let i=n.getId()?.getValue()?.toString(),p=n.setProperties||{},h=n.getTable()?.toString(),s=p.internal_type?.getValue?.()?.toString(),y=p.reference?.getValue?.()?.toString(),S=p.element?.getValue?.()?.toString();if(s==="reference"&&y){let f=e.find(l=>l.name===y);r.push({from:i,to:f?.id||`platform:${y}`,via:S||"reference",type:"schema_relationship"})}let N=p.table?.getValue?.()?.toString()||p.collection?.getValue?.()?.toString();if(N){let f=e.find(l=>l.name===N);r.push({from:i,to:f?.id||`platform:${N}`,via:p.table?"table":"collection",type:"dependency",targetTable:N})}if(t.getPluginName(h)==="AclPlugin"){let f=p.name?.getValue?.()?.toString();if(f&&f.includes(".")){let[l]=f.split("."),u=e.find(m=>m.name===l);r.push({from:i,to:u?.id||`platform:${l}`,via:"name",type:"dependency",targetTable:l})}}for(let[f,l]of Object.entries(p))if(Array.isArray(l))l.forEach(u=>{if(u&&u.constructor?.name==="RecordId"){let m=u.getValue?.()?.toString(),v=u.getTable?.()?.toString();m&&t.getPluginName(v)!=="TablePlugin"&&r.push({from:i,to:m,via:f,type:"dependency",targetTable:v})}});else if(l&&l.constructor?.name==="RecordId"){let u=l.getValue?.()?.toString(),m=l.getTable?.()?.toString();u&&t.getPluginName(m)!=="TablePlugin"&&r.push({from:i,to:u,via:f,type:"dependency",targetTable:m})}else if(l&&l.constructor?.name==="Record"){let u=l.getId?.()?.getValue?.()?.toString();if(u){let m=e.find(v=>v.id===u);m&&t.getPluginName(m.table)!=="TablePlugin"&&r.push({from:i,to:u,via:f,type:"dependency",targetTable:m.table})}}}return r}function Z(o){let e=[];for(let t of o.query()){let r=t.getId()?.getValue()?.toString(),n=t.getRelated?.()||[];for(let i of n){let p=i.getId()?.getValue()?.toString();p&&r&&e.push({from:r,to:p,type:"composition"})}}return e}function Q(o,e,t,r){let n=o.getConfig()||{},i=o.getPackage()||{};return{projectRoot:o.getRootDir()||process.cwd(),scopeId:n.scopeId||"unknown",scopeName:n.scope||"Global",recordCount:e,referenceCount:t,compositionCount:r,generatedAt:new Date().toISOString(),sdkVersion:i.dependencies?.["@servicenow/sdk"]||i.devDependencies?.["@servicenow/sdk"]||"unknown"}}async function ee(o,e){let t=await o.getRecords(),r=o.getRootDir(),n=X(t,r),i=Y(t,n,e),p=Z(t),h=[...i,...p];return{metadata:Q(o,t.query().length,i.length,p.length),nodes:n,edges:h}}var C=E(require("fs")),te=E(require("path"));function oe(o,e){let t=JSON.stringify(o,null,2),r=te.default.dirname(e);C.default.existsSync(r)||C.default.mkdirSync(r,{recursive:!0}),C.default.writeFileSync(e,t,"utf-8")}var re=E(require("path")),d=E(require("chalk")),L=E(require("cli-table3"));function ne(o,e){console.log(d.default.yellow.bold(`
2
- \u{1F4CA} LINEAGE MAP`)),console.log(d.default.gray("\u2550".repeat(80)));let t=new Map;for(let s of o.nodes){if(s.action==="DELETE")continue;let y=e.getDisplayGroupName(s.table);t.has(y)||t.set(y,[]),t.get(y).push(s)}for(let[s,y]of t){let S=y.filter(f=>!e.isChild(f.table)&&f.source.file);if(S.length===0)continue;if(console.log(d.default.cyan.bold(`
3
- ${s}`)),S.some(f=>e.getPluginName(f.table)==="BusinessRulePlugin")){let f=new L.default({head:[d.default.cyan("Name"),d.default.cyan("When"),d.default.cyan("Table"),d.default.cyan("On"),d.default.cyan("Order"),d.default.cyan("Status"),d.default.cyan("File")],colWidths:[32,8,28,16,7,8,28],wordWrap:!0,style:{head:[],border:["gray"]}});S.forEach(l=>{let u=l.action==="DELETE"?d.default.red("DELETE"):d.default.green("ACTIVE"),m=l.attributes.when||"",g=o.edges.find(c=>c.from===l.id&&c.type==="dependency"&&c.targetTable)?.targetTable||"",b=[];l.attributes.action_insert&&b.push("Insert"),l.attributes.action_update&&b.push("Update"),l.attributes.action_delete&&b.push("Delete"),l.attributes.action_query&&b.push("Query"),f.push([d.default.white(l.name),d.default.yellow(m),d.default.magenta(g),b.length?b.join(", "):d.default.gray("\u2014"),String(l.attributes.order??""),u,d.default.gray(re.default.basename(l.source.file))])}),console.log(f.toString())}else{let f=new L.default({head:[d.default.cyan("Name"),d.default.cyan("Status"),d.default.cyan("Source File")],wordWrap:!0,style:{head:[],border:["gray"]}});S.forEach(l=>{let u=l.action==="DELETE"?d.default.red("DELETE"):d.default.green("ACTIVE");f.push([d.default.white(l.name),u,d.default.gray(l.source.file)])}),console.log(f.toString())}}let r=o.edges.filter(s=>s.type==="schema_relationship");if(r.length>0){console.log(d.default.cyan.bold(`
4
- \u{1F517} SCHEMA RELATIONSHIPS (Reference Fields)`));let s=new L.default({head:[d.default.cyan("Field"),d.default.cyan("From Table"),d.default.cyan("Target Table")],wordWrap:!0,style:{head:[],border:["gray"]}});r.forEach(y=>{let S=o.nodes.find(u=>u.id===y.from),N=S?.attributes?.element||y.via||"unknown",f=S?.attributes?.name||"unknown",l=y.to.startsWith("platform:")?y.to.split(":")[1]:o.nodes.find(u=>u.id===y.to)?.name||y.to;s.push([d.default.white(N),d.default.gray(f),d.default.magenta(l)])}),console.log(s.toString())}console.log(d.default.cyan.bold(`
5
- \u{1F9E0} PROJECT INSIGHTS`));let n=o.edges.filter(s=>s.to.startsWith("platform:")).length,i=o.edges.filter(s=>!s.to.startsWith("platform:")).length,p=o.nodes.filter(s=>s.action!=="DELETE"&&s.source.file&&!s.id.startsWith("platform:")&&!e.isChild(s.table));console.log(` \u2022 Total User Artifacts: ${d.default.cyan(p.length)}`);let h=p.reduce((s,y)=>{let S=e.getDisplayGroupName(y.table);return s[S]=(s[S]||0)+1,s},{});console.log(` \u2022 User Artifacts Breakdown: ${JSON.stringify(h,null,2)}`),console.log(` \u2022 Platform Dependencies: ${d.default.magenta(n)}`),console.log(` \u2022 Internal References: ${d.default.white(i)}`),console.log(` \u2022 Total Artifacts: ${d.default.cyan(o.nodes.length)}`),console.log(` \u2022 Total Relationships: ${d.default.cyan(o.edges.length)}`),console.log(d.default.gray("\u2550".repeat(80))+`
6
- `)}var se=new ae.Command("analyze").description("Analyze and extract dependency graph from the current project").option("-o, --output <path>","Output file path","fluent-graph.json").option("-f, --format <type>","Output format: table, json","table").option("-q, --quiet","Suppress console output").option("--no-display","Skip visual display").option("--no-banner","Skip banner (inherited)").action(async o=>{let e=o.format==="json",t;try{let r=process.cwd();!o.quiet&&!e&&(t=(0,R.default)("Detecting ServiceNow SDK...").start());let n=await P(r);n.found||(t&&t.fail("SDK not found"),e?console.log(JSON.stringify({error:n.error,code:"SDK_NOT_FOUND"})):(console.error(w.default.red(`
7
- \u274C Error:`),n.error),console.log(w.default.yellow(`
8
- \u{1F4A1} Make sure you're in a ServiceNow Fluent project directory`))),process.exit(1)),t&&t.succeed(`\u2705 SDK detected at ${w.default.gray(n.sdkPath)}`),!o.quiet&&!e&&(t=(0,R.default)("Loading workspace SDK...").start());let i=B(n.sdkPath);t&&t.succeed("Workspace SDK loaded"),!o.quiet&&!e&&(t=(0,R.default)("Loading project configuration...").start());let p=new i.sdkapi.Project({rootDir:r});t&&t.succeed("Project loaded"),!o.quiet&&!e&&(t=(0,R.default)("Building dependency graph...").start());let h;if(e){let S=process.stdout.write.bind(process.stdout);process.stdout.write=process.stderr.write.bind(process.stderr),h=()=>{process.stdout.write=S}}let s=await ee(p,i.registry);h?.(),t&&t.succeed(`Graph built: ${w.default.cyan(s.nodes.length)} nodes, ${w.default.cyan(s.edges.length)} edges`);let y=M.default.isAbsolute(o.output)?o.output:M.default.join(r,o.output);oe(s,y),e?console.log(JSON.stringify(s,null,2)):(o.quiet||(console.log(w.default.green(`
9
- \u2705 Analysis complete`)),console.log(w.default.gray(" Location: ")+w.default.white(y)),console.log(w.default.gray(" Complexity: ")+w.default.cyan((s.edges.length/s.nodes.length).toFixed(2))+w.default.gray(" edges/node")),console.log(w.default.gray(" Generated at: ")+w.default.gray(new Date().toLocaleTimeString()))),o.display&&!o.quiet&&ne(s,i.registry))}catch(r){if(e){let n=r instanceof Error?r.message:"Unexpected error";console.log(JSON.stringify({error:n,code:"EXTRACTION_FAILED"}))}else r instanceof Error?(console.error(w.default.red(`
10
- \u274C Error:`),r.message),process.env.DEBUG&&(console.error(w.default.gray(`
11
- Stack trace:`)),console.error(r.stack))):(console.error(w.default.red(`
12
- \u274C Unknown error occurred`)),console.error(r));process.exit(1)}});var ce=require("commander"),O=E(require("fs")),G=E(require("path")),a=E(require("chalk")),V=E(require("cli-table3"));function Ee(o){let e=new Map;for(let t of o.nodes){let r=e.get(t.name)??[];r.push(t.id),e.set(t.name,r)}return e}function ie(o,e,t){let r=Math.min(t,25),n=Ee(o),i=o.nodes.filter(m=>m.name===e),p=i[0]??null,h=new Set,s=`platform:${e}`;h.add(s);for(let m of i)h.add(m.id);let S=[...new Set([s,...i.map(m=>m.id)])].map(m=>({nodeId:m,nodeName:e,hop:0,path:[e]})),N=[],f=new Set([e]),l=0,u=0;for(;S.length>0;){let m=[];for(let v of S){if(v.hop>=r)continue;let g=o.edges.filter(b=>b.to===v.nodeId||v.nodeName.length>0&&b.targetTable===v.nodeName);for(let b of g){let c=o.nodes.find(k=>k.id===b.from);if(!c)continue;if(h.has(c.id)){u++;continue}h.add(c.id);let A=n.get(c.name)??[];for(let k of A)h.has(k)||h.add(k);let T=v.hop+1,D=[...v.path,c.name];f.has(c.name)||(f.add(c.name),N.push({node:c,hop:T,path:D,viaEdge:b}));for(let k of A)m.push({nodeId:k,nodeName:c.name,hop:T,path:D});T>l&&(l=T)}}S=m}return N.sort((m,v)=>m.hop-v.hop||m.node.name.localeCompare(v.node.name)),{target:p,targetName:e,hops:N,maxHopReached:l,cyclesPrevented:u}}var le=new ce.Command("blast").description("Analyze blast radius for a target table").argument("<target>","Target table name (e.g., incident, x_bank_account)").option("-g, --graph <path>","Path to graph JSON file","fluent-graph.json").option("-f, --format <type>","Output format: table, json, csv","table").option("--depth <n>","Maximum traversal depth","10").option("--show-paths","Show the full dependency chain for each transitive artifact").option("--direct-only","Show only hop-1 (direct) impacts").option("--include-deleted","Include deleted artifacts in the analysis").option("--no-banner","Skip banner (inherited)").action(async(o,e)=>{let t=e.format==="json";try{let r=G.isAbsolute(e.graph)?e.graph:G.join(process.cwd(),e.graph),n=new I,i=await P(process.cwd());i.found&&i.sdkPath&&n.load(i.sdkPath),O.existsSync(r)||(t?console.log(JSON.stringify({error:`Graph file not found at ${r}`,code:"GRAPH_NOT_FOUND"})):console.error(a.default.red(`
13
- \u274C Error: Graph file not found at ${r}`)),process.exit(1));let p=JSON.parse(O.readFileSync(r,"utf8")),h=p.nodes.find(g=>g.name===o),s=h?h.id:`platform:${o}`,y=!h,S=p.edges.some(g=>g.to===s||g.targetTable===o);if(!h&&!S){let g=p.nodes.filter(c=>n.getPluginName(c.table)==="TablePlugin"&&!n.isChild(c.table)&&c.action!=="DELETE").map(c=>c.name).sort(),b=[...new Set(p.edges.filter(c=>c.to.startsWith("platform:")).map(c=>c.to.split(":")[1]))].sort();t?console.log(JSON.stringify({error:`Target "${o}" not found or referenced`,code:"TARGET_NOT_FOUND",availableCustomTables:g,availablePlatformTables:b})):(console.log(a.default.red(`
14
- \u274C Target "${o}" not found or referenced`)),console.log(a.default.yellow(`
15
- \u{1F4A1} Available custom tables:`)),g.forEach(c=>console.log(a.default.gray(" \u2022 ")+a.default.white(c))),console.log(a.default.yellow(`
16
- \u{1F4A1} Referenced platform tables:`)),b.forEach(c=>console.log(a.default.gray(" \u2022 ")+a.default.magenta(c)))),process.exit(1)}t||(console.log(a.default.yellow.bold(`
17
- \u{1F525} BLAST RADIUS ANALYSIS`)),console.log(a.default.gray("\u2550".repeat(80))),console.log(a.default.gray("Target: ")+a.default.white(o)+(y?a.default.yellow(" [Base Table]"):"")),y||console.log(a.default.gray("File: ")+a.default.gray(h.source.file)),console.log(a.default.gray("\u2550".repeat(80))));let N=e.directOnly?1:Math.max(1,parseInt(e.depth,10)||10),f=ie(p,o,N),l=g=>!e.includeDeleted&&g.node.action==="DELETE"?!1:g.node.source.file?!0:!n.isChild(g.node.table),u=f.hops.filter(l),m=u.filter(g=>g.hop===1),v=u.filter(g=>g.hop>1);if(u.length===0){console.log(t?JSON.stringify({target:o,isBaseTable:y,totalImpacted:0,direct:0,transitive:0,maxHopReached:0,cyclesPrevented:f.cyclesPrevented,riskLevel:"LOW",impacts:[]},null,2):a.default.green(`
1
+ "use strict";var $e=Object.create;var oe=Object.defineProperty;var Ge=Object.getOwnPropertyDescriptor;var Pe=Object.getOwnPropertyNames;var Fe=Object.getPrototypeOf,Ve=Object.prototype.hasOwnProperty;var Me=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Pe(e))!Ve.call(t,o)&&o!==n&&oe(t,o,{get:()=>e[o],enumerable:!(r=Ge(e,o))||r.enumerable});return t};var T=(t,e,n)=>(n=t!=null?$e(Fe(t)):{},Me(e||!t||!t.__esModule?oe(n,"default",{value:t,enumerable:!0}):n,t));var xe=require("commander"),Le=T(require("path")),Ce=T(require("fs"));var ye=require("commander"),Z=T(require("path")),D=T(require("chalk")),W=T(require("ora"));var Y=T(require("path")),X=T(require("fs")),C=async t=>{let e=Y.default.join(t,"node_modules");if(!X.default.existsSync(e))return{found:!1,error:"node_modules not found. Run npm install first."};let n=Y.default.join(e,"@servicenow","sdk");return X.default.existsSync(n)?{found:!0,sdkPath:n}:{found:!1,error:"@servicenow/sdk not found. This is not a ServiceNow Fluent project."}};var ie=require("module"),ae=T(require("path"));var re=require("module"),se=T(require("path")),F=class{constructor(){this.tables=new Map}load(e){try{let r=(0,re.createRequire)(se.default.join(e,"package.json"))("@servicenow/sdk-build-plugins");for(let[,o]of Object.entries(r)){let s=o;if(!s?.config?.name)continue;let a=s.config.name,l=Object.keys(s.config.records||{}).filter(i=>i!=="*");if(l.length!==0){for(let i of l)this.register(i,a);try{let i=s.getRelationships();i&&typeof i=="object"&&this.parseRelationships(i,a)}catch{}}}}catch{}}register(e,n){this.tables.has(e)||this.tables.set(e,{table:e,pluginName:n,isChild:!1,children:[]})}parseRelationships(e,n){for(let[r,o]of Object.entries(e))this.register(r,n),o&&typeof o=="object"&&this.processChildren(o,n,r)}processChildren(e,n,r){for(let[o,s]of Object.entries(e)){if(o==="via"||o==="descendant"||o==="relationships"||o===r)continue;let a=o;this.register(a,n);let l=this.tables.get(a);l.isChild||(l.isChild=!0,l.parentTable=r,s&&typeof s=="object"&&typeof s.via=="string"&&(l.parentVia=s.via));let i=this.tables.get(r);i&&!i.children.includes(a)&&i.children.push(a),s&&typeof s=="object"&&s.relationships&&typeof s.relationships=="object"&&this.processChildren(s.relationships,n,a)}}get(e){return this.tables.get(e)}getPluginName(e){return this.tables.get(e)?.pluginName??"Record"}getDisplayGroupName(e){let n=this.tables.get(e);return n?n.pluginName.replace(/Plugin$/,"").replace(/([a-z])([A-Z])/g,"$1 $2").replace(/^./,r=>r.toUpperCase()).trim():"Other"}isChild(e){return this.tables.get(e)?.isChild??!1}getParentTable(e){return this.tables.get(e)?.parentTable}getChildren(e){return this.tables.get(e)?.children??[]}getTablesByPlugin(e){return[...this.tables.values()].filter(n=>n.pluginName===e).map(n=>n.table)}getAllPluginNames(){return[...new Set([...this.tables.values()].map(e=>e.pluginName))].sort()}getAllTables(){return[...this.tables.keys()]}isKnown(e){return this.tables.has(e)}};function V(t){let e=(0,ie.createRequire)(ae.default.join(t,"package.json")),n=new F;return n.load(t),{buildCore:e("@servicenow/sdk-build-core"),sdkapi:e("@servicenow/sdk-api"),registry:n}}var ce=T(require("path"));function je(t){let e={};for(let[n,r]of Object.entries(t))if(r)try{let o=typeof r.getValue=="function"?r.getValue():r;(typeof o!="object"||o===null||Array.isArray(o))&&(e[n]=o)}catch{continue}return e}function le(t,e){let n=[];for(let r of t.query()){let o=r.setProperties||{},s=r.getId()?.getValue()?.toString()||"",a=r.getTable()?.toString()||"",l=o.name?.getValue?.()?.toString()||o.label?.getValue?.()?.toString()||o.sys_name?.getValue?.()?.toString()||s,i=r.getOriginalFilePath()?.toString()||"",c=i?ce.default.relative(e,i):"";n.push({id:s,table:a,action:r.getAction(),name:l,attributes:je(o),source:{file:c,type:r.getOriginalSource()?.getKindName?.()||"SourceFile"}})}return n}function de(t,e,n){let r=[];for(let o of t.query()){let s=o.getId()?.getValue()?.toString(),a=o.setProperties||{},l=o.getTable()?.toString(),i=a.internal_type?.getValue?.()?.toString(),c=a.reference?.getValue?.()?.toString(),m=a.element?.getValue?.()?.toString();if(i==="reference"&&c){let h=e.find(d=>d.name===c);r.push({from:s,to:h?.id||`platform:${c}`,via:m||"reference",type:"schema_relationship"})}let v=a.table?.getValue?.()?.toString()||a.collection?.getValue?.()?.toString();if(v){let h=e.find(d=>d.name===v);r.push({from:s,to:h?.id||`platform:${v}`,via:a.table?"table":"collection",type:"dependency",targetTable:v})}if(n.getPluginName(l)==="AclPlugin"){let h=a.name?.getValue?.()?.toString();if(h&&h.includes(".")){let[d]=h.split("."),g=e.find(w=>w.name===d);r.push({from:s,to:g?.id||`platform:${d}`,via:"name",type:"dependency",targetTable:d})}}for(let[h,d]of Object.entries(a))if(Array.isArray(d))d.forEach(g=>{if(g&&g.constructor?.name==="RecordId"){let w=g.getValue?.()?.toString(),N=g.getTable?.()?.toString();w&&n.getPluginName(N)!=="TablePlugin"&&r.push({from:s,to:w,via:h,type:"dependency",targetTable:N})}});else if(d&&d.constructor?.name==="RecordId"){let g=d.getValue?.()?.toString(),w=d.getTable?.()?.toString();g&&n.getPluginName(w)!=="TablePlugin"&&r.push({from:s,to:g,via:h,type:"dependency",targetTable:w})}else if(d&&d.constructor?.name==="Record"){let g=d.getId?.()?.getValue?.()?.toString();if(g){let w=e.find(N=>N.id===g);w&&n.getPluginName(w.table)!=="TablePlugin"&&r.push({from:s,to:g,via:h,type:"dependency",targetTable:w.table})}}}return r}function fe(t){let e=[];for(let n of t.query()){let r=n.getId()?.getValue()?.toString(),o=n.getRelated?.()||[];for(let s of o){let a=s.getId()?.getValue()?.toString();a&&r&&e.push({from:r,to:a,type:"composition"})}}return e}function ge(t,e,n,r){let o=t.getConfig()||{},s=t.getPackage()||{};return{projectRoot:t.getRootDir()||process.cwd(),scopeId:o.scopeId||"unknown",scopeName:o.scope||"Global",recordCount:e,referenceCount:n,compositionCount:r,generatedAt:new Date().toISOString(),sdkVersion:s.dependencies?.["@servicenow/sdk"]||s.devDependencies?.["@servicenow/sdk"]||"unknown"}}async function M(t,e){let n=await t.getRecords(),r=t.getRootDir(),o=le(n,r),s=de(n,o,e),a=fe(n),l=[...s,...a];return{metadata:ge(t,n.query().length,s.length,a.length),nodes:o,edges:l}}var _=T(require("fs")),pe=T(require("path"));function ue(t,e){let n=JSON.stringify(t,null,2),r=pe.default.dirname(e);_.default.existsSync(r)||_.default.mkdirSync(r,{recursive:!0}),_.default.writeFileSync(e,n,"utf-8")}var me=T(require("path")),b=T(require("chalk")),J=T(require("cli-table3"));function he(t,e){console.log(b.default.yellow.bold(`
2
+ \u{1F4CA} LINEAGE MAP`)),console.log(b.default.gray("\u2550".repeat(80)));let n=new Map;for(let i of t.nodes){if(i.action==="DELETE")continue;let c=e.getDisplayGroupName(i.table);n.has(c)||n.set(c,[]),n.get(c).push(i)}for(let[i,c]of n){let m=c.filter(h=>!e.isChild(h.table)&&h.source.file);if(m.length===0)continue;if(console.log(b.default.cyan.bold(`
3
+ ${i}`)),m.some(h=>e.getPluginName(h.table)==="BusinessRulePlugin")){let h=new J.default({head:[b.default.cyan("Name"),b.default.cyan("When"),b.default.cyan("Table"),b.default.cyan("On"),b.default.cyan("Order"),b.default.cyan("Status"),b.default.cyan("File")],colWidths:[32,8,28,16,7,8,28],wordWrap:!0,style:{head:[],border:["gray"]}});m.forEach(d=>{let g=d.action==="DELETE"?b.default.red("DELETE"):b.default.green("ACTIVE"),w=d.attributes.when||"",p=t.edges.find(y=>y.from===d.id&&y.type==="dependency"&&y.targetTable)?.targetTable||"",S=[];d.attributes.action_insert&&S.push("Insert"),d.attributes.action_update&&S.push("Update"),d.attributes.action_delete&&S.push("Delete"),d.attributes.action_query&&S.push("Query"),h.push([b.default.white(d.name),b.default.yellow(w),b.default.magenta(p),S.length?S.join(", "):b.default.gray("\u2014"),String(d.attributes.order??""),g,b.default.gray(me.default.basename(d.source.file))])}),console.log(h.toString())}else{let h=new J.default({head:[b.default.cyan("Name"),b.default.cyan("Status"),b.default.cyan("Source File")],wordWrap:!0,style:{head:[],border:["gray"]}});m.forEach(d=>{let g=d.action==="DELETE"?b.default.red("DELETE"):b.default.green("ACTIVE");h.push([b.default.white(d.name),g,b.default.gray(d.source.file)])}),console.log(h.toString())}}let r=t.edges.filter(i=>i.type==="schema_relationship");if(r.length>0){console.log(b.default.cyan.bold(`
4
+ \u{1F517} SCHEMA RELATIONSHIPS (Reference Fields)`));let i=new J.default({head:[b.default.cyan("Field"),b.default.cyan("From Table"),b.default.cyan("Target Table")],wordWrap:!0,style:{head:[],border:["gray"]}});r.forEach(c=>{let m=t.nodes.find(g=>g.id===c.from),v=m?.attributes?.element||c.via||"unknown",h=m?.attributes?.name||"unknown",d=c.to.startsWith("platform:")?c.to.split(":")[1]:t.nodes.find(g=>g.id===c.to)?.name||c.to;i.push([b.default.white(v),b.default.gray(h),b.default.magenta(d)])}),console.log(i.toString())}console.log(b.default.cyan.bold(`
5
+ \u{1F9E0} PROJECT INSIGHTS`));let o=t.edges.filter(i=>i.to.startsWith("platform:")).length,s=t.edges.filter(i=>!i.to.startsWith("platform:")).length,a=t.nodes.filter(i=>i.action!=="DELETE"&&i.source.file&&!i.id.startsWith("platform:")&&!e.isChild(i.table));console.log(` \u2022 Total User Artifacts: ${b.default.cyan(a.length)}`);let l=a.reduce((i,c)=>{let m=e.getDisplayGroupName(c.table);return i[m]=(i[m]||0)+1,i},{});console.log(` \u2022 User Artifacts Breakdown: ${JSON.stringify(l,null,2)}`),console.log(` \u2022 Platform Dependencies: ${b.default.magenta(o)}`),console.log(` \u2022 Internal References: ${b.default.white(s)}`),console.log(` \u2022 Total Artifacts: ${b.default.cyan(t.nodes.length)}`),console.log(` \u2022 Total Relationships: ${b.default.cyan(t.edges.length)}`),console.log(b.default.gray("\u2550".repeat(80))+`
6
+ `)}var be=new ye.Command("analyze").description("Analyze and extract dependency graph from the current project").option("-o, --output <path>","Output file path","fluent-graph.json").option("-f, --format <type>","Output format: table, json","table").option("-q, --quiet","Suppress console output").option("--no-display","Skip visual display").option("--no-banner","Skip banner (inherited)").action(async t=>{let e=t.format==="json",n;try{let r=process.cwd();!t.quiet&&!e&&(n=(0,W.default)("Detecting ServiceNow SDK...").start());let o=await C(r);o.found||(n&&n.fail("SDK not found"),e?console.log(JSON.stringify({error:o.error,code:"SDK_NOT_FOUND"})):(console.error(D.default.red(`
7
+ \u274C Error:`),o.error),console.log(D.default.yellow(`
8
+ \u{1F4A1} Make sure you're in a ServiceNow Fluent project directory`))),process.exit(1)),n&&n.succeed(`\u2705 SDK detected at ${D.default.gray(o.sdkPath)}`),!t.quiet&&!e&&(n=(0,W.default)("Loading workspace SDK...").start());let s=V(o.sdkPath);n&&n.succeed("Workspace SDK loaded"),!t.quiet&&!e&&(n=(0,W.default)("Loading project configuration...").start());let a=new s.sdkapi.Project({rootDir:r});n&&n.succeed("Project loaded"),!t.quiet&&!e&&(n=(0,W.default)("Building dependency graph...").start());let l;if(e){let m=process.stdout.write.bind(process.stdout);process.stdout.write=process.stderr.write.bind(process.stderr),l=()=>{process.stdout.write=m}}let i=await M(a,s.registry);l?.(),n&&n.succeed(`Graph built: ${D.default.cyan(i.nodes.length)} nodes, ${D.default.cyan(i.edges.length)} edges`);let c=Z.default.isAbsolute(t.output)?t.output:Z.default.join(r,t.output);ue(i,c),e?console.log(JSON.stringify(i,null,2)):(t.quiet||(console.log(D.default.green(`
9
+ \u2705 Analysis complete`)),console.log(D.default.gray(" Location: ")+D.default.white(c)),console.log(D.default.gray(" Complexity: ")+D.default.cyan((i.edges.length/i.nodes.length).toFixed(2))+D.default.gray(" edges/node")),console.log(D.default.gray(" Generated at: ")+D.default.gray(new Date().toLocaleTimeString()))),t.display&&!t.quiet&&he(i,s.registry))}catch(r){if(e){let o=r instanceof Error?r.message:"Unexpected error";console.log(JSON.stringify({error:o,code:"EXTRACTION_FAILED"}))}else r instanceof Error?(console.error(D.default.red(`
10
+ \u274C Error:`),r.message),process.env.DEBUG&&(console.error(D.default.gray(`
11
+ Stack trace:`)),console.error(r.stack))):(console.error(D.default.red(`
12
+ \u274C Unknown error occurred`)),console.error(r));process.exit(1)}});var we=require("commander"),B=T(require("fs")),H=T(require("path")),f=T(require("chalk")),Q=T(require("cli-table3"));function Ke(t){let e=new Map;for(let n of t.nodes){let r=e.get(n.name)??[];r.push(n.id),e.set(n.name,r)}return e}function We(t){let e=new Map,n=new Map,r=new Map;return t.edges.forEach((o,s)=>{r.set(o,s);let a=e.get(o.to)??[];if(a.push(o),e.set(o.to,a),o.targetTable){let l=n.get(o.targetTable)??[];l.push(o),n.set(o.targetTable,l)}}),{byNodeId:e,byTargetTable:n,originalIndexByEdge:r}}function _e(t,e,n){let{byNodeId:r,byTargetTable:o,originalIndexByEdge:s}=t,a=new Set(r.get(e)??[]);if(n.length>0)for(let l of o.get(n)??[])a.add(l);return[...a].sort((l,i)=>s.get(l)-s.get(i))}function j(t,e,n){let r=Math.min(n,25),o=Ke(t),s=We(t),a=new Map(t.nodes.map(p=>[p.id,p])),l=t.nodes.filter(p=>p.name===e),i=l[0]??null,c=new Set,m=`platform:${e}`;c.add(m);for(let p of l)c.add(p.id);let h=[...new Set([m,...l.map(p=>p.id)])].map(p=>({nodeId:p,nodeName:e,hop:0,path:[e]})),d=[],g=new Set([e]),w=0,N=0;for(;h.length>0;){let p=[];for(let S of h){if(S.hop>=r)continue;let y=_e(s,S.nodeId,S.nodeName);for(let A of y){let E=a.get(A.from);if(!E)continue;if(c.has(E.id)){N++;continue}c.add(E.id);let I=o.get(E.name)??[];for(let $ of I)c.has($)||c.add($);let R=S.hop+1,x=[...S.path,E.name];g.has(E.name)||(g.add(E.name),d.push({node:E,hop:R,path:x,viaEdge:A}));for(let $ of I)p.push({nodeId:$,nodeName:E.name,hop:R,path:x});R>w&&(w=R)}}h=p}return d.sort((p,S)=>p.hop-S.hop||p.node.name.localeCompare(S.node.name)),{target:i,targetName:e,hops:d,maxHopReached:w,cyclesPrevented:N}}var Se=new we.Command("blast").description("Analyze blast radius for a target table").argument("<target>","Target table name (e.g., incident, x_bank_account)").option("-g, --graph <path>","Path to graph JSON file","fluent-graph.json").option("-f, --format <type>","Output format: table, json, csv","table").option("--depth <n>","Maximum traversal depth","10").option("--show-paths","Show the full dependency chain for each transitive artifact").option("--direct-only","Show only hop-1 (direct) impacts").option("--include-deleted","Include deleted artifacts in the analysis").option("--no-banner","Skip banner (inherited)").action(async(t,e)=>{let n=e.format==="json";try{let r=H.isAbsolute(e.graph)?e.graph:H.join(process.cwd(),e.graph),o=new F,s=await C(process.cwd());s.found&&s.sdkPath&&o.load(s.sdkPath),B.existsSync(r)||(n?console.log(JSON.stringify({error:`Graph file not found at ${r}`,code:"GRAPH_NOT_FOUND"})):console.error(f.default.red(`
13
+ \u274C Error: Graph file not found at ${r}`)),process.exit(1));let a=JSON.parse(B.readFileSync(r,"utf8")),l=a.nodes.find(p=>p.name===t),i=l?l.id:`platform:${t}`,c=!l,m=a.edges.some(p=>p.to===i||p.targetTable===t);if(!l&&!m){let p=a.nodes.filter(y=>o.getPluginName(y.table)==="TablePlugin"&&!o.isChild(y.table)&&y.action!=="DELETE").map(y=>y.name).sort(),S=[...new Set(a.edges.filter(y=>y.to.startsWith("platform:")).map(y=>y.to.split(":")[1]))].sort();n?console.log(JSON.stringify({error:`Target "${t}" not found or referenced`,code:"TARGET_NOT_FOUND",availableCustomTables:p,availablePlatformTables:S})):(console.log(f.default.red(`
14
+ \u274C Target "${t}" not found or referenced`)),console.log(f.default.yellow(`
15
+ \u{1F4A1} Available custom tables:`)),p.forEach(y=>console.log(f.default.gray(" \u2022 ")+f.default.white(y))),console.log(f.default.yellow(`
16
+ \u{1F4A1} Referenced platform tables:`)),S.forEach(y=>console.log(f.default.gray(" \u2022 ")+f.default.magenta(y)))),process.exit(1)}n||(console.log(f.default.yellow.bold(`
17
+ \u{1F525} BLAST RADIUS ANALYSIS`)),console.log(f.default.gray("\u2550".repeat(80))),console.log(f.default.gray("Target: ")+f.default.white(t)+(c?f.default.yellow(" [Base Table]"):"")),c||console.log(f.default.gray("File: ")+f.default.gray(l.source.file)),console.log(f.default.gray("\u2550".repeat(80))));let v=e.directOnly?1:Math.max(1,parseInt(e.depth,10)||10),h=j(a,t,v),d=p=>!e.includeDeleted&&p.node.action==="DELETE"?!1:p.node.source.file?!0:!o.isChild(p.node.table),g=h.hops.filter(d),w=g.filter(p=>p.hop===1),N=g.filter(p=>p.hop>1);if(g.length===0){console.log(n?JSON.stringify({target:t,isBaseTable:c,totalImpacted:0,direct:0,transitive:0,maxHopReached:0,cyclesPrevented:h.cyclesPrevented,riskLevel:"LOW",impacts:[]},null,2):f.default.green(`
18
18
  \u2705 Zero dependencies detected in local project.
19
- `));return}switch(e.format){case"json":{let g=u.length,b=g<3?"LOW":g<8?"MEDIUM":"HIGH";console.log(JSON.stringify({target:o,isBaseTable:y,totalImpacted:g,direct:m.length,transitive:v.length,maxHopReached:f.maxHopReached,cyclesPrevented:f.cyclesPrevented,riskLevel:b,impacts:u.map(c=>({artifact:c.node.name,type:n.getDisplayGroupName(c.node.table).toLowerCase(),file:c.node.source.file,hop:c.hop,path:c.path,via:c.viaEdge.via??c.viaEdge.type,edgeType:c.viaEdge.type,state:c.node.action}))},null,2));break}case"csv":{console.log("Artifact,Type,File,Hop,Via,State,Path"),u.forEach(g=>{let b=g.viaEdge.via??g.viaEdge.type,c=g.path.join(" > ");console.log(`"${g.node.name}","${n.getDisplayGroupName(g.node.table).toLowerCase()}","${g.node.source.file}",${g.hop},"${b}","${g.node.action}","${c}"`)});break}default:{if(m.length>0){console.log(a.default.bold(`
20
- \u26A1 DIRECT IMPACT`)+a.default.gray(" (Hop 1)"));let A=new V.default({head:[a.default.cyan("Artifact"),a.default.cyan("Type"),a.default.cyan("File"),a.default.cyan("Via"),a.default.cyan("State")],wordWrap:!0,style:{head:[],border:["gray"]}});m.forEach(T=>{let D=we(T),k=T.node.action==="DELETE"?a.default.red:a.default.green;A.push([a.default.white(T.node.name),a.default.gray(n.getDisplayGroupName(T.node.table).toLowerCase()),a.default.gray(T.node.source.file||"\u2014"),a.default.gray(D),k(T.node.action==="DELETE"?"DELETE":"ACTIVE")])}),console.log(A.toString())}if(v.length>0&&!e.directOnly){console.log(a.default.bold(`
21
- \u{1F30A} TRANSITIVE IMPACT`)+a.default.gray(" (Hop 2+)"));let A=[a.default.cyan("Artifact"),a.default.cyan("Type"),a.default.cyan("File"),a.default.cyan("Hop"),a.default.cyan("State")];e.showPaths&&A.push(a.default.cyan("Path"));let T=new V.default({head:A,wordWrap:!0,style:{head:[],border:["gray"]}});v.forEach(D=>{let k=D.node.action==="DELETE"?a.default.red:a.default.green,K=[a.default.white(D.node.name),a.default.gray(n.getDisplayGroupName(D.node.table).toLowerCase()),a.default.gray(D.node.source.file||"\u2014"),a.default.yellow(String(D.hop)),k(D.node.action==="DELETE"?"DELETE":"ACTIVE")];e.showPaths&&K.push(a.default.gray(D.path.join(" \u2192 "))),T.push(K)}),console.log(T.toString())}console.log(a.default.yellow(`
22
- \u{1F4CA} IMPACT SUMMARY`));let g=u.length,b=g<3?"LOW":g<8?"MEDIUM":"HIGH",c=b==="LOW"?a.default.green:b==="MEDIUM"?a.default.yellow:a.default.red;console.log(a.default.gray("Total Impacted: ")+a.default.cyan(g)+a.default.gray(` (${m.length} direct, ${v.length} transitive)`)),console.log(a.default.gray("Max Depth Reached: ")+a.default.cyan(f.maxHopReached)),console.log(a.default.gray("Risk Level: ")+c.bold(b)),console.log(a.default.gray("\u2500".repeat(80))+`
23
- `),b==="HIGH"?console.log(a.default.yellow("\u26A0\uFE0F High impact \u2014 review carefully before deploying")):b==="MEDIUM"&&console.log(a.default.yellow("\u{1F4A1} Moderate impact"));break}}}catch(r){t?console.log(JSON.stringify({error:r.message??"Unexpected error",code:"UNEXPECTED_ERROR"})):console.error(a.default.red(`
24
- \u274C Error:`),r.message),process.exit(1)}});function we(o){let e=o.viaEdge;return e.type==="schema_relationship"?`${e.via??"ref"} (ref field)`:e.via??e.type}var j=E(require("chalk"));function de(){console.log(j.default.cyan(`
19
+ `));return}switch(e.format){case"json":{let p=g.length,S=p<3?"LOW":p<8?"MEDIUM":"HIGH";console.log(JSON.stringify({target:t,isBaseTable:c,totalImpacted:p,direct:w.length,transitive:N.length,maxHopReached:h.maxHopReached,cyclesPrevented:h.cyclesPrevented,riskLevel:S,impacts:g.map(y=>({artifact:y.node.name,type:o.getDisplayGroupName(y.node.table).toLowerCase(),file:y.node.source.file,hop:y.hop,path:y.path,via:y.viaEdge.via??y.viaEdge.type,edgeType:y.viaEdge.type,state:y.node.action}))},null,2));break}case"csv":{console.log("Artifact,Type,File,Hop,Via,State,Path"),g.forEach(p=>{let S=p.viaEdge.via??p.viaEdge.type,y=p.path.join(" > ");console.log(`"${p.node.name}","${o.getDisplayGroupName(p.node.table).toLowerCase()}","${p.node.source.file}",${p.hop},"${S}","${p.node.action}","${y}"`)});break}default:{if(w.length>0){console.log(f.default.bold(`
20
+ \u26A1 DIRECT IMPACT`)+f.default.gray(" (Hop 1)"));let A=new Q.default({head:[f.default.cyan("Artifact"),f.default.cyan("Type"),f.default.cyan("File"),f.default.cyan("Via"),f.default.cyan("State")],wordWrap:!0,style:{head:[],border:["gray"]}});w.forEach(E=>{let I=Je(E),R=E.node.action==="DELETE"?f.default.red:f.default.green;A.push([f.default.white(E.node.name),f.default.gray(o.getDisplayGroupName(E.node.table).toLowerCase()),f.default.gray(E.node.source.file||"\u2014"),f.default.gray(I),R(E.node.action==="DELETE"?"DELETE":"ACTIVE")])}),console.log(A.toString())}if(N.length>0&&!e.directOnly){console.log(f.default.bold(`
21
+ \u{1F30A} TRANSITIVE IMPACT`)+f.default.gray(" (Hop 2+)"));let A=[f.default.cyan("Artifact"),f.default.cyan("Type"),f.default.cyan("File"),f.default.cyan("Hop"),f.default.cyan("State")];e.showPaths&&A.push(f.default.cyan("Path"));let E=new Q.default({head:A,wordWrap:!0,style:{head:[],border:["gray"]}});N.forEach(I=>{let R=I.node.action==="DELETE"?f.default.red:f.default.green,x=[f.default.white(I.node.name),f.default.gray(o.getDisplayGroupName(I.node.table).toLowerCase()),f.default.gray(I.node.source.file||"\u2014"),f.default.yellow(String(I.hop)),R(I.node.action==="DELETE"?"DELETE":"ACTIVE")];e.showPaths&&x.push(f.default.gray(I.path.join(" \u2192 "))),E.push(x)}),console.log(E.toString())}console.log(f.default.yellow(`
22
+ \u{1F4CA} IMPACT SUMMARY`));let p=g.length,S=p<3?"LOW":p<8?"MEDIUM":"HIGH",y=S==="LOW"?f.default.green:S==="MEDIUM"?f.default.yellow:f.default.red;console.log(f.default.gray("Total Impacted: ")+f.default.cyan(p)+f.default.gray(` (${w.length} direct, ${N.length} transitive)`)),console.log(f.default.gray("Max Depth Reached: ")+f.default.cyan(h.maxHopReached)),console.log(f.default.gray("Risk Level: ")+y.bold(S)),console.log(f.default.gray("\u2500".repeat(80))+`
23
+ `),S==="HIGH"?console.log(f.default.yellow("\u26A0\uFE0F High impact \u2014 review carefully before deploying")):S==="MEDIUM"&&console.log(f.default.yellow("\u{1F4A1} Moderate impact"));break}}}catch(r){n?console.log(JSON.stringify({error:r.message??"Unexpected error",code:"UNEXPECTED_ERROR"})):console.error(f.default.red(`
24
+ \u274C Error:`),r.message),process.exit(1)}});function Je(t){let e=t.viaEdge;return e.type==="schema_relationship"?`${e.via??"ref"} (ref field)`:e.via??e.type}var Ee=require("commander"),u=T(require("chalk")),ee=T(require("ora")),G=T(require("cli-table3"));var Te=T(require("crypto"));function O(t,e){let n=t.attributes[e];return typeof n=="string"?n:void 0}function Be(t,e){let n=t.attributes[e];if(typeof n=="number")return n;if(typeof n=="string"&&n.trim()!==""){let r=Number(n);return Number.isNaN(r)?void 0:r}}function He(t,e){let n=t.attributes[e];return typeof n=="boolean"?n:void 0}function Ne(t,e,n,r={}){let{depth:o=1,includeFields:s=!0,includeScripts:a=!0,includeBlastRadius:l=!0}=r,i=Math.ceil(JSON.stringify(t).length/4),c=Ue(t,e,n),m=s?qe(t,e,c):[],{scripts:v,businessRules:h}=a?ze(t,e,c,n):{scripts:[],businessRules:[]},d=ve(t,n),g=Ye(t,c),w=new Set(d.map(I=>I.table));if(o>=2)for(let I of[...d]){let R=ve(t,I.table);for(let x of R)w.has(x.table)||(w.add(x.table),d.push(x))}let N;if(l){let I=j(t,n,10),R=I.hops.filter(K=>K.hop===1&&K.node.source.file).length,x=I.hops.filter(K=>K.hop>1&&K.node.source.file).length,$=R+x,Oe=$<3?"LOW":$<8?"MEDIUM":"HIGH";N={direct:R,transitive:x,risk:Oe}}let p={target:n,fields:m,scripts:v,businessRules:h,dependents:d,references:g,blastRadius:N},S=Math.ceil(JSON.stringify(p).length/4),y=Math.max(0,i-S),A=i>0?Math.round(y/i*100):0;return{contextId:Te.default.createHash("sha256").update(JSON.stringify(p)).digest("hex").slice(0,12),target:n,fields:m,scripts:v,businessRules:h,dependents:d,references:g,blastRadius:N,tokens:{contextTokens:S,fullGraphTokens:i,tokensSaved:y,reductionPercent:A}}}function Ue(t,e,n){return t.nodes.find(r=>r.name===n&&e.getPluginName(r.table)==="TablePlugin"&&!e.isChild(r.table))??t.nodes.find(r=>r.name===n)}function qe(t,e,n){if(!n)return[];let r=t.edges.filter(s=>s.type==="composition"&&s.from===n.id).map(s=>s.to),o=[];for(let s of r){let a=t.nodes.find(c=>c.id===s);if(!a||!e.isChild(a.table))continue;let l=O(a,"element"),i=O(a,"internal_type");!l||!i||i!=="collection"&&o.push({name:l,type:i,label:O(a,"column_label")||l,referenceTable:i==="reference"?O(a,"reference"):void 0})}return o.sort((s,a)=>s.name.localeCompare(a.name))}function ze(t,e,n,r){let o=n?.id??`platform:${r}`,s=t.edges.filter(c=>c.type==="dependency"&&(c.to===o||c.targetTable===r)),a=[],l=[];for(let c of s){let m=t.nodes.find(g=>g.id===c.from);if(!m||!m.source.file)continue;let v=e.getPluginName(m.table),h=Ze(O(m,"type"),v),d={name:m.name,event:h,field:O(m,"field"),order:Be(m,"order")??100,active:He(m,"active")!==!1,file:m.source.file};v==="BusinessRulePlugin"?l.push(d):a.push(d)}let i=(c,m)=>c.order-m.order||c.name.localeCompare(m.name);return{scripts:a.sort(i),businessRules:l.sort(i)}}function ve(t,e){let n=new Set(t.nodes.filter(s=>s.name===e).map(s=>s.id));n.add(`platform:${e}`);let r=t.edges.filter(s=>s.type==="schema_relationship"&&(n.has(s.to)||s.targetTable===e)),o=new Map;for(let s of r){let a=t.nodes.find(c=>c.id===s.from);if(!a)continue;let l=a.name;if(l===e)continue;let i=s.via||O(a,"element")||"unknown";if(!o.has(l)){let c=t.edges.filter(m=>m.type==="dependency"&&m.targetTable===l).length;o.set(l,{table:l,via:i,type:"FK",artifactCount:c})}}return[...o.values()].sort((s,a)=>s.table.localeCompare(a.table))}function Ye(t,e){if(!e)return[];let n=new Set(t.edges.filter(o=>o.type==="composition"&&o.from===e.id).map(o=>o.to)),r=[];for(let o of t.edges){if(o.type!=="schema_relationship"||!n.has(o.from))continue;let s=t.nodes.find(l=>l.id===o.from);if(!s)continue;let a=o.to.startsWith("platform:")?o.to.split(":")[1]:t.nodes.find(l=>l.id===o.to)?.name??o.to;r.push({field:O(s,"element")||o.via||"unknown",targetTable:a})}return r.sort((o,s)=>o.field.localeCompare(s.field))}var Xe={onload:"onLoad",onchange:"onChange",onsubmit:"onSubmit",before:"before",after:"after",async:"async",load:"onLoad",change:"onChange",submit:"onSubmit"};function Ze(t,e){return t?Xe[t.toLowerCase()]??t:e==="BusinessRulePlugin"?"before":"action"}async function U(t){let e=()=>!0,n=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr);process.stdout.write=e,process.stderr.write=e;try{return await t()}finally{process.stdout.write=n,process.stderr.write=r}}function L(t){return t.toLocaleString()}var Ie=new Ee.Command("context").description("Extract minimal AI-optimized context for a target table").argument("<target>","Target table name (e.g., account, incident)").option("--depth <n>","Dependency depth (0=target only, 1=direct, 2=two hops)","1").option("--no-fields","Exclude field details").option("--no-scripts","Exclude attached scripts").option("--no-blast","Exclude blast radius summary").option("-f, --format <type>","Output format: table, json","table").option("--show-metrics","Show per-section token breakdown and the raw JSON payload sent to an LLM").option("--no-banner","Skip banner (inherited)").action(async(t,e)=>{let n=e.format==="json",r;try{let o=process.cwd();n||(r=(0,ee.default)("Detecting ServiceNow SDK...").start());let s=await C(o);(!s.found||!s.sdkPath)&&(r&&r.fail("SDK not found"),n?console.log(JSON.stringify({error:s.error,code:"SDK_NOT_FOUND"})):console.error(u.default.red(`
25
+ \u274C Error:`),s.error),process.exit(1)),r&&r.succeed(`\u2705 SDK detected at ${u.default.gray(s.sdkPath)}`);let a=V(s.sdkPath),l=new a.sdkapi.Project({rootDir:o});n||(r=(0,ee.default)("Building dependency graph...").start());let i=await U(()=>M(l,a.registry));r&&(r.succeed(`Graph built: ${u.default.cyan(i.nodes.length)} nodes, ${u.default.cyan(i.edges.length)} edges`),console.log());let c=Ne(i,a.registry,t,{depth:parseInt(e.depth,10)||1,includeFields:e.fields!==!1,includeScripts:e.scripts!==!1,includeBlastRadius:e.blast!==!1});if(n){console.log(JSON.stringify(c,null,2));return}if(console.log(u.default.bold(`\u{1F9E0} CONTEXT FOR: ${u.default.white(t)}`)),console.log(u.default.gray("\u2550".repeat(52))),console.log(),console.log(`${u.default.gray("Context ID:")} ${u.default.cyan(c.contextId)}`),console.log(),c.fields.length>0){console.log(u.default.bold(`\u{1F4CB} Fields (${c.fields.length})`));let d=new G.default({head:[u.default.white("Field"),u.default.white("Type"),u.default.white("References")],style:{head:[],border:[]}});for(let g of c.fields)d.push([g.name,g.type,g.referenceTable??""]);console.log(d.toString()),console.log()}let m=[...c.scripts,...c.businessRules];if(m.length>0){console.log(u.default.bold(`\u26A1 Attached scripts (${m.length})`));let d=new G.default({head:[u.default.white("Script"),u.default.white("Event"),u.default.white("Field"),u.default.white("Order")],style:{head:[],border:[]}});for(let g of m)d.push([g.name,g.event,g.field??"",String(g.order)]);console.log(d.toString()),console.log()}if(c.dependents.length>0){console.log(u.default.bold(`\u{1F517} Dependents (${c.dependents.length} table${c.dependents.length!==1?"s":""} reference ${t})`));let d=new G.default({head:[u.default.white("Table"),u.default.white("Via field"),u.default.white("Type")],style:{head:[],border:[]}});for(let g of c.dependents)d.push([g.table,g.via,g.type]);console.log(d.toString()),console.log()}if(c.references.length>0){console.log(u.default.bold(`\u2197 References from ${t} (${c.references.length})`));let d=new G.default({head:[u.default.white("Field"),u.default.white("Points to")],style:{head:[],border:[]}});for(let g of c.references)d.push([g.field,g.targetTable]);console.log(d.toString()),console.log()}if(c.blastRadius){let{direct:d,transitive:g,risk:w}=c.blastRadius,N=w==="HIGH"?u.default.red:w==="MEDIUM"?u.default.yellow:u.default.green;console.log(`\u{1F525} Blast radius: ${d} direct, ${g} transitive \u2014 ${N(w)}`),console.log()}let{tokens:v}=c;console.log(u.default.bold("\u{1F4CA} TOKEN METRICS"));let h=new G.default({style:{head:[],border:[]}});if(h.push([u.default.gray("This context"),u.default.white(`${L(v.contextTokens)} tokens`)],[u.default.gray("Full graph"),u.default.white(`${L(v.fullGraphTokens)} tokens`)],[u.default.gray("Saved"),u.default.green(`${L(v.tokensSaved)} tokens`)],[u.default.gray("Reduction"),u.default.green(`${v.reductionPercent}%`)]),console.log(h.toString()),e.showMetrics){let d={target:c.target,fields:c.fields,scripts:c.scripts,businessRules:c.businessRules,dependents:c.dependents,references:c.references,blastRadius:c.blastRadius},g=[{label:"target",value:c.target},{label:"fields",value:c.fields},{label:"scripts",value:c.scripts},{label:"businessRules",value:c.businessRules},{label:"dependents",value:c.dependents},{label:"references",value:c.references},{label:"blastRadius",value:c.blastRadius}];console.log(),console.log(u.default.bold("\u{1F52C} METRICS BREAKDOWN (chars \xF7 4 \u2248 tokens)")),console.log(u.default.gray("\u2550".repeat(52)));let w=new G.default({head:[u.default.white("Section"),u.default.white("Chars"),u.default.white("~Tokens"),u.default.white("% of context")],style:{head:[],border:[]}}),N=JSON.stringify(d).length;for(let{label:p,value:S}of g){let y=JSON.stringify(S).length,A=Math.ceil(y/4),E=N>0?Math.round(y/N*100):0;w.push([u.default.gray(p),L(y),L(A),`${E}%`])}w.push([u.default.white("TOTAL"),u.default.white(L(N)),u.default.white(L(v.contextTokens)),u.default.white("100%")]),console.log(w.toString()),console.log(),console.log(u.default.bold("\u{1F4E6} RAW PAYLOAD (what an LLM receives)")),console.log(u.default.gray("\u2550".repeat(52))),console.log(u.default.gray("Formula: Math.ceil(JSON.stringify(payload).length / 4)")),console.log(u.default.gray(` Math.ceil(${L(N)} / 4) = ${L(v.contextTokens)} tokens`)),console.log(),console.log(JSON.stringify(d,null,2))}}catch(o){let s=o instanceof Error?o.message:"Unexpected error";n?console.log(JSON.stringify({error:s,code:"CONTEXT_FAILED"})):console.error(u.default.red(`
26
+ \u274C ${s}`)),process.exit(1)}});var De=require("commander"),k=T(require("chalk")),te=T(require("ora"));function q(t,e){let n=t.attributes[e];return typeof n=="string"?n:void 0}function Qe(t,e){let n=t.attributes[e];if(typeof n=="number")return n;if(typeof n=="string"&&n.trim()!==""){let r=Number(n);return Number.isNaN(r)?void 0:r}}function et(t,e){let n=t.length,r=e.length,o=Array.from({length:n+1},(s,a)=>Array.from({length:r+1},(l,i)=>a===0?i:i===0?a:0));for(let s=1;s<=n;s++)for(let a=1;a<=r;a++)o[s][a]=t[s-1]===e[a-1]?o[s-1][a-1]:1+Math.min(o[s-1][a-1],o[s-1][a],o[s][a-1]);return o[n][r]}function tt(t,e){let n,r=1/0;for(let o of e){let s=et(t.toLowerCase(),o.toLowerCase());s<r&&(r=s,n=o)}return n&&r<=Math.max(2,Math.floor(t.length*.4))?n:void 0}function nt(t,e){let n=[],r=new Map;for(let o of t.nodes){if(!e.isChild(o.table))continue;let s=q(o,"element");if(!s)continue;let a=t.nodes.find(i=>t.edges.some(c=>c.type==="composition"&&c.from===i.id&&c.to===o.id))?.name;if(!a)continue;let l=r.get(a)??[];l.push(s),r.set(a,l)}for(let o of t.edges){if(o.type!=="dependency")continue;let s=t.nodes.find(m=>m.id===o.from);if(!s?.source.file)continue;let a=q(s,"field");if(!a)continue;let l=o.targetTable??t.nodes.find(m=>m.id===o.to)?.name;if(!l)continue;let i=r.get(l);if(!i||i.includes(a))continue;let c=tt(a,i);n.push({severity:"error",code:"FG-001",message:`"${s.name}" references field "${a}" on table "${l}" but no such field exists`,artifact:s.name,table:l,file:s.source.file,suggestion:c?`Did you mean "${c}"?`:void 0})}return n}function ot(t){let e=[],n=new Map;for(let r of t.edges){if(r.type!=="dependency")continue;let o=t.nodes.find(c=>c.id===r.from);if(!o?.source.file)continue;let s=r.targetTable??t.nodes.find(c=>c.id===r.to)?.name??"unknown",a=q(o,"type")??"unknown",l=`${s}::${a}`,i=n.get(l)??[];i.push(o),n.set(l,i)}for(let[r,o]of n){let s=new Map;for(let a of o){let l=Qe(a,"order")??100,i=s.get(l)??[];i.push(a),s.set(l,i)}for(let[a,l]of s){if(l.length<2)continue;let[i,c]=r.split("::"),m=l.map(v=>v.name).join(" and ");e.push({severity:"warning",code:"FG-002",message:`${m} (order: ${a}) both fire "${c}" on "${i}" \u2014 nondeterministic execution order`,table:i,file:l[0].source.file})}}return e}function rt(t){let e=[],n=new Set(t.nodes.map(r=>r.id));for(let r of t.edges){if(r.type!=="schema_relationship"||r.to.startsWith("platform:")||n.has(r.to))continue;let o=t.nodes.find(l=>l.id===r.from);if(!o)continue;let s=r.to,a=q(o,"element");e.push({severity:"warning",code:"FG-003",message:`Field "${a??r.via??"unknown"}" on "${o.name}" references "${s}" but this table is not in the local project or known platform tables`,artifact:o.name,file:o.source.file})}return e}function st(t){let e=[],n=new Map;for(let r of t.nodes){let o=`${r.table}::${r.name}`,s=n.get(o)??[];s.push(r),n.set(o,s)}for(let[r,o]of n){if(o.length<2)continue;let[s,a]=r.split("::");e.push({severity:"warning",code:"FG-004",message:`Two artifacts named "${a}" exist on "${s}" \u2014 the last defined wins, the other is shadowed`,artifact:a,table:s,file:o[0].source.file})}return e}function it(t,e){let n=[],r=t.nodes.filter(o=>o.action==="DELETE");for(let o of r){let a=j(t,o.name,5).hops.filter(i=>i.node.action!=="DELETE"&&i.node.source.file);if(a.length===0)continue;let l=e.getDisplayGroupName(o.table).toLowerCase();n.push({severity:"error",code:"FG-005",message:`"${o.name}" (${l}) is marked DELETE but ${a.length} active artifact(s) still reference it \u2014 these will break after deployment`,artifact:o.name,table:o.table,file:o.source.file,suggestion:`Review: ${a.slice(0,3).map(i=>i.node.name).join(", ")}${a.length>3?" ...":""}`})}return n}function ke(t,e){let n=[...nt(t,e),...ot(t),...rt(t),...st(t),...it(t,e)],r={error:0,warning:1,info:2};n.sort((l,i)=>r[l.severity]-r[i.severity]);let o=n.filter(l=>l.severity==="error"),s=n.filter(l=>l.severity==="warning"),a=n.filter(l=>l.severity==="info");return{valid:o.length===0,issueCount:n.length,errors:o,warnings:s,info:a}}var at={error:k.default.red(" ERROR"),warning:k.default.yellow("WARNING"),info:k.default.cyan(" INFO")};function ct(t){let e=[`${at[t.severity]} [${t.code}] ${t.message}`];return t.file&&e.push(k.default.gray(` File: ${t.file}`)),t.suggestion&&e.push(k.default.cyan(` Suggestion: ${t.suggestion}`)),e.join(`
27
+ `)}function lt(t){return t==="error"||t==="warning"||t==="info"}function dt(t,e){switch(e){case"error":return t==="error";case"warning":return t==="error"||t==="warning";case"info":return!0;default:{let n=e;throw new Error(`Unhandled severity: ${JSON.stringify(n)}`)}}}var Re=new De.Command("validate").description("Run structural validation on the current project").option("-f, --format <type>","Output format: table, json","table").option("--severity <level>","Minimum severity to show: error, warning, info","info").option("--no-banner","Skip banner (inherited)").action(async t=>{let e=t.format==="json",n;try{let r=process.cwd();e||(n=(0,te.default)("Detecting ServiceNow SDK...").start());let o=await C(r);(!o.found||!o.sdkPath)&&(n&&n.fail("SDK not found"),e?console.log(JSON.stringify({error:o.error,code:"SDK_NOT_FOUND"})):console.error(k.default.red(`
28
+ \u274C Error:`),o.error),process.exit(1)),n&&n.succeed(`\u2705 SDK detected at ${k.default.gray(o.sdkPath)}`);let s=V(o.sdkPath),a=new s.sdkapi.Project({rootDir:r});e||(n=(0,te.default)("Building dependency graph...").start());let l=await U(()=>M(a,s.registry));n&&(n.succeed(`Graph built: ${k.default.cyan(l.nodes.length)} nodes, ${k.default.cyan(l.edges.length)} edges`),console.log());let i=ke(l,s.registry);if(e){console.log(JSON.stringify(i,null,2)),process.exit(i.valid?0:1);return}console.log(k.default.bold("\u{1F50D} STRUCTURAL VALIDATION")),console.log(k.default.gray("\u2550".repeat(44))),console.log();let c=lt(t.severity)?t.severity:"info",m=d=>dt(d.severity,c),v=i.errors.filter(m).concat(i.warnings.filter(m)).concat(i.info.filter(m));if(v.length===0)console.log(k.default.green(" \u2705 No issues found"));else for(let d of v)console.log(ct(d)),console.log();console.log(k.default.gray("\u2550".repeat(44)));let h=[i.errors.length?k.default.red(`${i.errors.length} error${i.errors.length!==1?"s":""}`):null,i.warnings.length?k.default.yellow(`${i.warnings.length} warning${i.warnings.length!==1?"s":""}`):null,i.info.length?k.default.cyan(`${i.info.length} info`):null].filter(Boolean);console.log(h.length?h.join(", "):k.default.green("0 issues")),process.exit(i.valid?0:1)}catch(r){let o=r instanceof Error?r.message:"Unexpected error";e?console.log(JSON.stringify({error:o,code:"VALIDATION_FAILED"})):console.error(k.default.red(`
29
+ \u274C ${o}`)),process.exit(1)}});var z=T(require("chalk"));function Ae(){console.log(z.default.cyan(`
25
30
  \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557
26
31
  \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551
27
32
  \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551
28
33
  \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551
29
34
  \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551
30
35
  \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D
31
- `)),console.log(j.default.gray(" Dependency visibility and impact analysis for ServiceNow Fluent SDK projects")),console.log(j.default.gray(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
32
- `))}var Te=ge.default.join(__dirname,"../../package.json"),Ne=fe.default.readFileSync(Te,"utf-8"),W=JSON.parse(Ne),x=new pe.Command,De=(()=>{let o=process.argv;for(let e=0;e<o.length;e++){let t=o[e];if(t==="--format"||t==="-f")return o[e+1]==="json";if(t==="--format=json"||t==="-f=json")return!0}return!1})(),ke=!process.argv.includes("--no-banner")&&!process.argv.includes("--quiet")&&!De;ke&&de();x.name(W.name).description(W.description).version(W.version).option("--no-banner","Suppress ASCII banner");x.addCommand(se);x.addCommand(le);process.argv.length===2&&x.help();x.parse(process.argv);
36
+ `)),console.log(z.default.gray(" Dependency visibility and impact analysis for ServiceNow Fluent SDK projects")),console.log(z.default.gray(` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
37
+ `))}var ft=Le.default.join(__dirname,"../../package.json"),gt=Ce.default.readFileSync(ft,"utf-8"),ne=JSON.parse(gt),P=new xe.Command,pt=(()=>{let t=process.argv;for(let e=0;e<t.length;e++){let n=t[e];if(n==="--format"||n==="-f")return t[e+1]==="json";if(n==="--format=json"||n==="-f=json")return!0}return!1})(),ut=!process.argv.includes("--no-banner")&&!process.argv.includes("--quiet")&&!pt;ut&&Ae();P.name(ne.name).description(ne.description).version(ne.version).option("--no-banner","Suppress ASCII banner");P.addCommand(be);P.addCommand(Se);P.addCommand(Ie);P.addCommand(Re);process.argv.length===2&&P.help();P.parse(process.argv);
package/dist/index.d.ts CHANGED
@@ -105,6 +105,9 @@ interface TraversalResult {
105
105
  * - edge.to === currentNodeId (exact physical node)
106
106
  * - edge.targetTable === currentNodeName (canonical name — reliable
107
107
  * for platform nodes and catches mis-targeted edges)
108
+ * This lookup is served by an adjacency index (buildIncomingEdgeIndex)
109
+ * built once up front, rather than re-scanning all of graph.edges on
110
+ * every BFS step.
108
111
  *
109
112
  * 4. ALL edge types are traversed. Display filtering is the caller's job.
110
113
  *
@@ -114,4 +117,120 @@ interface TraversalResult {
114
117
  */
115
118
  declare function traverseBlastRadius(graph: LineageGraph, targetTableName: string, maxDepth: number): TraversalResult;
116
119
 
117
- export { type ArtifactEdge, type ArtifactNode, type GraphMetadata, type LineageGraph, type TraversalNode, type TraversalResult, extractLineageGraph, traverseBlastRadius };
120
+ interface ImpactedArtifact {
121
+ name: string;
122
+ table: string;
123
+ type: string;
124
+ file: string;
125
+ reason: string;
126
+ state: string;
127
+ hop: number;
128
+ }
129
+ interface BlastRadiusResult {
130
+ target: string;
131
+ isBaseTable: boolean;
132
+ direct: ImpactedArtifact[];
133
+ transitive: ImpactedArtifact[];
134
+ totalImpacted: number;
135
+ maxDepth: number;
136
+ riskLevel: 'LOW' | 'MEDIUM' | 'HIGH';
137
+ }
138
+ interface ContextOptions {
139
+ depth?: number;
140
+ includeFields?: boolean;
141
+ includeScripts?: boolean;
142
+ includeBlastRadius?: boolean;
143
+ }
144
+ interface FieldInfo {
145
+ name: string;
146
+ type: string;
147
+ label: string;
148
+ referenceTable?: string;
149
+ }
150
+ /**
151
+ * The known Fluent script events, plus an escape hatch for whatever the SDK
152
+ * hands back that isn't one of them (the "LiteralUnion" pattern: keeps
153
+ * autocomplete/type-checking for the known cases without discarding an
154
+ * unrecognized runtime value by coercing it to one of the literals).
155
+ */
156
+ type KnownScriptEventType = 'onLoad' | 'onChange' | 'onSubmit' | 'before' | 'after' | 'async' | 'action';
157
+ type ScriptEventType = KnownScriptEventType | (string & {});
158
+ interface ScriptInfo {
159
+ name: string;
160
+ event: ScriptEventType;
161
+ field?: string;
162
+ order: number;
163
+ active: boolean;
164
+ file: string;
165
+ }
166
+ interface DependentInfo {
167
+ table: string;
168
+ via: string;
169
+ type: string;
170
+ artifactCount: number;
171
+ }
172
+ interface ReferenceInfo {
173
+ field: string;
174
+ targetTable: string;
175
+ }
176
+ interface ContextResult {
177
+ /** Deterministic sha256 fingerprint (12 hex chars) of this context snapshot.
178
+ * Consumers log this alongside generated code to create a provenance trail. */
179
+ contextId: string;
180
+ target: string;
181
+ fields: FieldInfo[];
182
+ scripts: ScriptInfo[];
183
+ businessRules: ScriptInfo[];
184
+ dependents: DependentInfo[];
185
+ references: ReferenceInfo[];
186
+ blastRadius?: {
187
+ direct: number;
188
+ transitive: number;
189
+ risk: string;
190
+ };
191
+ tokens: {
192
+ contextTokens: number;
193
+ fullGraphTokens: number;
194
+ tokensSaved: number;
195
+ reductionPercent: number;
196
+ };
197
+ }
198
+ type ValidationSeverity = 'error' | 'warning' | 'info';
199
+ /** Each code maps to exactly one structural check in analysis/validate.ts. */
200
+ type ValidationCode = 'FG-001' | 'FG-002' | 'FG-003' | 'FG-004' | 'FG-005';
201
+ interface ValidationIssue {
202
+ severity: ValidationSeverity;
203
+ code: ValidationCode;
204
+ message: string;
205
+ artifact?: string;
206
+ table?: string;
207
+ file?: string;
208
+ suggestion?: string;
209
+ }
210
+ interface ValidationResult {
211
+ valid: boolean;
212
+ issueCount: number;
213
+ errors: ValidationIssue[];
214
+ warnings: ValidationIssue[];
215
+ info: ValidationIssue[];
216
+ }
217
+ interface ProjectSummary {
218
+ totalArtifacts: number;
219
+ totalRelationships: number;
220
+ artifactsByPlugin: Record<string, number>;
221
+ platformDependencies: number;
222
+ internalReferences: number;
223
+ complexity: number;
224
+ }
225
+
226
+ /**
227
+ * Extract a minimal subgraph optimised for LLM consumption.
228
+ *
229
+ * No sys_ids, no SDK internals — just the structural facts an agent needs
230
+ * to understand what a table looks like and what will break if it changes.
231
+ */
232
+ declare function selectContext(graph: LineageGraph, registry: ArtifactTypeRegistry, targetTableName: string, options?: ContextOptions): ContextResult;
233
+
234
+ declare function validateGraph(graph: LineageGraph, registry: ArtifactTypeRegistry): ValidationResult;
235
+
236
+ export { type ArtifactEdge, type ArtifactNode, type BlastRadiusResult, type ContextOptions, type ContextResult, type DependentInfo, type FieldInfo, type GraphMetadata, type ImpactedArtifact, type LineageGraph, type ProjectSummary, type ReferenceInfo, type ScriptEventType, type ScriptInfo, type TraversalNode, type TraversalResult, type ValidationCode, type ValidationIssue, type ValidationResult, type ValidationSeverity, extractLineageGraph, selectContext, traverseBlastRadius, validateGraph };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var k=Object.create;var A=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var M=Object.getPrototypeOf,L=Object.prototype.hasOwnProperty;var D=(t,e)=>{for(var n in e)A(t,n,{get:e[n],enumerable:!0})},x=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of F(e))!L.call(t,r)&&r!==n&&A(t,r,{get:()=>e[r],enumerable:!(o=G(e,r))||o.enumerable});return t};var O=(t,e,n)=>(n=t!=null?k(M(t)):{},x(e||!t||!t.__esModule?A(n,"default",{value:t,enumerable:!0}):n,t)),q=t=>x(A({},"__esModule",{value:!0}),t);var z={};D(z,{extractLineageGraph:()=>$,traverseBlastRadius:()=>_});module.exports=q(z);var R=O(require("path"));function C(t){let e={};for(let[n,o]of Object.entries(t))if(o)try{let r=typeof o.getValue=="function"?o.getValue():o;(typeof r!="object"||r===null||Array.isArray(r))&&(e[n]=r)}catch{continue}return e}function V(t,e){let n=[];for(let o of t.query()){let r=o.setProperties||{},i=o.getId()?.getValue()?.toString()||"",c=o.getTable()?.toString()||"",f=r.name?.getValue?.()?.toString()||r.label?.getValue?.()?.toString()||r.sys_name?.getValue?.()?.toString()||i,m=o.getOriginalFilePath()?.toString()||"",y=m?R.default.relative(e,m):"";n.push({id:i,table:c,action:o.getAction(),name:f,attributes:C(r),source:{file:y,type:o.getOriginalSource()?.getKindName?.()||"SourceFile"}})}return n}function E(t,e,n){let o=[];for(let r of t.query()){let i=r.getId()?.getValue()?.toString(),c=r.setProperties||{},f=r.getTable()?.toString(),m=c.internal_type?.getValue?.()?.toString(),y=c.reference?.getValue?.()?.toString(),b=c.element?.getValue?.()?.toString();if(m==="reference"&&y){let g=e.find(s=>s.name===y);o.push({from:i,to:g?.id||`platform:${y}`,via:b||"reference",type:"schema_relationship"})}let u=c.table?.getValue?.()?.toString()||c.collection?.getValue?.()?.toString();if(u){let g=e.find(s=>s.name===u);o.push({from:i,to:g?.id||`platform:${u}`,via:c.table?"table":"collection",type:"dependency",targetTable:u})}if(n.getPluginName(f)==="AclPlugin"){let g=c.name?.getValue?.()?.toString();if(g&&g.includes(".")){let[s]=g.split("."),d=e.find(a=>a.name===s);o.push({from:i,to:d?.id||`platform:${s}`,via:"name",type:"dependency",targetTable:s})}}for(let[g,s]of Object.entries(c))if(Array.isArray(s))s.forEach(d=>{if(d&&d.constructor?.name==="RecordId"){let a=d.getValue?.()?.toString(),p=d.getTable?.()?.toString();a&&n.getPluginName(p)!=="TablePlugin"&&o.push({from:i,to:a,via:g,type:"dependency",targetTable:p})}});else if(s&&s.constructor?.name==="RecordId"){let d=s.getValue?.()?.toString(),a=s.getTable?.()?.toString();d&&n.getPluginName(a)!=="TablePlugin"&&o.push({from:i,to:d,via:g,type:"dependency",targetTable:a})}else if(s&&s.constructor?.name==="Record"){let d=s.getId?.()?.getValue?.()?.toString();if(d){let a=e.find(p=>p.id===d);a&&n.getPluginName(a.table)!=="TablePlugin"&&o.push({from:i,to:d,via:g,type:"dependency",targetTable:a.table})}}}return o}function T(t){let e=[];for(let n of t.query()){let o=n.getId()?.getValue()?.toString(),r=n.getRelated?.()||[];for(let i of r){let c=i.getId()?.getValue()?.toString();c&&o&&e.push({from:o,to:c,type:"composition"})}}return e}function P(t,e,n,o){let r=t.getConfig()||{},i=t.getPackage()||{};return{projectRoot:t.getRootDir()||process.cwd(),scopeId:r.scopeId||"unknown",scopeName:r.scope||"Global",recordCount:e,referenceCount:n,compositionCount:o,generatedAt:new Date().toISOString(),sdkVersion:i.dependencies?.["@servicenow/sdk"]||i.devDependencies?.["@servicenow/sdk"]||"unknown"}}async function $(t,e){let n=await t.getRecords(),o=t.getRootDir(),r=V(n,o),i=E(n,r,e),c=T(n),f=[...i,...c];return{metadata:P(t,n.query().length,i.length,c.length),nodes:r,edges:f}}function H(t){let e=new Map;for(let n of t.nodes){let o=e.get(n.name)??[];o.push(n.id),e.set(n.name,o)}return e}function _(t,e,n){let o=Math.min(n,25),r=H(t),i=t.nodes.filter(a=>a.name===e),c=i[0]??null,f=new Set,m=`platform:${e}`;f.add(m);for(let a of i)f.add(a.id);let b=[...new Set([m,...i.map(a=>a.id)])].map(a=>({nodeId:a,nodeName:e,hop:0,path:[e]})),u=[],g=new Set([e]),s=0,d=0;for(;b.length>0;){let a=[];for(let p of b){if(p.hop>=o)continue;let w=t.edges.filter(N=>N.to===p.nodeId||p.nodeName.length>0&&N.targetTable===p.nodeName);for(let N of w){let l=t.nodes.find(h=>h.id===N.from);if(!l)continue;if(f.has(l.id)){d++;continue}f.add(l.id);let I=r.get(l.name)??[];for(let h of I)f.has(h)||f.add(h);let S=p.hop+1,v=[...p.path,l.name];g.has(l.name)||(g.add(l.name),u.push({node:l,hop:S,path:v,viaEdge:N}));for(let h of I)a.push({nodeId:h,nodeName:l.name,hop:S,path:v});S>s&&(s=S)}}b=a}return u.sort((a,p)=>a.hop-p.hop||a.node.name.localeCompare(p.node.name)),{target:c,targetName:e,hops:u,maxHopReached:s,cyclesPrevented:d}}0&&(module.exports={extractLineageGraph,traverseBlastRadius});
1
+ "use strict";var H=Object.create;var V=Object.defineProperty;var W=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var j=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var U=(t,i)=>{for(var n in i)V(t,n,{get:i[n],enumerable:!0})},L=(t,i,n,o)=>{if(i&&typeof i=="object"||typeof i=="function")for(let e of q(i))!J.call(t,e)&&e!==n&&V(t,e,{get:()=>i[e],enumerable:!(o=W(i,e))||o.enumerable});return t};var G=(t,i,n)=>(n=t!=null?H(j(t)):{},L(i||!t||!t.__esModule?V(n,"default",{value:t,enumerable:!0}):n,t)),z=t=>L(V({},"__esModule",{value:!0}),t);var be={};U(be,{extractLineageGraph:()=>Q,selectContext:()=>B,traverseBlastRadius:()=>w,validateGraph:()=>K});module.exports=z(be);var F=G(require("path"));function Y(t){let i={};for(let[n,o]of Object.entries(t))if(o)try{let e=typeof o.getValue=="function"?o.getValue():o;(typeof e!="object"||e===null||Array.isArray(e))&&(i[n]=e)}catch{continue}return i}function C(t,i){let n=[];for(let o of t.query()){let e=o.setProperties||{},r=o.getId()?.getValue()?.toString()||"",s=o.getTable()?.toString()||"",a=e.name?.getValue?.()?.toString()||e.label?.getValue?.()?.toString()||e.sys_name?.getValue?.()?.toString()||r,c=o.getOriginalFilePath()?.toString()||"",d=c?F.default.relative(i,c):"";n.push({id:r,table:s,action:o.getAction(),name:a,attributes:Y(e),source:{file:d,type:o.getOriginalSource()?.getKindName?.()||"SourceFile"}})}return n}function M(t,i,n){let o=[];for(let e of t.query()){let r=e.getId()?.getValue()?.toString(),s=e.setProperties||{},a=e.getTable()?.toString(),c=s.internal_type?.getValue?.()?.toString(),d=s.reference?.getValue?.()?.toString(),l=s.element?.getValue?.()?.toString();if(c==="reference"&&d){let g=i.find(f=>f.name===d);o.push({from:r,to:g?.id||`platform:${d}`,via:l||"reference",type:"schema_relationship"})}let y=s.table?.getValue?.()?.toString()||s.collection?.getValue?.()?.toString();if(y){let g=i.find(f=>f.name===y);o.push({from:r,to:g?.id||`platform:${y}`,via:s.table?"table":"collection",type:"dependency",targetTable:y})}if(n.getPluginName(a)==="AclPlugin"){let g=s.name?.getValue?.()?.toString();if(g&&g.includes(".")){let[f]=g.split("."),u=i.find(m=>m.name===f);o.push({from:r,to:u?.id||`platform:${f}`,via:"name",type:"dependency",targetTable:f})}}for(let[g,f]of Object.entries(s))if(Array.isArray(f))f.forEach(u=>{if(u&&u.constructor?.name==="RecordId"){let m=u.getValue?.()?.toString(),b=u.getTable?.()?.toString();m&&n.getPluginName(b)!=="TablePlugin"&&o.push({from:r,to:m,via:g,type:"dependency",targetTable:b})}});else if(f&&f.constructor?.name==="RecordId"){let u=f.getValue?.()?.toString(),m=f.getTable?.()?.toString();u&&n.getPluginName(m)!=="TablePlugin"&&o.push({from:r,to:u,via:g,type:"dependency",targetTable:m})}else if(f&&f.constructor?.name==="Record"){let u=f.getId?.()?.getValue?.()?.toString();if(u){let m=i.find(b=>b.id===u);m&&n.getPluginName(m.table)!=="TablePlugin"&&o.push({from:r,to:u,via:g,type:"dependency",targetTable:m.table})}}}return o}function $(t){let i=[];for(let n of t.query()){let o=n.getId()?.getValue()?.toString(),e=n.getRelated?.()||[];for(let r of e){let s=r.getId()?.getValue()?.toString();s&&o&&i.push({from:o,to:s,type:"composition"})}}return i}function P(t,i,n,o){let e=t.getConfig()||{},r=t.getPackage()||{};return{projectRoot:t.getRootDir()||process.cwd(),scopeId:e.scopeId||"unknown",scopeName:e.scope||"Global",recordCount:i,referenceCount:n,compositionCount:o,generatedAt:new Date().toISOString(),sdkVersion:r.dependencies?.["@servicenow/sdk"]||r.devDependencies?.["@servicenow/sdk"]||"unknown"}}async function Q(t,i){let n=await t.getRecords(),o=t.getRootDir(),e=C(n,o),r=M(n,e,i),s=$(n),a=[...r,...s];return{metadata:P(t,n.query().length,r.length,s.length),nodes:e,edges:a}}function X(t){let i=new Map;for(let n of t.nodes){let o=i.get(n.name)??[];o.push(n.id),i.set(n.name,o)}return i}function Z(t){let i=new Map,n=new Map,o=new Map;return t.edges.forEach((e,r)=>{o.set(e,r);let s=i.get(e.to)??[];if(s.push(e),i.set(e.to,s),e.targetTable){let a=n.get(e.targetTable)??[];a.push(e),n.set(e.targetTable,a)}}),{byNodeId:i,byTargetTable:n,originalIndexByEdge:o}}function ee(t,i,n){let{byNodeId:o,byTargetTable:e,originalIndexByEdge:r}=t,s=new Set(o.get(i)??[]);if(n.length>0)for(let a of e.get(n)??[])s.add(a);return[...s].sort((a,c)=>r.get(a)-r.get(c))}function w(t,i,n){let o=Math.min(n,25),e=X(t),r=Z(t),s=new Map(t.nodes.map(p=>[p.id,p])),a=t.nodes.filter(p=>p.name===i),c=a[0]??null,d=new Set,l=`platform:${i}`;d.add(l);for(let p of a)d.add(p.id);let g=[...new Set([l,...a.map(p=>p.id)])].map(p=>({nodeId:p,nodeName:i,hop:0,path:[i]})),f=[],u=new Set([i]),m=0,b=0;for(;g.length>0;){let p=[];for(let h of g){if(h.hop>=o)continue;let R=ee(r,h.nodeId,h.nodeName);for(let x of R){let I=s.get(x.from);if(!I)continue;if(d.has(I.id)){b++;continue}d.add(I.id);let N=e.get(I.name)??[];for(let E of N)d.has(E)||d.add(E);let v=h.hop+1,A=[...h.path,I.name];u.has(I.name)||(u.add(I.name),f.push({node:I,hop:v,path:A,viaEdge:x}));for(let E of N)p.push({nodeId:E,nodeName:I.name,hop:v,path:A});v>m&&(m=v)}}g=p}return f.sort((p,h)=>p.hop-h.hop||p.node.name.localeCompare(h.node.name)),{target:c,targetName:i,hops:f,maxHopReached:m,cyclesPrevented:b}}var O=G(require("crypto"));function T(t,i){let n=t.attributes[i];return typeof n=="string"?n:void 0}function te(t,i){let n=t.attributes[i];if(typeof n=="number")return n;if(typeof n=="string"&&n.trim()!==""){let o=Number(n);return Number.isNaN(o)?void 0:o}}function ne(t,i){let n=t.attributes[i];return typeof n=="boolean"?n:void 0}function B(t,i,n,o={}){let{depth:e=1,includeFields:r=!0,includeScripts:s=!0,includeBlastRadius:a=!0}=o,c=Math.ceil(JSON.stringify(t).length/4),d=oe(t,i,n),l=r?ie(t,i,d):[],{scripts:y,businessRules:g}=s?re(t,i,d,n):{scripts:[],businessRules:[]},f=D(t,n),u=se(t,d),m=new Set(f.map(N=>N.table));if(e>=2)for(let N of[...f]){let v=D(t,N.table);for(let A of v)m.has(A.table)||(m.add(A.table),f.push(A))}let b;if(a){let N=w(t,n,10),v=N.hops.filter(S=>S.hop===1&&S.node.source.file).length,A=N.hops.filter(S=>S.hop>1&&S.node.source.file).length,E=v+A,_=E<3?"LOW":E<8?"MEDIUM":"HIGH";b={direct:v,transitive:A,risk:_}}let p={target:n,fields:l,scripts:y,businessRules:g,dependents:f,references:u,blastRadius:b},h=Math.ceil(JSON.stringify(p).length/4),R=Math.max(0,c-h),x=c>0?Math.round(R/c*100):0;return{contextId:O.default.createHash("sha256").update(JSON.stringify(p)).digest("hex").slice(0,12),target:n,fields:l,scripts:y,businessRules:g,dependents:f,references:u,blastRadius:b,tokens:{contextTokens:h,fullGraphTokens:c,tokensSaved:R,reductionPercent:x}}}function oe(t,i,n){return t.nodes.find(o=>o.name===n&&i.getPluginName(o.table)==="TablePlugin"&&!i.isChild(o.table))??t.nodes.find(o=>o.name===n)}function ie(t,i,n){if(!n)return[];let o=t.edges.filter(r=>r.type==="composition"&&r.from===n.id).map(r=>r.to),e=[];for(let r of o){let s=t.nodes.find(d=>d.id===r);if(!s||!i.isChild(s.table))continue;let a=T(s,"element"),c=T(s,"internal_type");!a||!c||c!=="collection"&&e.push({name:a,type:c,label:T(s,"column_label")||a,referenceTable:c==="reference"?T(s,"reference"):void 0})}return e.sort((r,s)=>r.name.localeCompare(s.name))}function re(t,i,n,o){let e=n?.id??`platform:${o}`,r=t.edges.filter(d=>d.type==="dependency"&&(d.to===e||d.targetTable===o)),s=[],a=[];for(let d of r){let l=t.nodes.find(u=>u.id===d.from);if(!l||!l.source.file)continue;let y=i.getPluginName(l.table),g=ce(T(l,"type"),y),f={name:l.name,event:g,field:T(l,"field"),order:te(l,"order")??100,active:ne(l,"active")!==!1,file:l.source.file};y==="BusinessRulePlugin"?a.push(f):s.push(f)}let c=(d,l)=>d.order-l.order||d.name.localeCompare(l.name);return{scripts:s.sort(c),businessRules:a.sort(c)}}function D(t,i){let n=new Set(t.nodes.filter(r=>r.name===i).map(r=>r.id));n.add(`platform:${i}`);let o=t.edges.filter(r=>r.type==="schema_relationship"&&(n.has(r.to)||r.targetTable===i)),e=new Map;for(let r of o){let s=t.nodes.find(d=>d.id===r.from);if(!s)continue;let a=s.name;if(a===i)continue;let c=r.via||T(s,"element")||"unknown";if(!e.has(a)){let d=t.edges.filter(l=>l.type==="dependency"&&l.targetTable===a).length;e.set(a,{table:a,via:c,type:"FK",artifactCount:d})}}return[...e.values()].sort((r,s)=>r.table.localeCompare(s.table))}function se(t,i){if(!i)return[];let n=new Set(t.edges.filter(e=>e.type==="composition"&&e.from===i.id).map(e=>e.to)),o=[];for(let e of t.edges){if(e.type!=="schema_relationship"||!n.has(e.from))continue;let r=t.nodes.find(a=>a.id===e.from);if(!r)continue;let s=e.to.startsWith("platform:")?e.to.split(":")[1]:t.nodes.find(a=>a.id===e.to)?.name??e.to;o.push({field:T(r,"element")||e.via||"unknown",targetTable:s})}return o.sort((e,r)=>e.field.localeCompare(r.field))}var ae={onload:"onLoad",onchange:"onChange",onsubmit:"onSubmit",before:"before",after:"after",async:"async",load:"onLoad",change:"onChange",submit:"onSubmit"};function ce(t,i){return t?ae[t.toLowerCase()]??t:i==="BusinessRulePlugin"?"before":"action"}function k(t,i){let n=t.attributes[i];return typeof n=="string"?n:void 0}function de(t,i){let n=t.attributes[i];if(typeof n=="number")return n;if(typeof n=="string"&&n.trim()!==""){let o=Number(n);return Number.isNaN(o)?void 0:o}}function fe(t,i){let n=t.length,o=i.length,e=Array.from({length:n+1},(r,s)=>Array.from({length:o+1},(a,c)=>s===0?c:c===0?s:0));for(let r=1;r<=n;r++)for(let s=1;s<=o;s++)e[r][s]=t[r-1]===i[s-1]?e[r-1][s-1]:1+Math.min(e[r-1][s-1],e[r-1][s],e[r][s-1]);return e[n][o]}function le(t,i){let n,o=1/0;for(let e of i){let r=fe(t.toLowerCase(),e.toLowerCase());r<o&&(o=r,n=e)}return n&&o<=Math.max(2,Math.floor(t.length*.4))?n:void 0}function ue(t,i){let n=[],o=new Map;for(let e of t.nodes){if(!i.isChild(e.table))continue;let r=k(e,"element");if(!r)continue;let s=t.nodes.find(c=>t.edges.some(d=>d.type==="composition"&&d.from===c.id&&d.to===e.id))?.name;if(!s)continue;let a=o.get(s)??[];a.push(r),o.set(s,a)}for(let e of t.edges){if(e.type!=="dependency")continue;let r=t.nodes.find(l=>l.id===e.from);if(!r?.source.file)continue;let s=k(r,"field");if(!s)continue;let a=e.targetTable??t.nodes.find(l=>l.id===e.to)?.name;if(!a)continue;let c=o.get(a);if(!c||c.includes(s))continue;let d=le(s,c);n.push({severity:"error",code:"FG-001",message:`"${r.name}" references field "${s}" on table "${a}" but no such field exists`,artifact:r.name,table:a,file:r.source.file,suggestion:d?`Did you mean "${d}"?`:void 0})}return n}function ge(t){let i=[],n=new Map;for(let o of t.edges){if(o.type!=="dependency")continue;let e=t.nodes.find(d=>d.id===o.from);if(!e?.source.file)continue;let r=o.targetTable??t.nodes.find(d=>d.id===o.to)?.name??"unknown",s=k(e,"type")??"unknown",a=`${r}::${s}`,c=n.get(a)??[];c.push(e),n.set(a,c)}for(let[o,e]of n){let r=new Map;for(let s of e){let a=de(s,"order")??100,c=r.get(a)??[];c.push(s),r.set(a,c)}for(let[s,a]of r){if(a.length<2)continue;let[c,d]=o.split("::"),l=a.map(y=>y.name).join(" and ");i.push({severity:"warning",code:"FG-002",message:`${l} (order: ${s}) both fire "${d}" on "${c}" \u2014 nondeterministic execution order`,table:c,file:a[0].source.file})}}return i}function pe(t){let i=[],n=new Set(t.nodes.map(o=>o.id));for(let o of t.edges){if(o.type!=="schema_relationship"||o.to.startsWith("platform:")||n.has(o.to))continue;let e=t.nodes.find(a=>a.id===o.from);if(!e)continue;let r=o.to,s=k(e,"element");i.push({severity:"warning",code:"FG-003",message:`Field "${s??o.via??"unknown"}" on "${e.name}" references "${r}" but this table is not in the local project or known platform tables`,artifact:e.name,file:e.source.file})}return i}function me(t){let i=[],n=new Map;for(let o of t.nodes){let e=`${o.table}::${o.name}`,r=n.get(e)??[];r.push(o),n.set(e,r)}for(let[o,e]of n){if(e.length<2)continue;let[r,s]=o.split("::");i.push({severity:"warning",code:"FG-004",message:`Two artifacts named "${s}" exist on "${r}" \u2014 the last defined wins, the other is shadowed`,artifact:s,table:r,file:e[0].source.file})}return i}function ye(t,i){let n=[],o=t.nodes.filter(e=>e.action==="DELETE");for(let e of o){let s=w(t,e.name,5).hops.filter(c=>c.node.action!=="DELETE"&&c.node.source.file);if(s.length===0)continue;let a=i.getDisplayGroupName(e.table).toLowerCase();n.push({severity:"error",code:"FG-005",message:`"${e.name}" (${a}) is marked DELETE but ${s.length} active artifact(s) still reference it \u2014 these will break after deployment`,artifact:e.name,table:e.table,file:e.source.file,suggestion:`Review: ${s.slice(0,3).map(c=>c.node.name).join(", ")}${s.length>3?" ...":""}`})}return n}function K(t,i){let n=[...ue(t,i),...ge(t),...pe(t),...me(t),...ye(t,i)],o={error:0,warning:1,info:2};n.sort((a,c)=>o[a.severity]-o[c.severity]);let e=n.filter(a=>a.severity==="error"),r=n.filter(a=>a.severity==="warning"),s=n.filter(a=>a.severity==="info");return{valid:e.length===0,issueCount:n.length,errors:e,warnings:r,info:s}}0&&(module.exports={extractLineageGraph,selectContext,traverseBlastRadius,validateGraph});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yesprasad/fluent-graph",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Dependency visibility and impact analysis for ServiceNow Fluent SDK projects",
5
5
  "bin": {
6
6
  "fluent-graph": "./bin/fluent-graph.js"