@ryanstark24/sfgraph-core 1.4.0 → 1.6.0
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/analyze/apex-platform.d.ts +23 -0
- package/dist/analyze/apex-platform.d.ts.map +1 -0
- package/dist/analyze/apex-platform.js +112 -0
- package/dist/analyze/apex-platform.js.map +1 -0
- package/dist/analyze/audit-graph.d.ts +34 -10
- package/dist/analyze/audit-graph.d.ts.map +1 -1
- package/dist/analyze/audit-graph.js +360 -39
- package/dist/analyze/audit-graph.js.map +1 -1
- package/dist/analyze/component-scope.d.ts +82 -0
- package/dist/analyze/component-scope.d.ts.map +1 -0
- package/dist/analyze/component-scope.js +207 -0
- package/dist/analyze/component-scope.js.map +1 -0
- package/dist/analyze/containment.d.ts +91 -0
- package/dist/analyze/containment.d.ts.map +1 -0
- package/dist/analyze/containment.js +175 -0
- package/dist/analyze/containment.js.map +1 -0
- package/dist/analyze/cross-org.d.ts +35 -0
- package/dist/analyze/cross-org.d.ts.map +1 -1
- package/dist/analyze/cross-org.js +102 -3
- package/dist/analyze/cross-org.js.map +1 -1
- package/dist/analyze/dead-code.d.ts +99 -3
- package/dist/analyze/dead-code.d.ts.map +1 -1
- package/dist/analyze/dead-code.js +282 -10
- package/dist/analyze/dead-code.js.map +1 -1
- package/dist/analyze/dependencies.d.ts +20 -1
- package/dist/analyze/dependencies.d.ts.map +1 -1
- package/dist/analyze/dependencies.js +96 -12
- package/dist/analyze/dependencies.js.map +1 -1
- package/dist/analyze/dependents.d.ts +111 -1
- package/dist/analyze/dependents.d.ts.map +1 -1
- package/dist/analyze/dependents.js +282 -20
- package/dist/analyze/dependents.js.map +1 -1
- package/dist/analyze/deployment-manifest/generate.d.ts +24 -0
- package/dist/analyze/deployment-manifest/generate.d.ts.map +1 -1
- package/dist/analyze/deployment-manifest/generate.js +40 -12
- package/dist/analyze/deployment-manifest/generate.js.map +1 -1
- package/dist/analyze/deployment-manifest/member-name-formatters.d.ts.map +1 -1
- package/dist/analyze/deployment-manifest/member-name-formatters.js +1 -0
- package/dist/analyze/deployment-manifest/member-name-formatters.js.map +1 -1
- package/dist/analyze/deployment-manifest/member-types.d.ts.map +1 -1
- package/dist/analyze/deployment-manifest/member-types.js +2 -0
- package/dist/analyze/deployment-manifest/member-types.js.map +1 -1
- package/dist/analyze/diff-seeds.d.ts +33 -0
- package/dist/analyze/diff-seeds.d.ts.map +1 -0
- package/dist/analyze/diff-seeds.js +299 -0
- package/dist/analyze/diff-seeds.js.map +1 -0
- package/dist/analyze/edge-filters.d.ts +25 -0
- package/dist/analyze/edge-filters.d.ts.map +1 -1
- package/dist/analyze/edge-filters.js +78 -2
- package/dist/analyze/edge-filters.js.map +1 -1
- package/dist/analyze/field-scope.d.ts +25 -0
- package/dist/analyze/field-scope.d.ts.map +1 -0
- package/dist/analyze/field-scope.js +94 -0
- package/dist/analyze/field-scope.js.map +1 -0
- package/dist/analyze/find-nodes.d.ts +48 -12
- package/dist/analyze/find-nodes.d.ts.map +1 -1
- package/dist/analyze/find-nodes.js +323 -12
- package/dist/analyze/find-nodes.js.map +1 -1
- package/dist/analyze/findings.d.ts.map +1 -1
- package/dist/analyze/findings.js +14 -0
- package/dist/analyze/findings.js.map +1 -1
- package/dist/analyze/git-diff.d.ts +27 -1
- package/dist/analyze/git-diff.d.ts.map +1 -1
- package/dist/analyze/git-diff.js +198 -26
- package/dist/analyze/git-diff.js.map +1 -1
- package/dist/analyze/governor.d.ts +25 -4
- package/dist/analyze/governor.d.ts.map +1 -1
- package/dist/analyze/governor.js +82 -18
- package/dist/analyze/governor.js.map +1 -1
- package/dist/analyze/index.d.ts +6 -0
- package/dist/analyze/index.d.ts.map +1 -1
- package/dist/analyze/index.js +6 -0
- package/dist/analyze/index.js.map +1 -1
- package/dist/analyze/path-to-qname.d.ts +9 -0
- package/dist/analyze/path-to-qname.d.ts.map +1 -1
- package/dist/analyze/path-to-qname.js +58 -15
- package/dist/analyze/path-to-qname.js.map +1 -1
- package/dist/analyze/populate.d.ts +40 -0
- package/dist/analyze/populate.d.ts.map +1 -1
- package/dist/analyze/populate.js +148 -48
- package/dist/analyze/populate.js.map +1 -1
- package/dist/analyze/pseudo-endpoints.d.ts +67 -0
- package/dist/analyze/pseudo-endpoints.d.ts.map +1 -0
- package/dist/analyze/pseudo-endpoints.js +78 -0
- package/dist/analyze/pseudo-endpoints.js.map +1 -0
- package/dist/analyze/security.d.ts +2 -0
- package/dist/analyze/security.d.ts.map +1 -1
- package/dist/analyze/security.js +44 -15
- package/dist/analyze/security.js.map +1 -1
- package/dist/analyze/test-coverage.d.ts +48 -0
- package/dist/analyze/test-coverage.d.ts.map +1 -1
- package/dist/analyze/test-coverage.js +72 -1
- package/dist/analyze/test-coverage.js.map +1 -1
- package/dist/analyze/wip-changed-files.d.ts +88 -0
- package/dist/analyze/wip-changed-files.d.ts.map +1 -0
- package/dist/analyze/wip-changed-files.js +422 -0
- package/dist/analyze/wip-changed-files.js.map +1 -0
- package/dist/analyze/wip-impact.d.ts +59 -0
- package/dist/analyze/wip-impact.d.ts.map +1 -1
- package/dist/analyze/wip-impact.js +199 -126
- package/dist/analyze/wip-impact.js.map +1 -1
- package/dist/domain/apex-platform.d.ts +10 -0
- package/dist/domain/apex-platform.d.ts.map +1 -0
- package/dist/domain/apex-platform.js +231 -0
- package/dist/domain/apex-platform.js.map +1 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.d.ts.map +1 -1
- package/dist/domain/index.js +2 -0
- package/dist/domain/index.js.map +1 -1
- package/dist/domain/rel-types.d.ts +7 -0
- package/dist/domain/rel-types.d.ts.map +1 -1
- package/dist/domain/rel-types.js +7 -0
- package/dist/domain/rel-types.js.map +1 -1
- package/dist/domain/standard-sobjects.d.ts +20 -0
- package/dist/domain/standard-sobjects.d.ts.map +1 -0
- package/dist/domain/standard-sobjects.js +183 -0
- package/dist/domain/standard-sobjects.js.map +1 -0
- package/dist/embedding/cache.d.ts +41 -0
- package/dist/embedding/cache.d.ts.map +1 -0
- package/dist/embedding/cache.js +69 -0
- package/dist/embedding/cache.js.map +1 -0
- package/dist/embedding/embed.d.ts +59 -0
- package/dist/embedding/embed.d.ts.map +1 -1
- package/dist/embedding/embed.js +193 -28
- package/dist/embedding/embed.js.map +1 -1
- package/dist/embedding/index.d.ts +7 -0
- package/dist/embedding/index.d.ts.map +1 -1
- package/dist/embedding/index.js +4 -0
- package/dist/embedding/index.js.map +1 -1
- package/dist/embedding/pool.d.ts +66 -6
- package/dist/embedding/pool.d.ts.map +1 -1
- package/dist/embedding/pool.js +156 -6
- package/dist/embedding/pool.js.map +1 -1
- package/dist/embedding/queue.d.ts +166 -9
- package/dist/embedding/queue.d.ts.map +1 -1
- package/dist/embedding/queue.js +397 -39
- package/dist/embedding/queue.js.map +1 -1
- package/dist/embedding/tokenize.d.ts +18 -0
- package/dist/embedding/tokenize.d.ts.map +1 -0
- package/dist/embedding/tokenize.js +43 -0
- package/dist/embedding/tokenize.js.map +1 -0
- package/dist/embedding/worker.d.ts +0 -5
- package/dist/embedding/worker.d.ts.map +1 -1
- package/dist/embedding/worker.js +27 -4
- package/dist/embedding/worker.js.map +1 -1
- package/dist/extractors/filesystem/index.d.ts +21 -1
- package/dist/extractors/filesystem/index.d.ts.map +1 -1
- package/dist/extractors/filesystem/index.js +195 -38
- package/dist/extractors/filesystem/index.js.map +1 -1
- package/dist/extractors/filesystem/vlocity-datapacks.d.ts +27 -0
- package/dist/extractors/filesystem/vlocity-datapacks.d.ts.map +1 -0
- package/dist/extractors/filesystem/vlocity-datapacks.js +201 -0
- package/dist/extractors/filesystem/vlocity-datapacks.js.map +1 -0
- package/dist/extractors/interfaces/metadata-source.d.ts +29 -0
- package/dist/extractors/interfaces/metadata-source.d.ts.map +1 -1
- package/dist/extractors/live-org/bulk-retrieve.d.ts +37 -1
- package/dist/extractors/live-org/bulk-retrieve.d.ts.map +1 -1
- package/dist/extractors/live-org/bulk-retrieve.js +233 -50
- package/dist/extractors/live-org/bulk-retrieve.js.map +1 -1
- package/dist/extractors/live-org/capabilities.d.ts +15 -0
- package/dist/extractors/live-org/capabilities.d.ts.map +1 -1
- package/dist/extractors/live-org/capabilities.js +54 -9
- package/dist/extractors/live-org/capabilities.js.map +1 -1
- package/dist/extractors/live-org/extractors/apex.d.ts +12 -2
- package/dist/extractors/live-org/extractors/apex.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/apex.js +151 -80
- package/dist/extractors/live-org/extractors/apex.js.map +1 -1
- package/dist/extractors/live-org/extractors/custom-settings.d.ts +2 -1
- package/dist/extractors/live-org/extractors/custom-settings.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/custom-settings.js +19 -9
- package/dist/extractors/live-org/extractors/custom-settings.js.map +1 -1
- package/dist/extractors/live-org/extractors/flow.d.ts +2 -1
- package/dist/extractors/live-org/extractors/flow.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/flow.js +12 -3
- package/dist/extractors/live-org/extractors/flow.js.map +1 -1
- package/dist/extractors/live-org/extractors/generic-metadata.d.ts +23 -3
- package/dist/extractors/live-org/extractors/generic-metadata.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/generic-metadata.js +110 -25
- package/dist/extractors/live-org/extractors/generic-metadata.js.map +1 -1
- package/dist/extractors/live-org/extractors/integration.d.ts +2 -1
- package/dist/extractors/live-org/extractors/integration.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/integration.js +14 -5
- package/dist/extractors/live-org/extractors/integration.js.map +1 -1
- package/dist/extractors/live-org/extractors/lwc.d.ts +2 -1
- package/dist/extractors/live-org/extractors/lwc.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/lwc.js +14 -6
- package/dist/extractors/live-org/extractors/lwc.js.map +1 -1
- package/dist/extractors/live-org/extractors/object.d.ts +2 -11
- package/dist/extractors/live-org/extractors/object.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/object.js +127 -226
- package/dist/extractors/live-org/extractors/object.js.map +1 -1
- package/dist/extractors/live-org/extractors/omnistudio.d.ts +3 -0
- package/dist/extractors/live-org/extractors/omnistudio.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/omnistudio.js +57 -22
- package/dist/extractors/live-org/extractors/omnistudio.js.map +1 -1
- package/dist/extractors/live-org/extractors/security.d.ts +12 -1
- package/dist/extractors/live-org/extractors/security.d.ts.map +1 -1
- package/dist/extractors/live-org/extractors/security.js +29 -7
- package/dist/extractors/live-org/extractors/security.js.map +1 -1
- package/dist/extractors/live-org/rate-limit.d.ts +7 -1
- package/dist/extractors/live-org/rate-limit.d.ts.map +1 -1
- package/dist/extractors/live-org/rate-limit.js +53 -10
- package/dist/extractors/live-org/rate-limit.js.map +1 -1
- package/dist/extractors/live-org/retry.d.ts +136 -0
- package/dist/extractors/live-org/retry.d.ts.map +1 -0
- package/dist/extractors/live-org/retry.js +416 -0
- package/dist/extractors/live-org/retry.js.map +1 -0
- package/dist/extractors/live-org/vlocity/runner.d.ts +2 -1
- package/dist/extractors/live-org/vlocity/runner.d.ts.map +1 -1
- package/dist/extractors/live-org/vlocity/runner.js +95 -67
- package/dist/extractors/live-org/vlocity/runner.js.map +1 -1
- package/dist/extractors/vlocity-shared/row.d.ts +19 -0
- package/dist/extractors/vlocity-shared/row.d.ts.map +1 -0
- package/dist/extractors/vlocity-shared/row.js +69 -0
- package/dist/extractors/vlocity-shared/row.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ingest/delta.d.ts +70 -0
- package/dist/ingest/delta.d.ts.map +1 -0
- package/dist/ingest/delta.js +534 -0
- package/dist/ingest/delta.js.map +1 -0
- package/dist/ingest/detect-deletions-guard.d.ts +34 -0
- package/dist/ingest/detect-deletions-guard.d.ts.map +1 -1
- package/dist/ingest/detect-deletions-guard.js +59 -0
- package/dist/ingest/detect-deletions-guard.js.map +1 -1
- package/dist/ingest/index.d.ts +8 -1
- package/dist/ingest/index.d.ts.map +1 -1
- package/dist/ingest/index.js +4 -0
- package/dist/ingest/index.js.map +1 -1
- package/dist/ingest/live-ingest.d.ts +171 -7
- package/dist/ingest/live-ingest.d.ts.map +1 -1
- package/dist/ingest/live-ingest.js +1014 -249
- package/dist/ingest/live-ingest.js.map +1 -1
- package/dist/ingest/lock.d.ts +47 -0
- package/dist/ingest/lock.d.ts.map +1 -0
- package/dist/ingest/lock.js +222 -0
- package/dist/ingest/lock.js.map +1 -0
- package/dist/ingest/member-rows.d.ts +88 -0
- package/dist/ingest/member-rows.d.ts.map +1 -0
- package/dist/ingest/member-rows.js +152 -0
- package/dist/ingest/member-rows.js.map +1 -0
- package/dist/ingest/offline-org.d.ts +8 -0
- package/dist/ingest/offline-org.d.ts.map +1 -0
- package/dist/ingest/offline-org.js +41 -0
- package/dist/ingest/offline-org.js.map +1 -0
- package/dist/ingest/parse-member.d.ts +53 -0
- package/dist/ingest/parse-member.d.ts.map +1 -0
- package/dist/ingest/parse-member.js +276 -0
- package/dist/ingest/parse-member.js.map +1 -0
- package/dist/ingest/parse-pool.d.ts +139 -0
- package/dist/ingest/parse-pool.d.ts.map +1 -0
- package/dist/ingest/parse-pool.js +593 -0
- package/dist/ingest/parse-pool.js.map +1 -0
- package/dist/ingest/parse-worker.d.ts +3 -0
- package/dist/ingest/parse-worker.d.ts.map +1 -0
- package/dist/ingest/parse-worker.js +100 -0
- package/dist/ingest/parse-worker.js.map +1 -0
- package/dist/ingest/profile.d.ts +27 -0
- package/dist/ingest/profile.d.ts.map +1 -0
- package/dist/ingest/profile.js +48 -0
- package/dist/ingest/profile.js.map +1 -0
- package/dist/ingest/resolver-gates.d.ts +12 -0
- package/dist/ingest/resolver-gates.d.ts.map +1 -0
- package/dist/ingest/resolver-gates.js +53 -0
- package/dist/ingest/resolver-gates.js.map +1 -0
- package/dist/parsers/apex/arity-resolver.d.ts +16 -3
- package/dist/parsers/apex/arity-resolver.d.ts.map +1 -1
- package/dist/parsers/apex/arity-resolver.js +31 -2
- package/dist/parsers/apex/arity-resolver.js.map +1 -1
- package/dist/parsers/apex/ast-extractor.d.ts +4 -0
- package/dist/parsers/apex/ast-extractor.d.ts.map +1 -1
- package/dist/parsers/apex/ast-extractor.js +96 -29
- package/dist/parsers/apex/ast-extractor.js.map +1 -1
- package/dist/parsers/apex/class-refs.d.ts +78 -0
- package/dist/parsers/apex/class-refs.d.ts.map +1 -0
- package/dist/parsers/apex/class-refs.js +465 -0
- package/dist/parsers/apex/class-refs.js.map +1 -0
- package/dist/parsers/apex/class.d.ts +158 -0
- package/dist/parsers/apex/class.d.ts.map +1 -1
- package/dist/parsers/apex/class.js +1025 -183
- package/dist/parsers/apex/class.js.map +1 -1
- package/dist/parsers/apex/dynamic-soql.d.ts +52 -0
- package/dist/parsers/apex/dynamic-soql.d.ts.map +1 -0
- package/dist/parsers/apex/dynamic-soql.js +427 -0
- package/dist/parsers/apex/dynamic-soql.js.map +1 -0
- package/dist/parsers/apex/field-access.d.ts +65 -0
- package/dist/parsers/apex/field-access.d.ts.map +1 -0
- package/dist/parsers/apex/field-access.js +438 -0
- package/dist/parsers/apex/field-access.js.map +1 -0
- package/dist/parsers/apex/interface-ref-resolver.d.ts +32 -0
- package/dist/parsers/apex/interface-ref-resolver.d.ts.map +1 -0
- package/dist/parsers/apex/interface-ref-resolver.js +64 -0
- package/dist/parsers/apex/interface-ref-resolver.js.map +1 -0
- package/dist/parsers/apex/name-refs.d.ts +83 -0
- package/dist/parsers/apex/name-refs.d.ts.map +1 -0
- package/dist/parsers/apex/name-refs.js +465 -0
- package/dist/parsers/apex/name-refs.js.map +1 -0
- package/dist/parsers/apex/page-refs.d.ts +11 -0
- package/dist/parsers/apex/page-refs.d.ts.map +1 -0
- package/dist/parsers/apex/page-refs.js +34 -0
- package/dist/parsers/apex/page-refs.js.map +1 -0
- package/dist/parsers/apex/ref-resolver.d.ts +58 -0
- package/dist/parsers/apex/ref-resolver.d.ts.map +1 -0
- package/dist/parsers/apex/ref-resolver.js +348 -0
- package/dist/parsers/apex/ref-resolver.js.map +1 -0
- package/dist/parsers/apex/soql-fields.d.ts +75 -0
- package/dist/parsers/apex/soql-fields.d.ts.map +1 -0
- package/dist/parsers/apex/soql-fields.js +338 -0
- package/dist/parsers/apex/soql-fields.js.map +1 -0
- package/dist/parsers/apex/trigger.d.ts.map +1 -1
- package/dist/parsers/apex/trigger.js +90 -2
- package/dist/parsers/apex/trigger.js.map +1 -1
- package/dist/parsers/apex/vlocity-calls.d.ts +25 -0
- package/dist/parsers/apex/vlocity-calls.d.ts.map +1 -0
- package/dist/parsers/apex/vlocity-calls.js +225 -0
- package/dist/parsers/apex/vlocity-calls.js.map +1 -0
- package/dist/parsers/aura/bundle.d.ts +39 -0
- package/dist/parsers/aura/bundle.d.ts.map +1 -0
- package/dist/parsers/aura/bundle.js +269 -0
- package/dist/parsers/aura/bundle.js.map +1 -0
- package/dist/parsers/aura/embed-resolver.d.ts +17 -0
- package/dist/parsers/aura/embed-resolver.d.ts.map +1 -0
- package/dist/parsers/aura/embed-resolver.js +58 -0
- package/dist/parsers/aura/embed-resolver.js.map +1 -0
- package/dist/parsers/aura/index.d.ts +4 -0
- package/dist/parsers/aura/index.d.ts.map +1 -0
- package/dist/parsers/aura/index.js +6 -0
- package/dist/parsers/aura/index.js.map +1 -0
- package/dist/parsers/common.d.ts +9 -0
- package/dist/parsers/common.d.ts.map +1 -1
- package/dist/parsers/common.js +17 -0
- package/dist/parsers/common.js.map +1 -1
- package/dist/parsers/cross-flavor-resolver.js +8 -2
- package/dist/parsers/cross-flavor-resolver.js.map +1 -1
- package/dist/parsers/flow/invocable-resolver.d.ts.map +1 -1
- package/dist/parsers/flow/invocable-resolver.js +2 -0
- package/dist/parsers/flow/invocable-resolver.js.map +1 -1
- package/dist/parsers/flow/parser.d.ts +9 -0
- package/dist/parsers/flow/parser.d.ts.map +1 -1
- package/dist/parsers/flow/parser.js +327 -2
- package/dist/parsers/flow/parser.js.map +1 -1
- package/dist/parsers/flow/relationship-resolver.d.ts +34 -0
- package/dist/parsers/flow/relationship-resolver.d.ts.map +1 -0
- package/dist/parsers/flow/relationship-resolver.js +199 -0
- package/dist/parsers/flow/relationship-resolver.js.map +1 -0
- package/dist/parsers/generic/opaque-node-parser.d.ts +5 -1
- package/dist/parsers/generic/opaque-node-parser.d.ts.map +1 -1
- package/dist/parsers/generic/opaque-node-parser.js +37 -5
- package/dist/parsers/generic/opaque-node-parser.js.map +1 -1
- package/dist/parsers/generic/reflection-walker.d.ts +7 -0
- package/dist/parsers/generic/reflection-walker.d.ts.map +1 -1
- package/dist/parsers/generic/reflection-walker.js +37 -10
- package/dist/parsers/generic/reflection-walker.js.map +1 -1
- package/dist/parsers/hash-salts.d.ts +72 -0
- package/dist/parsers/hash-salts.d.ts.map +1 -0
- package/dist/parsers/hash-salts.js +91 -0
- package/dist/parsers/hash-salts.js.map +1 -0
- package/dist/parsers/index.d.ts +2 -0
- package/dist/parsers/index.d.ts.map +1 -1
- package/dist/parsers/index.js +2 -0
- package/dist/parsers/index.js.map +1 -1
- package/dist/parsers/lwc/bundle.d.ts +1 -0
- package/dist/parsers/lwc/bundle.d.ts.map +1 -1
- package/dist/parsers/lwc/bundle.js +37 -2
- package/dist/parsers/lwc/bundle.js.map +1 -1
- package/dist/parsers/lwc/field-key-resolver.d.ts +32 -0
- package/dist/parsers/lwc/field-key-resolver.d.ts.map +1 -0
- package/dist/parsers/lwc/field-key-resolver.js +92 -0
- package/dist/parsers/lwc/field-key-resolver.js.map +1 -0
- package/dist/parsers/lwc/html-visitor.d.ts +1 -1
- package/dist/parsers/lwc/html-visitor.d.ts.map +1 -1
- package/dist/parsers/lwc/html-visitor.js +78 -1
- package/dist/parsers/lwc/html-visitor.js.map +1 -1
- package/dist/parsers/lwc/js-visitor.d.ts +16 -0
- package/dist/parsers/lwc/js-visitor.d.ts.map +1 -1
- package/dist/parsers/lwc/js-visitor.js +348 -10
- package/dist/parsers/lwc/js-visitor.js.map +1 -1
- package/dist/parsers/lwc/omnidef.d.ts +10 -0
- package/dist/parsers/lwc/omnidef.d.ts.map +1 -0
- package/dist/parsers/lwc/omnidef.js +127 -0
- package/dist/parsers/lwc/omnidef.js.map +1 -0
- package/dist/parsers/lwc/xml-meta.d.ts.map +1 -1
- package/dist/parsers/lwc/xml-meta.js +16 -1
- package/dist/parsers/lwc/xml-meta.js.map +1 -1
- package/dist/parsers/object/formula-hop-resolver.d.ts +33 -0
- package/dist/parsers/object/formula-hop-resolver.d.ts.map +1 -0
- package/dist/parsers/object/formula-hop-resolver.js +60 -0
- package/dist/parsers/object/formula-hop-resolver.js.map +1 -0
- package/dist/parsers/object/formula-refs.d.ts +40 -0
- package/dist/parsers/object/formula-refs.d.ts.map +1 -0
- package/dist/parsers/object/formula-refs.js +129 -0
- package/dist/parsers/object/formula-refs.js.map +1 -0
- package/dist/parsers/object/object.d.ts +3 -0
- package/dist/parsers/object/object.d.ts.map +1 -1
- package/dist/parsers/object/object.js +53 -52
- package/dist/parsers/object/object.js.map +1 -1
- package/dist/parsers/omnistudio/data-transform.d.ts.map +1 -1
- package/dist/parsers/omnistudio/data-transform.js +2 -2
- package/dist/parsers/omnistudio/data-transform.js.map +1 -1
- package/dist/parsers/omnistudio/integration-procedure.d.ts.map +1 -1
- package/dist/parsers/omnistudio/integration-procedure.js +4 -5
- package/dist/parsers/omnistudio/integration-procedure.js.map +1 -1
- package/dist/parsers/omnistudio/overlap-detector.d.ts.map +1 -1
- package/dist/parsers/omnistudio/overlap-detector.js +27 -2
- package/dist/parsers/omnistudio/overlap-detector.js.map +1 -1
- package/dist/parsers/omnistudio/process.d.ts.map +1 -1
- package/dist/parsers/omnistudio/process.js +4 -5
- package/dist/parsers/omnistudio/process.js.map +1 -1
- package/dist/parsers/omnistudio/remote-refs.d.ts +11 -0
- package/dist/parsers/omnistudio/remote-refs.d.ts.map +1 -0
- package/dist/parsers/omnistudio/remote-refs.js +17 -0
- package/dist/parsers/omnistudio/remote-refs.js.map +1 -0
- package/dist/parsers/omnistudio/ui-card.d.ts.map +1 -1
- package/dist/parsers/omnistudio/ui-card.js +4 -5
- package/dist/parsers/omnistudio/ui-card.js.map +1 -1
- package/dist/parsers/resolver-prune.d.ts +14 -0
- package/dist/parsers/resolver-prune.d.ts.map +1 -0
- package/dist/parsers/resolver-prune.js +29 -0
- package/dist/parsers/resolver-prune.js.map +1 -0
- package/dist/parsers/rules/_engine.d.ts.map +1 -1
- package/dist/parsers/rules/_engine.js +4 -0
- package/dist/parsers/rules/_engine.js.map +1 -1
- package/dist/parsers/rules/_engine.ts +3 -0
- package/dist/parsers/rules/_loader.d.ts +8 -0
- package/dist/parsers/rules/_loader.d.ts.map +1 -1
- package/dist/parsers/rules/_loader.js +17 -0
- package/dist/parsers/rules/_loader.js.map +1 -1
- package/dist/parsers/rules/_loader.ts +19 -0
- package/dist/parsers/rules/_schema.d.ts +12 -12
- package/dist/parsers/rules/_selectors.d.ts +1 -0
- package/dist/parsers/rules/_selectors.d.ts.map +1 -1
- package/dist/parsers/rules/_selectors.js +6 -0
- package/dist/parsers/rules/_selectors.js.map +1 -1
- package/dist/parsers/rules/_selectors.ts +6 -0
- package/dist/parsers/rules/apex-component.yml +49 -4
- package/dist/parsers/rules/apex-page.yml +49 -4
- package/dist/parsers/rules/approval-process.yml +25 -5
- package/dist/parsers/rules/custom-metadata-type.yml +7 -7
- package/dist/parsers/rules/custom-tab.yml +41 -0
- package/dist/parsers/rules/lightning-page.yml +4 -2
- package/dist/parsers/rules/workflow.yml +30 -10
- package/dist/parsers/vf/refs.d.ts +39 -0
- package/dist/parsers/vf/refs.d.ts.map +1 -0
- package/dist/parsers/vf/refs.js +152 -0
- package/dist/parsers/vf/refs.js.map +1 -0
- package/dist/parsers/vlocity/apex-resolver.d.ts +21 -0
- package/dist/parsers/vlocity/apex-resolver.d.ts.map +1 -0
- package/dist/parsers/vlocity/apex-resolver.js +115 -0
- package/dist/parsers/vlocity/apex-resolver.js.map +1 -0
- package/dist/parsers/vlocity/common.d.ts +56 -1
- package/dist/parsers/vlocity/common.d.ts.map +1 -1
- package/dist/parsers/vlocity/common.js +275 -7
- package/dist/parsers/vlocity/common.js.map +1 -1
- package/dist/parsers/vlocity/data-raptor.d.ts +9 -0
- package/dist/parsers/vlocity/data-raptor.d.ts.map +1 -1
- package/dist/parsers/vlocity/data-raptor.js +133 -31
- package/dist/parsers/vlocity/data-raptor.js.map +1 -1
- package/dist/parsers/vlocity/datapack-refs.d.ts +22 -0
- package/dist/parsers/vlocity/datapack-refs.d.ts.map +1 -0
- package/dist/parsers/vlocity/datapack-refs.js +126 -0
- package/dist/parsers/vlocity/datapack-refs.js.map +1 -0
- package/dist/parsers/vlocity/dr-relationship-resolver.d.ts +26 -0
- package/dist/parsers/vlocity/dr-relationship-resolver.d.ts.map +1 -0
- package/dist/parsers/vlocity/dr-relationship-resolver.js +137 -0
- package/dist/parsers/vlocity/dr-relationship-resolver.js.map +1 -0
- package/dist/parsers/vlocity/integration-procedure.d.ts.map +1 -1
- package/dist/parsers/vlocity/integration-procedure.js +15 -48
- package/dist/parsers/vlocity/integration-procedure.js.map +1 -1
- package/dist/parsers/vlocity/name-match-resolver.d.ts +21 -0
- package/dist/parsers/vlocity/name-match-resolver.d.ts.map +1 -0
- package/dist/parsers/vlocity/name-match-resolver.js +97 -0
- package/dist/parsers/vlocity/name-match-resolver.js.map +1 -0
- package/dist/parsers/vlocity/omni-script.d.ts.map +1 -1
- package/dist/parsers/vlocity/omni-script.js +18 -32
- package/dist/parsers/vlocity/omni-script.js.map +1 -1
- package/dist/parsers/vlocity/vlocity-card.d.ts.map +1 -1
- package/dist/parsers/vlocity/vlocity-card.js +30 -36
- package/dist/parsers/vlocity/vlocity-card.js.map +1 -1
- package/dist/render/mermaid/dependency-graph.d.ts.map +1 -1
- package/dist/render/mermaid/dependency-graph.js +13 -1
- package/dist/render/mermaid/dependency-graph.js.map +1 -1
- package/dist/render/mermaid/diff.d.ts.map +1 -1
- package/dist/render/mermaid/diff.js +4 -2
- package/dist/render/mermaid/diff.js.map +1 -1
- package/dist/render/mermaid/shared.d.ts +7 -0
- package/dist/render/mermaid/shared.d.ts.map +1 -1
- package/dist/render/mermaid/shared.js +15 -6
- package/dist/render/mermaid/shared.js.map +1 -1
- package/dist/storage/index.d.ts +3 -3
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +2 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/interfaces.d.ts +155 -1
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/sqlite/graph-store.d.ts +352 -3
- package/dist/storage/sqlite/graph-store.d.ts.map +1 -1
- package/dist/storage/sqlite/graph-store.js +1683 -74
- package/dist/storage/sqlite/graph-store.js.map +1 -1
- package/dist/storage/sqlite/migrations.d.ts +11 -0
- package/dist/storage/sqlite/migrations.d.ts.map +1 -1
- package/dist/storage/sqlite/migrations.js +226 -11
- package/dist/storage/sqlite/migrations.js.map +1 -1
- package/dist/storage/sqlite/snapshot-store.d.ts +79 -3
- package/dist/storage/sqlite/snapshot-store.d.ts.map +1 -1
- package/dist/storage/sqlite/snapshot-store.js +500 -100
- package/dist/storage/sqlite/snapshot-store.js.map +1 -1
- package/dist/storage/sqlite/snapshot-versions.d.ts +51 -0
- package/dist/storage/sqlite/snapshot-versions.d.ts.map +1 -0
- package/dist/storage/sqlite/snapshot-versions.js +209 -0
- package/dist/storage/sqlite/snapshot-versions.js.map +1 -0
- package/dist/storage/sqlite/vector-store.d.ts +12 -0
- package/dist/storage/sqlite/vector-store.d.ts.map +1 -1
- package/dist/storage/sqlite/vector-store.js +57 -14
- package/dist/storage/sqlite/vector-store.js.map +1 -1
- package/dist/util/process-owner.d.ts +35 -0
- package/dist/util/process-owner.d.ts.map +1 -0
- package/dist/util/process-owner.js +87 -0
- package/dist/util/process-owner.js.map +1 -0
- package/dist/util/worker-heap-cap.d.ts +10 -0
- package/dist/util/worker-heap-cap.d.ts.map +1 -0
- package/dist/util/worker-heap-cap.js +65 -0
- package/dist/util/worker-heap-cap.js.map +1 -0
- package/package.json +6 -7
- package/dist/analyze/__tests__/audit-graph.test.d.ts +0 -2
- package/dist/analyze/__tests__/audit-graph.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/audit-graph.test.js +0 -201
- package/dist/analyze/__tests__/audit-graph.test.js.map +0 -1
- package/dist/analyze/__tests__/cross-org-two-store.test.d.ts +0 -2
- package/dist/analyze/__tests__/cross-org-two-store.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/cross-org-two-store.test.js +0 -68
- package/dist/analyze/__tests__/cross-org-two-store.test.js.map +0 -1
- package/dist/analyze/__tests__/dead-code-confidence.test.d.ts +0 -2
- package/dist/analyze/__tests__/dead-code-confidence.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/dead-code-confidence.test.js +0 -66
- package/dist/analyze/__tests__/dead-code-confidence.test.js.map +0 -1
- package/dist/analyze/__tests__/deployment-manifest.test.d.ts +0 -2
- package/dist/analyze/__tests__/deployment-manifest.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/deployment-manifest.test.js +0 -66
- package/dist/analyze/__tests__/deployment-manifest.test.js.map +0 -1
- package/dist/analyze/__tests__/edge-filters.test.d.ts +0 -2
- package/dist/analyze/__tests__/edge-filters.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/edge-filters.test.js +0 -66
- package/dist/analyze/__tests__/edge-filters.test.js.map +0 -1
- package/dist/analyze/__tests__/find-nodes.test.d.ts +0 -2
- package/dist/analyze/__tests__/find-nodes.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/find-nodes.test.js +0 -129
- package/dist/analyze/__tests__/find-nodes.test.js.map +0 -1
- package/dist/analyze/__tests__/findings.test.d.ts +0 -2
- package/dist/analyze/__tests__/findings.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/findings.test.js +0 -100
- package/dist/analyze/__tests__/findings.test.js.map +0 -1
- package/dist/analyze/__tests__/governor-risks-detection.test.d.ts +0 -2
- package/dist/analyze/__tests__/governor-risks-detection.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/governor-risks-detection.test.js +0 -160
- package/dist/analyze/__tests__/governor-risks-detection.test.js.map +0 -1
- package/dist/analyze/__tests__/populate.test.d.ts +0 -2
- package/dist/analyze/__tests__/populate.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/populate.test.js +0 -129
- package/dist/analyze/__tests__/populate.test.js.map +0 -1
- package/dist/analyze/__tests__/security-fls-filter.test.d.ts +0 -2
- package/dist/analyze/__tests__/security-fls-filter.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/security-fls-filter.test.js +0 -23
- package/dist/analyze/__tests__/security-fls-filter.test.js.map +0 -1
- package/dist/analyze/__tests__/traversal-caps.test.d.ts +0 -2
- package/dist/analyze/__tests__/traversal-caps.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/traversal-caps.test.js +0 -129
- package/dist/analyze/__tests__/traversal-caps.test.js.map +0 -1
- package/dist/analyze/__tests__/wip-impact.test.d.ts +0 -2
- package/dist/analyze/__tests__/wip-impact.test.d.ts.map +0 -1
- package/dist/analyze/__tests__/wip-impact.test.js +0 -134
- package/dist/analyze/__tests__/wip-impact.test.js.map +0 -1
- package/dist/embedding/__tests__/embed.test.d.ts +0 -2
- package/dist/embedding/__tests__/embed.test.d.ts.map +0 -1
- package/dist/embedding/__tests__/embed.test.js +0 -72
- package/dist/embedding/__tests__/embed.test.js.map +0 -1
- package/dist/embedding/__tests__/queue.test.d.ts +0 -2
- package/dist/embedding/__tests__/queue.test.d.ts.map +0 -1
- package/dist/embedding/__tests__/queue.test.js +0 -64
- package/dist/embedding/__tests__/queue.test.js.map +0 -1
- package/dist/extractors/__tests__/network-egress.test.d.ts +0 -2
- package/dist/extractors/__tests__/network-egress.test.d.ts.map +0 -1
- package/dist/extractors/__tests__/network-egress.test.js +0 -32
- package/dist/extractors/__tests__/network-egress.test.js.map +0 -1
- package/dist/extractors/filesystem/__tests__/filesystem.test.d.ts +0 -2
- package/dist/extractors/filesystem/__tests__/filesystem.test.d.ts.map +0 -1
- package/dist/extractors/filesystem/__tests__/filesystem.test.js +0 -96
- package/dist/extractors/filesystem/__tests__/filesystem.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/_jsforce-mock.d.ts +0 -38
- package/dist/extractors/live-org/__tests__/_jsforce-mock.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/_jsforce-mock.js +0 -96
- package/dist/extractors/live-org/__tests__/_jsforce-mock.js.map +0 -1
- package/dist/extractors/live-org/__tests__/auth.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/auth.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/auth.test.js +0 -85
- package/dist/extractors/live-org/__tests__/auth.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/bulk-retrieve.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/bulk-retrieve.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/bulk-retrieve.test.js +0 -150
- package/dist/extractors/live-org/__tests__/bulk-retrieve.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/capabilities.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/capabilities.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/capabilities.test.js +0 -54
- package/dist/extractors/live-org/__tests__/capabilities.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/custom-settings.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/custom-settings.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/custom-settings.test.js +0 -79
- package/dist/extractors/live-org/__tests__/custom-settings.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/discovery.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/discovery.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/discovery.test.js +0 -55
- package/dist/extractors/live-org/__tests__/discovery.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/dispatch.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/dispatch.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/dispatch.test.js +0 -68
- package/dist/extractors/live-org/__tests__/dispatch.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/extractor-enrichment.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/extractor-enrichment.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/extractor-enrichment.test.js +0 -374
- package/dist/extractors/live-org/__tests__/extractor-enrichment.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/liveness.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/liveness.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/liveness.test.js +0 -148
- package/dist/extractors/live-org/__tests__/liveness.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/mcd-baseline.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/mcd-baseline.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/mcd-baseline.test.js +0 -177
- package/dist/extractors/live-org/__tests__/mcd-baseline.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/metadata-read-batch.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/metadata-read-batch.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/metadata-read-batch.test.js +0 -75
- package/dist/extractors/live-org/__tests__/metadata-read-batch.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.js +0 -135
- package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/pools.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/pools.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/pools.test.js +0 -78
- package/dist/extractors/live-org/__tests__/pools.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/rate-limit.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/rate-limit.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/rate-limit.test.js +0 -73
- package/dist/extractors/live-org/__tests__/rate-limit.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/read-only-proxy.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/read-only-proxy.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/read-only-proxy.test.js +0 -376
- package/dist/extractors/live-org/__tests__/read-only-proxy.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/soql-rebatch.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/soql-rebatch.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/soql-rebatch.test.js +0 -118
- package/dist/extractors/live-org/__tests__/soql-rebatch.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/source-member.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/source-member.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/source-member.test.js +0 -84
- package/dist/extractors/live-org/__tests__/source-member.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/stop-waiting.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/stop-waiting.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/stop-waiting.test.js +0 -243
- package/dist/extractors/live-org/__tests__/stop-waiting.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/vlocity-runner.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/vlocity-runner.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/vlocity-runner.test.js +0 -115
- package/dist/extractors/live-org/__tests__/vlocity-runner.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/watchdog-budgets.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/watchdog-budgets.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/watchdog-budgets.test.js +0 -40
- package/dist/extractors/live-org/__tests__/watchdog-budgets.test.js.map +0 -1
- package/dist/extractors/live-org/__tests__/wedge-isolation.test.d.ts +0 -2
- package/dist/extractors/live-org/__tests__/wedge-isolation.test.d.ts.map +0 -1
- package/dist/extractors/live-org/__tests__/wedge-isolation.test.js +0 -270
- package/dist/extractors/live-org/__tests__/wedge-isolation.test.js.map +0 -1
- package/dist/extractors/live-org/extractors/__tests__/apex.test.d.ts +0 -2
- package/dist/extractors/live-org/extractors/__tests__/apex.test.d.ts.map +0 -1
- package/dist/extractors/live-org/extractors/__tests__/apex.test.js +0 -73
- package/dist/extractors/live-org/extractors/__tests__/apex.test.js.map +0 -1
- package/dist/extractors/live-org/extractors/__tests__/lwc.parallel.test.d.ts +0 -2
- package/dist/extractors/live-org/extractors/__tests__/lwc.parallel.test.d.ts.map +0 -1
- package/dist/extractors/live-org/extractors/__tests__/lwc.parallel.test.js +0 -183
- package/dist/extractors/live-org/extractors/__tests__/lwc.parallel.test.js.map +0 -1
- package/dist/ingest/__tests__/config-values.test.d.ts +0 -2
- package/dist/ingest/__tests__/config-values.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/config-values.test.js +0 -65
- package/dist/ingest/__tests__/config-values.test.js.map +0 -1
- package/dist/ingest/__tests__/full-sync-deletion.test.d.ts +0 -2
- package/dist/ingest/__tests__/full-sync-deletion.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/full-sync-deletion.test.js +0 -207
- package/dist/ingest/__tests__/full-sync-deletion.test.js.map +0 -1
- package/dist/ingest/__tests__/incremental-deletion.test.d.ts +0 -2
- package/dist/ingest/__tests__/incremental-deletion.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/incremental-deletion.test.js +0 -92
- package/dist/ingest/__tests__/incremental-deletion.test.js.map +0 -1
- package/dist/ingest/__tests__/live-ingest.detect-deletions-guard.test.d.ts +0 -10
- package/dist/ingest/__tests__/live-ingest.detect-deletions-guard.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/live-ingest.detect-deletions-guard.test.js +0 -251
- package/dist/ingest/__tests__/live-ingest.detect-deletions-guard.test.js.map +0 -1
- package/dist/ingest/__tests__/live-ingest.test.d.ts +0 -2
- package/dist/ingest/__tests__/live-ingest.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/live-ingest.test.js +0 -155
- package/dist/ingest/__tests__/live-ingest.test.js.map +0 -1
- package/dist/ingest/__tests__/multi-org-orchestrator.test.d.ts +0 -2
- package/dist/ingest/__tests__/multi-org-orchestrator.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/multi-org-orchestrator.test.js +0 -108
- package/dist/ingest/__tests__/multi-org-orchestrator.test.js.map +0 -1
- package/dist/ingest/__tests__/parser-dispatch.test.d.ts +0 -2
- package/dist/ingest/__tests__/parser-dispatch.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/parser-dispatch.test.js +0 -55
- package/dist/ingest/__tests__/parser-dispatch.test.js.map +0 -1
- package/dist/ingest/__tests__/read-only-wrapping.test.d.ts +0 -2
- package/dist/ingest/__tests__/read-only-wrapping.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/read-only-wrapping.test.js +0 -62
- package/dist/ingest/__tests__/read-only-wrapping.test.js.map +0 -1
- package/dist/ingest/__tests__/vlocity-dispatch-integration.test.d.ts +0 -2
- package/dist/ingest/__tests__/vlocity-dispatch-integration.test.d.ts.map +0 -1
- package/dist/ingest/__tests__/vlocity-dispatch-integration.test.js +0 -134
- package/dist/ingest/__tests__/vlocity-dispatch-integration.test.js.map +0 -1
- package/dist/parsers/__tests__/_harness.d.ts +0 -13
- package/dist/parsers/__tests__/_harness.d.ts.map +0 -1
- package/dist/parsers/__tests__/_harness.js +0 -84
- package/dist/parsers/__tests__/_harness.js.map +0 -1
- package/dist/parsers/__tests__/apex.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/apex.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/apex.golden.test.js +0 -28
- package/dist/parsers/__tests__/apex.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/cross-flavor.test.d.ts +0 -2
- package/dist/parsers/__tests__/cross-flavor.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/cross-flavor.test.js +0 -56
- package/dist/parsers/__tests__/cross-flavor.test.js.map +0 -1
- package/dist/parsers/__tests__/datatransform-structured.test.d.ts +0 -2
- package/dist/parsers/__tests__/datatransform-structured.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/datatransform-structured.test.js +0 -147
- package/dist/parsers/__tests__/datatransform-structured.test.js.map +0 -1
- package/dist/parsers/__tests__/edge-provenance.test.d.ts +0 -2
- package/dist/parsers/__tests__/edge-provenance.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/edge-provenance.test.js +0 -47
- package/dist/parsers/__tests__/edge-provenance.test.js.map +0 -1
- package/dist/parsers/__tests__/flow.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/flow.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/flow.golden.test.js +0 -17
- package/dist/parsers/__tests__/flow.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/integration.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/integration.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/integration.golden.test.js +0 -31
- package/dist/parsers/__tests__/integration.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/long-tail.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/long-tail.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/long-tail.golden.test.js +0 -115
- package/dist/parsers/__tests__/long-tail.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/lwc-bindings.test.d.ts +0 -2
- package/dist/parsers/__tests__/lwc-bindings.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/lwc-bindings.test.js +0 -149
- package/dist/parsers/__tests__/lwc-bindings.test.js.map +0 -1
- package/dist/parsers/__tests__/lwc.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/lwc.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/lwc.golden.test.js +0 -16
- package/dist/parsers/__tests__/lwc.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/mcd-gap-fills.test.d.ts +0 -2
- package/dist/parsers/__tests__/mcd-gap-fills.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/mcd-gap-fills.test.js +0 -75
- package/dist/parsers/__tests__/mcd-gap-fills.test.js.map +0 -1
- package/dist/parsers/__tests__/object-inline-fields.test.d.ts +0 -2
- package/dist/parsers/__tests__/object-inline-fields.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/object-inline-fields.test.js +0 -115
- package/dist/parsers/__tests__/object-inline-fields.test.js.map +0 -1
- package/dist/parsers/__tests__/object.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/object.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/object.golden.test.js +0 -33
- package/dist/parsers/__tests__/object.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/omnistudio-overlap.test.d.ts +0 -2
- package/dist/parsers/__tests__/omnistudio-overlap.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/omnistudio-overlap.test.js +0 -163
- package/dist/parsers/__tests__/omnistudio-overlap.test.js.map +0 -1
- package/dist/parsers/__tests__/omnistudio.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/omnistudio.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/omnistudio.golden.test.js +0 -53
- package/dist/parsers/__tests__/omnistudio.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/reflection-walker.test.d.ts +0 -2
- package/dist/parsers/__tests__/reflection-walker.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/reflection-walker.test.js +0 -162
- package/dist/parsers/__tests__/reflection-walker.test.js.map +0 -1
- package/dist/parsers/__tests__/registry.test.d.ts +0 -2
- package/dist/parsers/__tests__/registry.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/registry.test.js +0 -23
- package/dist/parsers/__tests__/registry.test.js.map +0 -1
- package/dist/parsers/__tests__/security.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/security.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/security.golden.test.js +0 -31
- package/dist/parsers/__tests__/security.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/vlocity.golden.test.d.ts +0 -2
- package/dist/parsers/__tests__/vlocity.golden.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/vlocity.golden.test.js +0 -105
- package/dist/parsers/__tests__/vlocity.golden.test.js.map +0 -1
- package/dist/parsers/__tests__/worker-pool.test.d.ts +0 -2
- package/dist/parsers/__tests__/worker-pool.test.d.ts.map +0 -1
- package/dist/parsers/__tests__/worker-pool.test.js +0 -48
- package/dist/parsers/__tests__/worker-pool.test.js.map +0 -1
- package/dist/parsers/apex/__tests__/arity-resolver.test.d.ts +0 -2
- package/dist/parsers/apex/__tests__/arity-resolver.test.d.ts.map +0 -1
- package/dist/parsers/apex/__tests__/arity-resolver.test.js +0 -135
- package/dist/parsers/apex/__tests__/arity-resolver.test.js.map +0 -1
- package/dist/parsers/apex/__tests__/ast-extractor.test.d.ts +0 -2
- package/dist/parsers/apex/__tests__/ast-extractor.test.d.ts.map +0 -1
- package/dist/parsers/apex/__tests__/ast-extractor.test.js +0 -206
- package/dist/parsers/apex/__tests__/ast-extractor.test.js.map +0 -1
- package/dist/parsers/apex/__tests__/empty-body.test.d.ts +0 -2
- package/dist/parsers/apex/__tests__/empty-body.test.d.ts.map +0 -1
- package/dist/parsers/apex/__tests__/empty-body.test.js +0 -40
- package/dist/parsers/apex/__tests__/empty-body.test.js.map +0 -1
- package/dist/parsers/apex/__tests__/is-test-attribute.test.d.ts +0 -2
- package/dist/parsers/apex/__tests__/is-test-attribute.test.d.ts.map +0 -1
- package/dist/parsers/apex/__tests__/is-test-attribute.test.js +0 -56
- package/dist/parsers/apex/__tests__/is-test-attribute.test.js.map +0 -1
- package/dist/parsers/apex/__tests__/regex-arg-count.test.d.ts +0 -2
- package/dist/parsers/apex/__tests__/regex-arg-count.test.d.ts.map +0 -1
- package/dist/parsers/apex/__tests__/regex-arg-count.test.js +0 -82
- package/dist/parsers/apex/__tests__/regex-arg-count.test.js.map +0 -1
- package/dist/parsers/apex/__tests__/regex-inloop.test.d.ts +0 -2
- package/dist/parsers/apex/__tests__/regex-inloop.test.d.ts.map +0 -1
- package/dist/parsers/apex/__tests__/regex-inloop.test.js +0 -70
- package/dist/parsers/apex/__tests__/regex-inloop.test.js.map +0 -1
- package/dist/parsers/flow/__tests__/invocable-resolver.test.d.ts +0 -2
- package/dist/parsers/flow/__tests__/invocable-resolver.test.d.ts.map +0 -1
- package/dist/parsers/flow/__tests__/invocable-resolver.test.js +0 -91
- package/dist/parsers/flow/__tests__/invocable-resolver.test.js.map +0 -1
- package/dist/parsers/rules/__tests__/applies-to.test.d.ts +0 -2
- package/dist/parsers/rules/__tests__/applies-to.test.d.ts.map +0 -1
- package/dist/parsers/rules/__tests__/applies-to.test.js +0 -31
- package/dist/parsers/rules/__tests__/applies-to.test.js.map +0 -1
- package/dist/parsers/rules/__tests__/applies-to.test.ts +0 -40
- package/dist/parsers/rules/__tests__/engine.test.d.ts +0 -2
- package/dist/parsers/rules/__tests__/engine.test.d.ts.map +0 -1
- package/dist/parsers/rules/__tests__/engine.test.js +0 -127
- package/dist/parsers/rules/__tests__/engine.test.js.map +0 -1
- package/dist/parsers/rules/__tests__/engine.test.ts +0 -138
- package/dist/parsers/rules/__tests__/loader.test.d.ts +0 -2
- package/dist/parsers/rules/__tests__/loader.test.d.ts.map +0 -1
- package/dist/parsers/rules/__tests__/loader.test.js +0 -31
- package/dist/parsers/rules/__tests__/loader.test.js.map +0 -1
- package/dist/parsers/rules/__tests__/loader.test.ts +0 -37
- package/dist/parsers/rules/__tests__/schema.test.d.ts +0 -2
- package/dist/parsers/rules/__tests__/schema.test.d.ts.map +0 -1
- package/dist/parsers/rules/__tests__/schema.test.js +0 -33
- package/dist/parsers/rules/__tests__/schema.test.js.map +0 -1
- package/dist/parsers/rules/__tests__/schema.test.ts +0 -37
- package/dist/parsers/rules/__tests__/selectors.test.d.ts +0 -2
- package/dist/parsers/rules/__tests__/selectors.test.d.ts.map +0 -1
- package/dist/parsers/rules/__tests__/selectors.test.js +0 -51
- package/dist/parsers/rules/__tests__/selectors.test.js.map +0 -1
- package/dist/parsers/rules/__tests__/selectors.test.ts +0 -62
- package/dist/render/__tests__/sarif.test.d.ts +0 -2
- package/dist/render/__tests__/sarif.test.d.ts.map +0 -1
- package/dist/render/__tests__/sarif.test.js +0 -126
- package/dist/render/__tests__/sarif.test.js.map +0 -1
- package/dist/render/mermaid/__tests__/mermaid.test.d.ts +0 -2
- package/dist/render/mermaid/__tests__/mermaid.test.d.ts.map +0 -1
- package/dist/render/mermaid/__tests__/mermaid.test.js +0 -74
- package/dist/render/mermaid/__tests__/mermaid.test.js.map +0 -1
- package/dist/storage/__tests__/analysis-tables-migration.test.d.ts +0 -2
- package/dist/storage/__tests__/analysis-tables-migration.test.d.ts.map +0 -1
- package/dist/storage/__tests__/analysis-tables-migration.test.js +0 -44
- package/dist/storage/__tests__/analysis-tables-migration.test.js.map +0 -1
- package/dist/storage/__tests__/graph-store.test.d.ts +0 -2
- package/dist/storage/__tests__/graph-store.test.d.ts.map +0 -1
- package/dist/storage/__tests__/graph-store.test.js +0 -145
- package/dist/storage/__tests__/graph-store.test.js.map +0 -1
- package/dist/storage/__tests__/identifier.test.d.ts +0 -2
- package/dist/storage/__tests__/identifier.test.d.ts.map +0 -1
- package/dist/storage/__tests__/identifier.test.js +0 -35
- package/dist/storage/__tests__/identifier.test.js.map +0 -1
- package/dist/storage/__tests__/load-better-sqlite3.test.d.ts +0 -2
- package/dist/storage/__tests__/load-better-sqlite3.test.d.ts.map +0 -1
- package/dist/storage/__tests__/load-better-sqlite3.test.js +0 -65
- package/dist/storage/__tests__/load-better-sqlite3.test.js.map +0 -1
- package/dist/storage/__tests__/migrations.test.d.ts +0 -2
- package/dist/storage/__tests__/migrations.test.d.ts.map +0 -1
- package/dist/storage/__tests__/migrations.test.js +0 -93
- package/dist/storage/__tests__/migrations.test.js.map +0 -1
- package/dist/storage/__tests__/perf-50k.test.d.ts +0 -2
- package/dist/storage/__tests__/perf-50k.test.d.ts.map +0 -1
- package/dist/storage/__tests__/perf-50k.test.js +0 -67
- package/dist/storage/__tests__/perf-50k.test.js.map +0 -1
- package/dist/storage/__tests__/rename-stability.test.d.ts +0 -2
- package/dist/storage/__tests__/rename-stability.test.d.ts.map +0 -1
- package/dist/storage/__tests__/rename-stability.test.js +0 -135
- package/dist/storage/__tests__/rename-stability.test.js.map +0 -1
- package/dist/storage/__tests__/snapshot-store.test.d.ts +0 -2
- package/dist/storage/__tests__/snapshot-store.test.d.ts.map +0 -1
- package/dist/storage/__tests__/snapshot-store.test.js +0 -117
- package/dist/storage/__tests__/snapshot-store.test.js.map +0 -1
- package/dist/storage/__tests__/snippets.test.d.ts +0 -2
- package/dist/storage/__tests__/snippets.test.d.ts.map +0 -1
- package/dist/storage/__tests__/snippets.test.js +0 -88
- package/dist/storage/__tests__/snippets.test.js.map +0 -1
- package/dist/storage/__tests__/sync-generation.test.d.ts +0 -2
- package/dist/storage/__tests__/sync-generation.test.d.ts.map +0 -1
- package/dist/storage/__tests__/sync-generation.test.js +0 -159
- package/dist/storage/__tests__/sync-generation.test.js.map +0 -1
- package/dist/storage/__tests__/vector-store-load-failure.test.d.ts +0 -2
- package/dist/storage/__tests__/vector-store-load-failure.test.d.ts.map +0 -1
- package/dist/storage/__tests__/vector-store-load-failure.test.js +0 -42
- package/dist/storage/__tests__/vector-store-load-failure.test.js.map +0 -1
- package/dist/storage/__tests__/vector-store.test.d.ts +0 -2
- package/dist/storage/__tests__/vector-store.test.d.ts.map +0 -1
- package/dist/storage/__tests__/vector-store.test.js +0 -87
- package/dist/storage/__tests__/vector-store.test.js.map +0 -1
- package/dist/telemetry/__tests__/event-schema.test.d.ts +0 -2
- package/dist/telemetry/__tests__/event-schema.test.d.ts.map +0 -1
- package/dist/telemetry/__tests__/event-schema.test.js +0 -56
- package/dist/telemetry/__tests__/event-schema.test.js.map +0 -1
- package/dist/telemetry/__tests__/machine-id.test.d.ts +0 -2
- package/dist/telemetry/__tests__/machine-id.test.d.ts.map +0 -1
- package/dist/telemetry/__tests__/machine-id.test.js +0 -33
- package/dist/telemetry/__tests__/machine-id.test.js.map +0 -1
- package/dist/telemetry/__tests__/pipeline.test.d.ts +0 -2
- package/dist/telemetry/__tests__/pipeline.test.d.ts.map +0 -1
- package/dist/telemetry/__tests__/pipeline.test.js +0 -68
- package/dist/telemetry/__tests__/pipeline.test.js.map +0 -1
- package/dist/telemetry/__tests__/sanitizer.test.d.ts +0 -2
- package/dist/telemetry/__tests__/sanitizer.test.d.ts.map +0 -1
- package/dist/telemetry/__tests__/sanitizer.test.js +0 -143
- package/dist/telemetry/__tests__/sanitizer.test.js.map +0 -1
|
@@ -2,8 +2,88 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { asOrgId, asQualifiedName, asSha256 } from "@ryanstark24/sfgraph-shared";
|
|
3
3
|
import { METADATA_CATEGORY, REL_TYPES } from "../../domain/index.js";
|
|
4
4
|
import { makeEdge, makeNode, stripNs } from "../common.js";
|
|
5
|
+
import { APEX_NODE_HASH_SALT } from "../hash-salts.js";
|
|
6
|
+
import { NEW_INSTANCE_RE, STATIC_CALL_RE, emitClassRefEdges, emitDescribeRefEdges, emitStaticCallEdges, emitTypeRefEdges, isUserClassName, localVarNames, } from "./class-refs.js";
|
|
5
7
|
import { parseAnnotations, stripCommentsAndStrings } from "./common.js";
|
|
6
|
-
|
|
8
|
+
import { PLACEHOLDER, dynamicSoqlClauseFields, foldDynamicSoql, maskApex, parseDynamicSoql, } from "./dynamic-soql.js";
|
|
9
|
+
import { classScopes, collectDecls, extractFieldAccesses, paramDecls, } from "./field-access.js";
|
|
10
|
+
import { emitInstanceCallEdges, emitNameRefEdges, makeTypeLookup, recordTypeAt, } from "./name-refs.js";
|
|
11
|
+
import { emitPageRefEdges } from "./page-refs.js";
|
|
12
|
+
import { blankBracketGroups, findInlineQueries, soqlFieldRefs } from "./soql-fields.js";
|
|
13
|
+
import { classStringConstants, extractVlocityCalls, extractVlocityDispatch, stripCommentsOnly, } from "./vlocity-calls.js";
|
|
14
|
+
export { APEX_NODE_HASH_SALT } from "../hash-salts.js";
|
|
15
|
+
/**
|
|
16
|
+
* apex-parser (antlr4ts) keeps a DFA cache on each grammar's static ATN. It is
|
|
17
|
+
* per isolate, it is never trimmed, and it grows with every distinct input:
|
|
18
|
+
* over a large Vlocity corpus it held ~190 MB live (and a 2-4x larger V8 heap) in each
|
|
19
|
+
* parse worker. The cache is only a memo for prediction, so dropping it does
|
|
20
|
+
* not change any parse result. Release it after this many syntax checks in the
|
|
21
|
+
* current isolate (each parse worker, or the main thread in serial mode).
|
|
22
|
+
* Measured on a large Vlocity repo, 4 workers: 50 cut peak RSS by ~1.07 GB for ~5% more parse
|
|
23
|
+
* time.
|
|
24
|
+
*/
|
|
25
|
+
export const APEX_DFA_CLEAR_INTERVAL = 50;
|
|
26
|
+
let apexChecksSinceDfaClear = 0;
|
|
27
|
+
/** Count one apex-parser syntax check; clear the lexer and parser DFA when due. */
|
|
28
|
+
function noteApexSyntaxCheck(lexer, parser) {
|
|
29
|
+
apexChecksSinceDfaClear++;
|
|
30
|
+
if (apexChecksSinceDfaClear < APEX_DFA_CLEAR_INTERVAL)
|
|
31
|
+
return;
|
|
32
|
+
apexChecksSinceDfaClear = 0;
|
|
33
|
+
lexer?.interpreter?.clearDFA();
|
|
34
|
+
parser?.interpreter?.clearDFA();
|
|
35
|
+
}
|
|
36
|
+
/** Test hook: restart the DFA-release counter for this isolate. */
|
|
37
|
+
export function resetApexDfaCounterForTest() {
|
|
38
|
+
apexChecksSinceDfaClear = 0;
|
|
39
|
+
}
|
|
40
|
+
// Modifiers are optional (`*`, not `+`): a method with NO explicit access
|
|
41
|
+
// modifier is implicitly private in Apex and is still a real method — the old
|
|
42
|
+
// `+` silently dropped every unmodified helper/inner-class method. Allowing
|
|
43
|
+
// zero modifiers means the regex can also touch control-flow that happens to
|
|
44
|
+
// look like `<word> <word>(...) {` (notably `else if (x) {`), so findMethods
|
|
45
|
+
// rejects any match whose return-type or name is a reserved keyword.
|
|
46
|
+
const METHOD_RE = /(?:@[\w()=,'"\s.]+\s+)*(?:(public|private|protected|global|virtual|override|abstract|static|webservice|with\s+sharing|without\s+sharing|inherited\s+sharing|testmethod|final)\s+)*([A-Za-z_][\w<>,.\s\[\]]*?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*\{/gi;
|
|
47
|
+
// Apex reserved words that can appear as `<kw> <kw>(...) {` but are NOT method
|
|
48
|
+
// declarations. If the captured return-type or method-name is one of these, the
|
|
49
|
+
// match is control flow / a statement, not a method.
|
|
50
|
+
const NON_METHOD_KEYWORDS = new Set([
|
|
51
|
+
"if",
|
|
52
|
+
"else",
|
|
53
|
+
"for",
|
|
54
|
+
"while",
|
|
55
|
+
"do",
|
|
56
|
+
"switch",
|
|
57
|
+
"catch",
|
|
58
|
+
"try",
|
|
59
|
+
"finally",
|
|
60
|
+
"return",
|
|
61
|
+
"new",
|
|
62
|
+
"throw",
|
|
63
|
+
"when",
|
|
64
|
+
]);
|
|
65
|
+
// Access/sharing/definition modifiers. When the regex makes modifiers optional,
|
|
66
|
+
// a CONSTRUCTOR `public Foo(...) {` backtracks to returnType="public", name="Foo"
|
|
67
|
+
// (the class name) — a phantom method. A real method's return type is a TYPE,
|
|
68
|
+
// never a modifier, so a modifier captured AS the return type means we matched a
|
|
69
|
+
// constructor (or other non-method) and must drop it.
|
|
70
|
+
const MODIFIER_KEYWORDS = new Set([
|
|
71
|
+
"public",
|
|
72
|
+
"private",
|
|
73
|
+
"protected",
|
|
74
|
+
"global",
|
|
75
|
+
"virtual",
|
|
76
|
+
"override",
|
|
77
|
+
"abstract",
|
|
78
|
+
"static",
|
|
79
|
+
"webservice",
|
|
80
|
+
"with",
|
|
81
|
+
"without",
|
|
82
|
+
"inherited",
|
|
83
|
+
"sharing",
|
|
84
|
+
"testmethod",
|
|
85
|
+
"final",
|
|
86
|
+
]);
|
|
7
87
|
function findMatchingBrace(src, openIdx) {
|
|
8
88
|
let depth = 0;
|
|
9
89
|
for (let i = openIdx; i < src.length; i++) {
|
|
@@ -18,48 +98,208 @@ function findMatchingBrace(src, openIdx) {
|
|
|
18
98
|
}
|
|
19
99
|
return -1;
|
|
20
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Split `raw` on commas at generic/bracket/paren depth 0, so
|
|
103
|
+
* `Map<Id, Contact> a, List<String> b` is two items, not three.
|
|
104
|
+
*/
|
|
105
|
+
function splitTopLevel(raw) {
|
|
106
|
+
const out = [];
|
|
107
|
+
let depth = 0;
|
|
108
|
+
let cur = "";
|
|
109
|
+
for (const c of raw) {
|
|
110
|
+
if (c === "<" || c === "(" || c === "[" || c === "{")
|
|
111
|
+
depth++;
|
|
112
|
+
else if ((c === ">" || c === ")" || c === "]" || c === "}") && depth > 0)
|
|
113
|
+
depth--;
|
|
114
|
+
if (c === "," && depth === 0) {
|
|
115
|
+
out.push(cur);
|
|
116
|
+
cur = "";
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
cur += c;
|
|
120
|
+
}
|
|
121
|
+
out.push(cur);
|
|
122
|
+
return out.map((x) => x.trim()).filter((x) => x.length > 0);
|
|
123
|
+
}
|
|
21
124
|
function parseParams(raw) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
125
|
+
return splitTopLevel(raw);
|
|
126
|
+
}
|
|
127
|
+
/** `Database.Batchable<SObject>` -> `Database.Batchable` (whitespace-normalised). */
|
|
128
|
+
function stripTypeArgs(t) {
|
|
129
|
+
const lt = t.indexOf("<");
|
|
130
|
+
return (lt >= 0 ? t.slice(0, lt) : t).replace(/\s+/g, "");
|
|
131
|
+
}
|
|
132
|
+
/** Remove `@Name` / `@Name(...)` annotations (balanced parens) from `text`. */
|
|
133
|
+
function stripAnnotations(text) {
|
|
134
|
+
let out = "";
|
|
135
|
+
for (let i = 0; i < text.length; i++) {
|
|
136
|
+
if (text[i] !== "@") {
|
|
137
|
+
out += text[i];
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
let j = i + 1;
|
|
141
|
+
while (j < text.length && /\w/.test(text[j] ?? ""))
|
|
142
|
+
j++;
|
|
143
|
+
let k = j;
|
|
144
|
+
while (k < text.length && /\s/.test(text[k] ?? ""))
|
|
145
|
+
k++;
|
|
146
|
+
if (text[k] === "(") {
|
|
147
|
+
let d = 0;
|
|
148
|
+
for (; k < text.length; k++) {
|
|
149
|
+
if (text[k] === "(")
|
|
150
|
+
d++;
|
|
151
|
+
else if (text[k] === ")" && --d === 0)
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
j = k + 1;
|
|
155
|
+
}
|
|
156
|
+
out += " ";
|
|
157
|
+
i = j - 1;
|
|
158
|
+
}
|
|
159
|
+
return out;
|
|
26
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Header of the file's OUTER type: the first `class`/`interface`/`enum` keyword at
|
|
163
|
+
* brace depth 0 (so an inner class can never be taken for the outer one),
|
|
164
|
+
* with `extends` / `implements` clauses parsed by a depth-aware split so
|
|
165
|
+
* generic arguments (`Database.Batchable<SObject>`) neither break the match
|
|
166
|
+
* nor split the list. Type arguments are dropped from the emitted names.
|
|
167
|
+
* `src` must have comments and strings stripped.
|
|
168
|
+
*/
|
|
27
169
|
function extractClassHeader(src) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
170
|
+
const empty = {
|
|
171
|
+
name: "",
|
|
172
|
+
extendsClass: null,
|
|
173
|
+
extendsList: [],
|
|
174
|
+
implementsList: [],
|
|
175
|
+
isInterface: false,
|
|
176
|
+
isTest: false,
|
|
177
|
+
modifiers: [],
|
|
178
|
+
};
|
|
179
|
+
// `enum` too: a top-level enum file declares its modifiers like a class does (cycle 3 T6 fix 1).
|
|
180
|
+
const kwRe = /\b(class|interface|enum)\s+([A-Za-z_]\w*)/gi;
|
|
181
|
+
let depth = 0;
|
|
182
|
+
let scanned = 0;
|
|
183
|
+
let hit = null;
|
|
184
|
+
for (let m = kwRe.exec(src); m !== null; m = kwRe.exec(src)) {
|
|
185
|
+
for (; scanned < m.index; scanned++) {
|
|
186
|
+
if (src[scanned] === "{")
|
|
187
|
+
depth++;
|
|
188
|
+
else if (src[scanned] === "}")
|
|
189
|
+
depth--;
|
|
190
|
+
}
|
|
191
|
+
if (depth === 0) {
|
|
192
|
+
hit = m;
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
38
195
|
}
|
|
39
|
-
|
|
196
|
+
if (!hit)
|
|
197
|
+
return empty;
|
|
198
|
+
const open = src.indexOf("{", hit.index + hit[0].length);
|
|
199
|
+
if (open < 0)
|
|
200
|
+
return empty;
|
|
201
|
+
const isInterface = (hit[1] ?? "").toLowerCase() === "interface";
|
|
202
|
+
const name = hit[2] ?? "";
|
|
203
|
+
// Everything before the keyword at depth 0 is annotations + modifiers. Only
|
|
204
|
+
// the trailing statement matters (a preceding `}`/`;` cannot occur before
|
|
205
|
+
// the outer type, but be defensive).
|
|
206
|
+
const prefixStart = Math.max(src.lastIndexOf("}", hit.index), src.lastIndexOf(";", hit.index));
|
|
207
|
+
const prefix = src.slice(prefixStart + 1, hit.index);
|
|
208
|
+
const isTest = parseAnnotations(prefix).some((a) => a.name.toLowerCase() === "istest");
|
|
209
|
+
const modifiers = stripAnnotations(prefix)
|
|
40
210
|
.trim()
|
|
41
211
|
.split(/\s+/)
|
|
42
212
|
.filter((s) => s.length > 0);
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.filter((
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
213
|
+
const tail = src.slice(hit.index + hit[0].length, open);
|
|
214
|
+
const extIdx = tail.search(/\bextends\b/i);
|
|
215
|
+
const implIdx = tail.search(/\bimplements\b/i);
|
|
216
|
+
const clause = (from, kwLen) => {
|
|
217
|
+
if (from < 0)
|
|
218
|
+
return "";
|
|
219
|
+
const ends = [extIdx, implIdx].filter((x) => x > from);
|
|
220
|
+
return tail.slice(from + kwLen, ends.length > 0 ? Math.min(...ends) : tail.length);
|
|
221
|
+
};
|
|
222
|
+
const extendsRaw = clause(extIdx, "extends".length);
|
|
223
|
+
const implementsRaw = clause(implIdx, "implements".length);
|
|
224
|
+
// An interface may extend several interfaces; a class extends one class.
|
|
225
|
+
const extendsList = splitTopLevel(extendsRaw)
|
|
226
|
+
.map(stripTypeArgs)
|
|
227
|
+
.filter((x) => x);
|
|
228
|
+
const implementsList = splitTopLevel(implementsRaw)
|
|
229
|
+
.map(stripTypeArgs)
|
|
230
|
+
.filter((x) => x.length > 0);
|
|
231
|
+
return {
|
|
232
|
+
name,
|
|
233
|
+
extendsClass: extendsList[0] ?? null,
|
|
234
|
+
extendsList,
|
|
235
|
+
implementsList,
|
|
236
|
+
isInterface,
|
|
237
|
+
isTest,
|
|
238
|
+
modifiers,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The bundled apex-parser grammar predates Apex's null-coalescing operator, so
|
|
243
|
+
* `a ?? b` is a syntax error to it. For grammar validation only, rewrite every
|
|
244
|
+
* code-level `??` (never inside comments or strings) to `||`: same length, so
|
|
245
|
+
* token offsets stay valid, and the same binary-expression shape. The original
|
|
246
|
+
* source is still used for every extraction and snippet.
|
|
247
|
+
*/
|
|
248
|
+
export function rewriteNullCoalescing(src) {
|
|
249
|
+
if (!src.includes("??"))
|
|
250
|
+
return src;
|
|
251
|
+
const masked = maskApex(src);
|
|
252
|
+
const out = src.split("");
|
|
253
|
+
for (let i = masked.indexOf("??"); i >= 0; i = masked.indexOf("??", i + 2)) {
|
|
254
|
+
out[i] = "|";
|
|
255
|
+
out[i + 1] = "|";
|
|
256
|
+
}
|
|
257
|
+
return out.join("");
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Human-readable message for a parse failure. apex-parser's
|
|
261
|
+
* ThrowingErrorListener throws a plain `{ line, column, message }` object (not
|
|
262
|
+
* an Error), which `String(err)` turned into "[object Object]".
|
|
263
|
+
*/
|
|
264
|
+
export function formatParseError(err) {
|
|
265
|
+
const MAX = 300;
|
|
266
|
+
const clip = (s) => (s.length > MAX ? `${s.slice(0, MAX)}…` : s);
|
|
267
|
+
if (err instanceof Error)
|
|
268
|
+
return clip(err.message);
|
|
269
|
+
if (err && typeof err === "object") {
|
|
270
|
+
const e = err;
|
|
271
|
+
const msg = typeof e.message === "string" ? e.message : JSON.stringify(err);
|
|
272
|
+
const pos = typeof e.line === "number" ? `${e.line}:${typeof e.column === "number" ? e.column : 0} ` : "";
|
|
273
|
+
return clip(`${pos}${msg}`);
|
|
274
|
+
}
|
|
275
|
+
return clip(String(err));
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Raw (comment/string-preserving) body of the method's declaration: the first
|
|
279
|
+
* not-yet-used `name(<same arity>) {` in `masked` that isn't a call (`.name(`)
|
|
280
|
+
* or constructor invocation. Matching and brace-balancing run on `masked`; the
|
|
281
|
+
* slice is taken from `src` at the same offsets. Null when not found.
|
|
282
|
+
*/
|
|
283
|
+
function declRawBody(src, masked, m, used) {
|
|
284
|
+
const re = new RegExp(`(?<![.\\w])${m.name}\\s*\\(([^)]*)\\)\\s*\\{`, "g");
|
|
285
|
+
for (const hit of masked.matchAll(re)) {
|
|
286
|
+
const at = hit.index ?? 0;
|
|
287
|
+
if (used.has(at))
|
|
288
|
+
continue;
|
|
289
|
+
if (/\bnew\s+$/.test(masked.slice(Math.max(0, at - 5), at)))
|
|
290
|
+
continue;
|
|
291
|
+
if (parseParams(hit[1] ?? "").length !== m.params.length)
|
|
292
|
+
continue;
|
|
293
|
+
const open = at + hit[0].length - 1;
|
|
294
|
+
const close = findMatchingBrace(masked, open);
|
|
295
|
+
if (close <= 0)
|
|
296
|
+
return null;
|
|
297
|
+
used.add(at);
|
|
298
|
+
return { text: src.slice(open + 1, close), start: open + 1 };
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
function findMethods(src, className) {
|
|
63
303
|
const out = [];
|
|
64
304
|
const re = new RegExp(METHOD_RE.source, "gi");
|
|
65
305
|
let m = re.exec(src);
|
|
@@ -67,6 +307,19 @@ function findMethods(src) {
|
|
|
67
307
|
const returnType = (m[2] ?? "").trim();
|
|
68
308
|
const name = m[3] ?? "";
|
|
69
309
|
const params = parseParams(m[4] ?? "");
|
|
310
|
+
// Reject non-methods that share the `<word> <word>(...) {` shape:
|
|
311
|
+
// - control flow: `else if (x) {` → returnType="else", name="if".
|
|
312
|
+
// - a CONSTRUCTOR: `public Foo(...) {` backtracks to returnType="public"
|
|
313
|
+
// (a modifier) / name=<class name>. A real method's return type is a
|
|
314
|
+
// type, never a modifier, and its name is never the class name.
|
|
315
|
+
if (NON_METHOD_KEYWORDS.has(returnType.toLowerCase()) ||
|
|
316
|
+
NON_METHOD_KEYWORDS.has(name.toLowerCase()) ||
|
|
317
|
+
MODIFIER_KEYWORDS.has(returnType.toLowerCase()) ||
|
|
318
|
+
(className !== undefined && name === className)) {
|
|
319
|
+
re.lastIndex = (m.index ?? 0) + 1;
|
|
320
|
+
m = re.exec(src);
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
70
323
|
// Skip constructors (returnType empty or equals class name when first letter is uppercase)
|
|
71
324
|
// We'll keep ctor handling outside.
|
|
72
325
|
const openBraceIdx = (m.index ?? 0) + m[0].length - 1;
|
|
@@ -110,6 +363,22 @@ function extractApiVersion(metaXml) {
|
|
|
110
363
|
const m = metaXml.match(/<apiVersion>([^<]+)<\/apiVersion>/);
|
|
111
364
|
return m?.[1] ? m[1].trim() : null;
|
|
112
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* `<shadowsManaged>ns</shadowsManaged>` entries the live extractor adds to the
|
|
368
|
+
* metaXml envelope when this local class shares its (namespace-less) qname
|
|
369
|
+
* with managed-package classes. Not a Salesforce field.
|
|
370
|
+
*/
|
|
371
|
+
function extractShadowsManaged(metaXml) {
|
|
372
|
+
if (!metaXml)
|
|
373
|
+
return [];
|
|
374
|
+
const out = [];
|
|
375
|
+
for (const m of metaXml.matchAll(/<shadowsManaged>([^<]+)<\/shadowsManaged>/g)) {
|
|
376
|
+
const ns = m[1]?.trim();
|
|
377
|
+
if (ns)
|
|
378
|
+
out.push(ns);
|
|
379
|
+
}
|
|
380
|
+
return out;
|
|
381
|
+
}
|
|
113
382
|
function sha256(s) {
|
|
114
383
|
return createHash("sha256").update(s).digest("hex");
|
|
115
384
|
}
|
|
@@ -126,23 +395,79 @@ function sha256(s) {
|
|
|
126
395
|
* not inside the recorded range. Known limitation (shared with the populate
|
|
127
396
|
* body-scan): a braceless single-statement loop body isn't tracked.
|
|
128
397
|
*/
|
|
129
|
-
function computeLoopRanges(body) {
|
|
398
|
+
export function computeLoopRanges(body) {
|
|
130
399
|
const ranges = [];
|
|
131
400
|
const stack = [];
|
|
401
|
+
// Brace positions known to open a loop body. Marking the SPECIFIC `{` (rather
|
|
402
|
+
// than a sticky `pendingLoop` flag) is what fixes braceless loops: a
|
|
403
|
+
// `for (...) doIt();` or `do x(); while(y);` has no brace, so the next
|
|
404
|
+
// UNRELATED `{` is no longer mis-marked as a loop body (the old flag bled into
|
|
405
|
+
// it, producing false SOQL/DML-in-loop governor findings).
|
|
406
|
+
const loopBraces = new Set();
|
|
132
407
|
const wordBefore = (i) => /\W/.test(body[i - 1] ?? " ");
|
|
133
408
|
const boundaryAt = (i) => /\W/.test(body[i] ?? " ");
|
|
134
|
-
|
|
409
|
+
const skipWs = (i) => {
|
|
410
|
+
let j = i;
|
|
411
|
+
while (j < body.length && /\s/.test(body[j] ?? ""))
|
|
412
|
+
j += 1;
|
|
413
|
+
return j;
|
|
414
|
+
};
|
|
415
|
+
// Index of the `)` matching the `(` at `open`, or -1.
|
|
416
|
+
const matchParen = (open) => {
|
|
417
|
+
let d = 0;
|
|
418
|
+
for (let j = open; j < body.length; j++) {
|
|
419
|
+
if (body[j] === "(")
|
|
420
|
+
d += 1;
|
|
421
|
+
else if (body[j] === ")" && --d === 0)
|
|
422
|
+
return j;
|
|
423
|
+
}
|
|
424
|
+
return -1;
|
|
425
|
+
};
|
|
426
|
+
// End (`;` at depth 0) of the single statement starting at `from`.
|
|
427
|
+
const stmtEnd = (from) => {
|
|
428
|
+
let d = 0;
|
|
429
|
+
for (let j = from; j < body.length; j++) {
|
|
430
|
+
const c = body[j];
|
|
431
|
+
if (c === "(" || c === "{" || c === "[")
|
|
432
|
+
d += 1;
|
|
433
|
+
else if (c === ")" || c === "}" || c === "]")
|
|
434
|
+
d -= 1;
|
|
435
|
+
else if (c === ";" && d === 0)
|
|
436
|
+
return j;
|
|
437
|
+
}
|
|
438
|
+
return body.length - 1;
|
|
439
|
+
};
|
|
135
440
|
for (let i = 0; i < body.length; i++) {
|
|
136
441
|
const ch = body[i];
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
442
|
+
let kwLen = 0;
|
|
443
|
+
if (ch === "f" && body.startsWith("for", i) && wordBefore(i) && boundaryAt(i + 3))
|
|
444
|
+
kwLen = 3;
|
|
445
|
+
else if (ch === "w" && body.startsWith("while", i) && wordBefore(i) && boundaryAt(i + 5))
|
|
446
|
+
kwLen = 5;
|
|
447
|
+
else if (ch === "d" && body.startsWith("do", i) && wordBefore(i) && boundaryAt(i + 2))
|
|
448
|
+
kwLen = 2;
|
|
449
|
+
if (kwLen > 0) {
|
|
450
|
+
let j = skipWs(i + kwLen);
|
|
451
|
+
// for/while carry a `(condition)` before the body; `do` does not. Skip the
|
|
452
|
+
// condition so its inner `;`s (e.g. `for (i=0; i<n; i++)`) aren't read as a
|
|
453
|
+
// braceless body terminator.
|
|
454
|
+
if ((kwLen === 3 || kwLen === 5) && body[j] === "(") {
|
|
455
|
+
const close = matchParen(j);
|
|
456
|
+
if (close < 0)
|
|
457
|
+
continue;
|
|
458
|
+
j = skipWs(close + 1);
|
|
459
|
+
}
|
|
460
|
+
if (body[j] === "{") {
|
|
461
|
+
loopBraces.add(j); // braced body — recorded when the stack pops it
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
ranges.push({ start: j - 1, end: stmtEnd(j) }); // braceless single statement
|
|
465
|
+
}
|
|
466
|
+
i = j - 1;
|
|
141
467
|
continue;
|
|
142
468
|
}
|
|
143
469
|
if (ch === "{") {
|
|
144
|
-
stack.push({ openIdx: i, isLoop:
|
|
145
|
-
pendingLoop = false;
|
|
470
|
+
stack.push({ openIdx: i, isLoop: loopBraces.has(i) });
|
|
146
471
|
}
|
|
147
472
|
else if (ch === "}") {
|
|
148
473
|
const top = stack.pop();
|
|
@@ -152,92 +477,393 @@ function computeLoopRanges(body) {
|
|
|
152
477
|
}
|
|
153
478
|
return ranges;
|
|
154
479
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
480
|
+
// Inline SOQL queries are found by soql-fields findInlineQueries: from
|
|
481
|
+
// `[SELECT` to the `]` that closes it at bracket depth 0, outside string
|
|
482
|
+
// literals (a bind such as `:ids[0]` does not end the query). The object is
|
|
483
|
+
// taken by parseSoql, which finds the FROM at paren-depth 0 so a child subquery
|
|
484
|
+
// (`(SELECT … FROM Contacts)`) or a semi-join (`WHERE Id IN (SELECT … FROM
|
|
485
|
+
// Contact)`) doesn't get mistaken for the outer object.
|
|
486
|
+
export const SOSL_RE = /\[\s*FIND\b/gi;
|
|
487
|
+
/** Dynamic query entry points (the query text is a runtime string). */
|
|
488
|
+
export const DYNAMIC_QUERY_RE = /\b(Database|Search)\s*\.\s*(query|queryWithBinds|countQuery|countQueryWithBinds|getQueryLocator|getQueryLocatorWithBinds)\s*\(/gi;
|
|
161
489
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* call). Handles nested parens, string literals, and single-line `//`
|
|
165
|
-
* plus block `/* * /` comments. Returns the arg count, or `null` if the
|
|
166
|
-
* caller's parens never close inside `body` (truncated source / unbalanced).
|
|
167
|
-
*
|
|
168
|
-
* Used by the regex pass to emit `ApexMethod:Foo.bar(2)` instead of the
|
|
169
|
-
* `(?)` ambiguity placeholder — turning most regex-mode CALLS edges into
|
|
170
|
-
* precise-arity edges that bypass the arity resolver's overload fan-out.
|
|
171
|
-
* Cases that genuinely can't be counted (lambdas, splat, comment-split
|
|
172
|
-
* args) fall back to `(?)` — preserving the existing ambiguous-fan-out
|
|
173
|
-
* semantics on the long tail.
|
|
490
|
+
* T15 I-5: one EXECUTES_SOSL `SOSL:*` edge when `code` has an inline SOSL
|
|
491
|
+
* query, stamped `inLoop` when any of them sits inside a loop body.
|
|
174
492
|
*/
|
|
175
|
-
function
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
493
|
+
export function emitSoslEdge(ctx, srcQname, code, inLoopAt, edges) {
|
|
494
|
+
let found = false;
|
|
495
|
+
let inLoop = false;
|
|
496
|
+
for (const m of code.matchAll(new RegExp(SOSL_RE.source, "gi"))) {
|
|
497
|
+
found = true;
|
|
498
|
+
if (inLoopAt(m.index ?? 0))
|
|
499
|
+
inLoop = true;
|
|
500
|
+
}
|
|
501
|
+
if (found) {
|
|
502
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_SOSL, "SOSL:*", inLoop ? { inLoop: true } : {}));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* T15 I-5: the dynamic query calls (`Database.query`, `Search.query`, ...)
|
|
507
|
+
* inside a loop body of `code` (strings/comments stripped), as sorted
|
|
508
|
+
* distinct canonical names. Stamped on the method / trigger node as
|
|
509
|
+
* `dynamicQueryInLoop`; empty when none.
|
|
510
|
+
*/
|
|
511
|
+
export function dynamicQueriesInLoop(code, inLoopAt) {
|
|
512
|
+
const out = new Set();
|
|
513
|
+
for (const m of code.matchAll(new RegExp(DYNAMIC_QUERY_RE.source, "gi"))) {
|
|
514
|
+
if (!inLoopAt(m.index ?? 0))
|
|
188
515
|
continue;
|
|
516
|
+
const cls = (m[1] ?? "").toLowerCase() === "search" ? "Search" : "Database";
|
|
517
|
+
const op = (m[2] ?? "").toLowerCase();
|
|
518
|
+
const canon = [
|
|
519
|
+
"query",
|
|
520
|
+
"queryWithBinds",
|
|
521
|
+
"countQuery",
|
|
522
|
+
"countQueryWithBinds",
|
|
523
|
+
"getQueryLocator",
|
|
524
|
+
"getQueryLocatorWithBinds",
|
|
525
|
+
].find((k) => k.toLowerCase() === op) ?? op;
|
|
526
|
+
out.add(`${cls}.${canon}`);
|
|
527
|
+
}
|
|
528
|
+
return [...out].sort();
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Extract the outer object + SELECT clause from a SOQL query's inner text (the
|
|
532
|
+
* part after `[SELECT`). The object is the identifier after the first `FROM`
|
|
533
|
+
* encountered at parenthesis depth 0; subquery/semi-join FROMs (depth > 0) are
|
|
534
|
+
* skipped. Returns object=null for a malformed query.
|
|
535
|
+
*/
|
|
536
|
+
export function parseSoql(inner) {
|
|
537
|
+
let depth = 0;
|
|
538
|
+
for (let i = 0; i < inner.length; i++) {
|
|
539
|
+
const c = inner[i];
|
|
540
|
+
if (c === "(")
|
|
541
|
+
depth += 1;
|
|
542
|
+
else if (c === ")") {
|
|
543
|
+
if (depth > 0)
|
|
544
|
+
depth -= 1;
|
|
189
545
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
i
|
|
194
|
-
|
|
546
|
+
else if (depth === 0 &&
|
|
547
|
+
(c === "f" || c === "F") &&
|
|
548
|
+
/^from\b/i.test(inner.slice(i)) &&
|
|
549
|
+
/\W/.test(inner[i - 1] ?? " ")) {
|
|
550
|
+
const m = inner.slice(i + 4).match(/^\s+([A-Za-z_][\w.]*)/);
|
|
551
|
+
if (m?.[1])
|
|
552
|
+
return { object: m[1], selectClause: inner.slice(0, i) };
|
|
195
553
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
554
|
+
}
|
|
555
|
+
return { object: null, selectClause: inner };
|
|
556
|
+
}
|
|
557
|
+
export const DML_RE = /\b(insert|update|delete|upsert|undelete|merge)\s+([A-Za-z_][\w.]*|\[)/gi;
|
|
558
|
+
/** `Database.insert(recs)`, `Database.update(recs, false)`, `Database.INSERT(…)`:
|
|
559
|
+
* the Database class DML methods (Apex is case-insensitive). Group 2 is the
|
|
560
|
+
* first argument when it is a plain name. */
|
|
561
|
+
export const DATABASE_DML_RE = /\bDatabase\s*\.\s*(insert|update|upsert|delete|undelete|merge)\s*\(\s*([A-Za-z_][\w.]*)?/gi;
|
|
562
|
+
/**
|
|
563
|
+
* EXECUTES_DML `DML:<op>` for DML statements (`insert x;`, `delete [SELECT …]`)
|
|
564
|
+
* and Database DML calls (`Database.insert(x)`) in `code` (strings/comments
|
|
565
|
+
* stripped), stamped `inLoop` when inside a loop body. `target` is the record
|
|
566
|
+
* expression's name when there is one.
|
|
567
|
+
*
|
|
568
|
+
* When the record expression's sObject type is known (see `recordTypeAt`:
|
|
569
|
+
* `new T(…)`, an inline query, a variable `objects.lookup` types, or
|
|
570
|
+
* `Trigger.new` / `Trigger.old`), also EXECUTES_DML `CustomObject:<T>`, like
|
|
571
|
+
* EXECUTES_SOQL, one per object with the sorted operations in `ops`. That edge
|
|
572
|
+
* carries no `inLoop`: the governor checks read the `DML:<op>` marker, so a
|
|
573
|
+
* statement in a loop is reported once.
|
|
574
|
+
*/
|
|
575
|
+
export function emitDmlEdges(ctx, srcQname, code, inLoopAt, edges, objects = {}) {
|
|
576
|
+
const found = [];
|
|
577
|
+
for (const m of code.matchAll(new RegExp(DML_RE.source, "gi"))) {
|
|
578
|
+
const raw = m[2] ?? "";
|
|
579
|
+
const i = m.index ?? 0;
|
|
580
|
+
const target = raw === "[" || raw.toLowerCase() === "new" ? undefined : raw;
|
|
581
|
+
found.push({ i, op: (m[1] ?? "").toLowerCase(), target, arg: i + m[0].length - raw.length });
|
|
582
|
+
}
|
|
583
|
+
for (const m of code.matchAll(new RegExp(DATABASE_DML_RE.source, "gi"))) {
|
|
584
|
+
const t = m[2] && m[2].toLowerCase() !== "new" ? m[2] : undefined;
|
|
585
|
+
const i = m.index ?? 0;
|
|
586
|
+
const arg = i + m[0].length - (m[2] ?? "").length;
|
|
587
|
+
found.push({ i, op: (m[1] ?? "").toLowerCase(), target: t, arg });
|
|
588
|
+
}
|
|
589
|
+
found.sort((a, b) => a.i - b.i);
|
|
590
|
+
const byObject = new Map();
|
|
591
|
+
for (const f of found) {
|
|
592
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_DML, `DML:${f.op}`, {
|
|
593
|
+
...(f.target !== undefined ? { target: f.target } : {}),
|
|
594
|
+
...(inLoopAt(f.i) ? { inLoop: true } : {}),
|
|
595
|
+
}));
|
|
596
|
+
const object = recordTypeAt(code, f.arg, objects);
|
|
597
|
+
if (!object)
|
|
212
598
|
continue;
|
|
599
|
+
const obj = stripNs(object, ctx.namespace);
|
|
600
|
+
const hit = byObject.get(obj.toLowerCase()) ?? { object: obj, ops: new Set() };
|
|
601
|
+
hit.ops.add(f.op);
|
|
602
|
+
byObject.set(obj.toLowerCase(), hit);
|
|
603
|
+
}
|
|
604
|
+
for (const { object, ops } of byObject.values()) {
|
|
605
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_DML, `CustomObject:${object}`, {
|
|
606
|
+
ops: [...ops].sort(),
|
|
607
|
+
}));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Inline `[SELECT …]` queries in `code`: EXECUTES_SOQL on the outer FROM
|
|
612
|
+
* object (with `query`, `inLoop`) and READS_FIELD for every plain field the
|
|
613
|
+
* outer query names (SELECT list, WHERE / HAVING operands, GROUP BY, ORDER
|
|
614
|
+
* BY). Dotted relationship fields are not this object's fields: they are
|
|
615
|
+
* appended to `paths`.
|
|
616
|
+
*/
|
|
617
|
+
export function emitInlineSoqlEdges(ctx, srcQname, code, inLoopAt, edges, paths, children) {
|
|
618
|
+
for (const sq of findInlineQueries(code)) {
|
|
619
|
+
// Bind expressions (`:ids[0]`, an inline query held in a bind) are Apex,
|
|
620
|
+
// not this query's SOQL: blank their bracket groups for the grammar pass.
|
|
621
|
+
const inner = blankBracketGroups(sq.inner);
|
|
622
|
+
const { object } = parseSoql(inner);
|
|
623
|
+
const obj = stripNs(object ?? "", ctx.namespace);
|
|
624
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_SOQL, `CustomObject:${obj}`, {
|
|
625
|
+
query: sq.text.trim(),
|
|
626
|
+
...(inLoopAt(sq.index) ? { inLoop: true } : {}),
|
|
627
|
+
}));
|
|
628
|
+
if (!obj)
|
|
629
|
+
continue;
|
|
630
|
+
const refs = soqlFieldRefs(inner);
|
|
631
|
+
for (const f of refs.fields)
|
|
632
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.READS_FIELD, `CustomField:${obj}.${f}`));
|
|
633
|
+
for (const p of refs.paths)
|
|
634
|
+
paths.push({ object: obj, path: p, rel: REL_TYPES.READS_FIELD });
|
|
635
|
+
// `TYPEOF What WHEN Account THEN Name …`: branch fields are read on the branch's type.
|
|
636
|
+
for (const b of refs.typeOf) {
|
|
637
|
+
const bObj = stripNs(b.object, ctx.namespace);
|
|
638
|
+
for (const f of b.fields)
|
|
639
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.READS_FIELD, `CustomField:${bObj}.${f}`));
|
|
640
|
+
for (const p of b.paths)
|
|
641
|
+
paths.push({ object: bObj, path: p, rel: REL_TYPES.READS_FIELD });
|
|
642
|
+
}
|
|
643
|
+
if (children)
|
|
644
|
+
addChildRelationships(children, obj, parseSoql(inner).selectClause);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/** Literal custom-field names: `'Name__c'` (optional namespace), nothing else in the quotes. */
|
|
648
|
+
const FIELD_LITERAL_RE = /'((?:[A-Za-z][A-Za-z0-9]*__)?[A-Za-z][A-Za-z0-9_]*__c)'/g;
|
|
649
|
+
/** Declared types that are never an sObject. */
|
|
650
|
+
const NOT_SOBJECT = new Set(["sobject", "string", "object", "id", "map", "list", "set"]);
|
|
651
|
+
/** Records `apexFieldNameLiterals` on a method node (see ApexFieldNameLiteral). */
|
|
652
|
+
function setFieldNameLiterals(node, rawDecl, decls) {
|
|
653
|
+
const objects = new Set();
|
|
654
|
+
for (const d of decls) {
|
|
655
|
+
for (const t of [d.type, d.elem]) {
|
|
656
|
+
if (!t || t.includes(".") || NOT_SOBJECT.has(t.toLowerCase()))
|
|
657
|
+
continue;
|
|
658
|
+
if (/^[A-Za-z]\w*$/.test(t))
|
|
659
|
+
objects.add(t);
|
|
213
660
|
}
|
|
214
|
-
|
|
661
|
+
}
|
|
662
|
+
if (objects.size === 0)
|
|
663
|
+
return;
|
|
664
|
+
const fields = new Set();
|
|
665
|
+
for (const m of stripCommentsOnly(rawDecl).matchAll(FIELD_LITERAL_RE)) {
|
|
666
|
+
if (m[1])
|
|
667
|
+
fields.add(m[1]);
|
|
668
|
+
}
|
|
669
|
+
if (fields.size === 0)
|
|
670
|
+
return;
|
|
671
|
+
const objs = [...objects].sort().slice(0, 20);
|
|
672
|
+
node.attributes.apexFieldNameLiterals = [...fields]
|
|
673
|
+
.sort()
|
|
674
|
+
.slice(0, 200)
|
|
675
|
+
.map((field) => ({ field, objects: objs }));
|
|
676
|
+
}
|
|
677
|
+
/** Append the depth-1 `(SELECT …)` groups of `selectClause`, deduped on
|
|
678
|
+
* (object, relationship) with their fields merged. One linear pass. */
|
|
679
|
+
function addChildRelationships(into, object, selectClause) {
|
|
680
|
+
let depth = 0;
|
|
681
|
+
let start = -1;
|
|
682
|
+
for (let i = 0; i < selectClause.length; i++) {
|
|
683
|
+
const c = selectClause[i];
|
|
684
|
+
if (c === "(") {
|
|
215
685
|
depth += 1;
|
|
216
|
-
|
|
686
|
+
if (depth === 1)
|
|
687
|
+
start = i + 1;
|
|
688
|
+
}
|
|
689
|
+
else if (c === ")" && depth > 0) {
|
|
217
690
|
depth -= 1;
|
|
218
|
-
if (depth
|
|
219
|
-
|
|
691
|
+
if (depth !== 0)
|
|
692
|
+
continue;
|
|
693
|
+
const sub = /^\s*select\b/i.exec(selectClause.slice(start, i));
|
|
694
|
+
if (!sub)
|
|
695
|
+
continue;
|
|
696
|
+
const refs = soqlFieldRefs(selectClause.slice(start + sub[0].length, i));
|
|
697
|
+
if (!refs.object || refs.object.includes("."))
|
|
698
|
+
continue;
|
|
699
|
+
const hit = into.find((r) => r.object.toLowerCase() === object.toLowerCase() &&
|
|
700
|
+
r.relationship.toLowerCase() === refs.object?.toLowerCase());
|
|
701
|
+
const rec = hit ?? { object, relationship: refs.object, fields: [] };
|
|
702
|
+
if (!hit)
|
|
703
|
+
into.push(rec);
|
|
704
|
+
for (const f of refs.fields)
|
|
705
|
+
if (!rec.fields.some((x) => x.toLowerCase() === f.toLowerCase()))
|
|
706
|
+
rec.fields.push(f);
|
|
220
707
|
}
|
|
221
|
-
|
|
222
|
-
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
/** Store child-relationship subqueries on a node (only when there are any). */
|
|
711
|
+
function setChildRelationships(node, rels) {
|
|
712
|
+
if (!node || rels.length === 0)
|
|
713
|
+
return;
|
|
714
|
+
node.attributes.apexChildRelationships = rels;
|
|
715
|
+
}
|
|
716
|
+
/** Append `add` to `into`, deduped on (object, path, rel) case-insensitively. */
|
|
717
|
+
function mergePaths(into, add) {
|
|
718
|
+
const seen = new Set(into.map((p) => `${p.object}|${p.path}|${p.rel}`.toLowerCase()));
|
|
719
|
+
for (const p of add) {
|
|
720
|
+
const k = `${p.object}|${p.path}|${p.rel}`.toLowerCase();
|
|
721
|
+
if (seen.has(k))
|
|
722
|
+
continue;
|
|
723
|
+
seen.add(k);
|
|
724
|
+
into.push(p);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
const FIELD_ACCESS_RE = /\b([A-Z][A-Za-z0-9_]*(?:__r)?)\.([A-Za-z_][\w]*?)(?:__c)?\b/g;
|
|
728
|
+
const NAMED_CRED_RE = /['"]callout:([A-Za-z_][\w]*)/g;
|
|
729
|
+
/**
|
|
730
|
+
* Push READS_FIELD / WRITES_FIELD edges for member-access field refs, skipping
|
|
731
|
+
* any (rel, dst) already emitted for this source since `fromIdx` (e.g. a
|
|
732
|
+
* READS_FIELD from a SOQL select list) so existing edge attributes are kept.
|
|
733
|
+
*/
|
|
734
|
+
export function emitFieldAccessEdges(ctx, srcQname, accesses, edges, fromIdx) {
|
|
735
|
+
const seen = new Set();
|
|
736
|
+
for (let i = fromIdx; i < edges.length; i++) {
|
|
737
|
+
const e = edges[i];
|
|
738
|
+
if (e && String(e.srcQualifiedName) === srcQname)
|
|
739
|
+
seen.add(`${e.relType}|${String(e.dstQualifiedName).toLowerCase()}`);
|
|
740
|
+
}
|
|
741
|
+
const push = (rel, dst) => {
|
|
742
|
+
const key = `${rel}|${dst.toLowerCase()}`;
|
|
743
|
+
if (seen.has(key))
|
|
744
|
+
return;
|
|
745
|
+
seen.add(key);
|
|
746
|
+
edges.push(makeEdge(ctx, srcQname, rel, dst, { via: "member-access" }));
|
|
747
|
+
};
|
|
748
|
+
for (const a of accesses) {
|
|
749
|
+
const dst = `CustomField:${stripNs(a.object, ctx.namespace)}.${a.field}`;
|
|
750
|
+
if (a.read)
|
|
751
|
+
push(REL_TYPES.READS_FIELD, dst);
|
|
752
|
+
if (a.write)
|
|
753
|
+
push(REL_TYPES.WRITES_FIELD, dst);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* READS_FIELD `dynamic: true` for the outer SELECT fields and the WHERE /
|
|
758
|
+
* HAVING / GROUP BY / ORDER BY field operands of each folded dynamic SOQL
|
|
759
|
+
* string in `queries` (see foldDynamicSoql, dynamicSoqlClauseFields). Only
|
|
760
|
+
* when the FROM object is literal text; computed fields are skipped. A
|
|
761
|
+
* (src, dst) READS_FIELD already emitted since `fromIdx` is not repeated.
|
|
762
|
+
* Returns the number of clause field operands skipped because FROM is
|
|
763
|
+
* computed (see setDynamicSoqlSkips).
|
|
764
|
+
*/
|
|
765
|
+
export function emitDynamicSoqlEdges(ctx, srcQname, queries, edges, fromIdx) {
|
|
766
|
+
const seen = new Set();
|
|
767
|
+
// Objects this source already queries (static SOQL): an added dynamic edge
|
|
768
|
+
// would replace that edge (the store merges last-wins) and could drop its
|
|
769
|
+
// `inLoop`, so those keep the static edge alone.
|
|
770
|
+
const queried = new Set();
|
|
771
|
+
for (let i = fromIdx; i < edges.length; i++) {
|
|
772
|
+
const e = edges[i];
|
|
773
|
+
if (!e || String(e.srcQualifiedName) !== srcQname)
|
|
774
|
+
continue;
|
|
775
|
+
if (e.relType === REL_TYPES.READS_FIELD)
|
|
776
|
+
seen.add(String(e.dstQualifiedName).toLowerCase());
|
|
777
|
+
else if (e.relType === REL_TYPES.EXECUTES_SOQL)
|
|
778
|
+
queried.add(String(e.dstQualifiedName).toLowerCase());
|
|
779
|
+
}
|
|
780
|
+
let skipped = 0;
|
|
781
|
+
for (const q of queries) {
|
|
782
|
+
const parsed = parseDynamicSoql(q);
|
|
783
|
+
const clause = dynamicSoqlClauseFields(q, parsed);
|
|
784
|
+
skipped += clause.skipped;
|
|
785
|
+
if (!parsed)
|
|
786
|
+
continue;
|
|
787
|
+
const obj = stripNs(parsed.object, ctx.namespace);
|
|
788
|
+
// The object the query reads, like a static `[SELECT …]`. No `inLoop`:
|
|
789
|
+
// loop detection for dynamic queries is `dynamicQueryInLoop` on the node.
|
|
790
|
+
const objDst = `CustomObject:${obj}`;
|
|
791
|
+
if (!queried.has(objDst.toLowerCase())) {
|
|
792
|
+
queried.add(objDst.toLowerCase());
|
|
793
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_SOQL, objDst, {
|
|
794
|
+
query: q.split(PLACEHOLDER).join("…").trim().slice(0, 500),
|
|
795
|
+
dynamic: true,
|
|
796
|
+
}));
|
|
223
797
|
}
|
|
224
|
-
|
|
225
|
-
|
|
798
|
+
for (const fname of [...parsed.fields, ...clause.fields]) {
|
|
799
|
+
const dst = `CustomField:${obj}.${fname}`;
|
|
800
|
+
if (seen.has(dst.toLowerCase()))
|
|
801
|
+
continue;
|
|
802
|
+
seen.add(dst.toLowerCase());
|
|
803
|
+
edges.push(makeEdge(ctx, srcQname, REL_TYPES.READS_FIELD, dst, { dynamic: true }));
|
|
226
804
|
}
|
|
227
|
-
i += 1;
|
|
228
805
|
}
|
|
229
|
-
|
|
230
|
-
return null; // unbalanced — let caller emit `(?)`
|
|
231
|
-
// No commas + no non-whitespace content between parens = arity 0.
|
|
232
|
-
// Any non-whitespace + N commas = N+1 args.
|
|
233
|
-
return sawNonWhitespace ? argCount + 1 : 0;
|
|
806
|
+
return skipped;
|
|
234
807
|
}
|
|
235
|
-
|
|
808
|
+
/**
|
|
809
|
+
* Parse stat on a source node: `dynamicSoqlFieldsSkipped`, the dynamic SOQL
|
|
810
|
+
* clause field operands not attributed because the query's FROM is computed
|
|
811
|
+
* (only when there are any). The apex ref resolver sums it.
|
|
812
|
+
*/
|
|
813
|
+
export function setDynamicSoqlSkips(node, skipped) {
|
|
814
|
+
if (!node || skipped <= 0)
|
|
815
|
+
return;
|
|
816
|
+
const attrs = node.attributes;
|
|
817
|
+
attrs.dynamicSoqlFieldsSkipped = (attrs.dynamicSoqlFieldsSkipped ?? 0) + skipped;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Collapse repeated EXECUTES_SOQL / EXECUTES_DML facts with the same
|
|
821
|
+
* (src, rel, dst) into one, in place of first occurrence. The store merges
|
|
822
|
+
* edges last-wins on that key, so without this an out-of-loop query after an
|
|
823
|
+
* in-loop one on the same object erased `inLoop`. The kept fact carries
|
|
824
|
+
* `inLoop: true` if any occurrence did (taking that occurrence's attributes),
|
|
825
|
+
* and `count` = number of occurrences when more than one.
|
|
826
|
+
*/
|
|
827
|
+
export function aggregateGovernorFacts(edges) {
|
|
828
|
+
const out = [];
|
|
829
|
+
const byKey = new Map();
|
|
830
|
+
for (const e of edges) {
|
|
831
|
+
if (e.relType !== REL_TYPES.EXECUTES_SOQL && e.relType !== REL_TYPES.EXECUTES_DML) {
|
|
832
|
+
out.push(e);
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
const key = `${e.relType}|${String(e.srcQualifiedName)}|${String(e.dstQualifiedName).toLowerCase()}`;
|
|
836
|
+
const prev = byKey.get(key);
|
|
837
|
+
if (!prev) {
|
|
838
|
+
const copy = { ...e, attributes: { ...e.attributes } };
|
|
839
|
+
byKey.set(key, { edge: copy, count: 1 });
|
|
840
|
+
out.push(copy);
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
prev.count += 1;
|
|
844
|
+
const attrs = e.attributes;
|
|
845
|
+
if (attrs.inLoop === true && prev.edge.attributes.inLoop !== true)
|
|
846
|
+
prev.edge.attributes = { ...attrs };
|
|
847
|
+
prev.edge.attributes.count = prev.count;
|
|
848
|
+
}
|
|
849
|
+
return out;
|
|
850
|
+
}
|
|
851
|
+
const LOWER_NEW_RE = /\bnew\s+([a-z][A-Za-z0-9_]*)\s*\(/g;
|
|
852
|
+
const LOWER_STATIC_CALL_RE = /(?<![\w.])([a-z][A-Za-z0-9_]*)\.([A-Za-z][A-Za-z0-9_]*)\s*\(/g;
|
|
853
|
+
const NOT_A_CLASS = new Set(["this", "super"]);
|
|
236
854
|
function parseClassNameAndIsTest(body) {
|
|
237
855
|
const testTargets = new Set();
|
|
856
|
+
const lowerTargets = [];
|
|
238
857
|
const m = body.match(/@isTest\s*\(\s*seeAllData\s*=\s*[^)]*\)|@isTest/i);
|
|
239
858
|
if (!m)
|
|
240
|
-
return { isTest: false, testTargets };
|
|
859
|
+
return { isTest: false, testTargets, lowerTargets };
|
|
860
|
+
for (const re of [LOWER_NEW_RE, LOWER_STATIC_CALL_RE]) {
|
|
861
|
+
for (const x of body.matchAll(new RegExp(re.source, "g"))) {
|
|
862
|
+
const t = x[1] ?? "";
|
|
863
|
+
if (t && !NOT_A_CLASS.has(t))
|
|
864
|
+
lowerTargets.push(t);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
241
867
|
// crude: find class names referenced via `new X(` or `X.foo(`
|
|
242
868
|
const re1 = new RegExp(NEW_INSTANCE_RE.source, "g");
|
|
243
869
|
let mm = re1.exec(body);
|
|
@@ -253,7 +879,7 @@ function parseClassNameAndIsTest(body) {
|
|
|
253
879
|
testTargets.add(mm[1]);
|
|
254
880
|
mm = re2.exec(body);
|
|
255
881
|
}
|
|
256
|
-
return { isTest: true, testTargets };
|
|
882
|
+
return { isTest: true, testTargets, lowerTargets };
|
|
257
883
|
}
|
|
258
884
|
export class ApexClassParser {
|
|
259
885
|
category = METADATA_CATEGORY.APEX_CLASS;
|
|
@@ -263,7 +889,12 @@ export class ApexClassParser {
|
|
|
263
889
|
const edges = [];
|
|
264
890
|
const snippets = [];
|
|
265
891
|
const className = stripNs(input.className, ctx.namespace);
|
|
266
|
-
const
|
|
892
|
+
const shadowsManaged = extractShadowsManaged(input.metaXml);
|
|
893
|
+
const shadowAttr = shadowsManaged.length > 0 ? { shadowsManaged } : {};
|
|
894
|
+
// Fold the collision into the hash (only when present, so every other
|
|
895
|
+
// node keeps its hash): node merge skips attributes when the hash is
|
|
896
|
+
// unchanged, and the stored local node must pick up the attr.
|
|
897
|
+
const fileHash = sha256(`${APEX_NODE_HASH_SALT}\n${input.body}${shadowsManaged.length > 0 ? `\nshadowsManaged:${shadowsManaged.join(",")}` : ""}`);
|
|
267
898
|
// Managed-package Apex (and any member whose Body the extractor couldn't
|
|
268
899
|
// fetch) arrives with an empty body. That is NOT a parse failure — the
|
|
269
900
|
// class exists, we just can't see its source. Emit a bare node so
|
|
@@ -276,6 +907,8 @@ export class ApexClassParser {
|
|
|
276
907
|
modifiers: [],
|
|
277
908
|
isTest: false,
|
|
278
909
|
apiVersion: extractApiVersion(input.metaXml),
|
|
910
|
+
bodyUnavailable: true,
|
|
911
|
+
...shadowAttr,
|
|
279
912
|
}, fileHash));
|
|
280
913
|
return { nodes, edges, snippets };
|
|
281
914
|
}
|
|
@@ -292,25 +925,48 @@ export class ApexClassParser {
|
|
|
292
925
|
// When parserMode != "regex", retain the parsed compilationUnit tree
|
|
293
926
|
// so the AST extractor can walk it without parsing twice.
|
|
294
927
|
let parsedTree = null;
|
|
928
|
+
let lexer = null;
|
|
929
|
+
let parser = null;
|
|
295
930
|
try {
|
|
296
931
|
// Lazy-load to avoid pulling apex-parser into other parsers' worker.
|
|
297
932
|
const apex = await import("apex-parser");
|
|
298
933
|
const { ApexLexer, ApexParser, CommonTokenStream, CaseInsensitiveInputStream, ThrowingErrorListener, } = apex;
|
|
299
|
-
const input1 = new CaseInsensitiveInputStream("file", input.body);
|
|
300
|
-
|
|
934
|
+
const input1 = new CaseInsensitiveInputStream("file", rewriteNullCoalescing(input.body));
|
|
935
|
+
lexer = new ApexLexer(input1);
|
|
301
936
|
lexer.removeErrorListeners();
|
|
302
937
|
lexer.addErrorListener(new ThrowingErrorListener());
|
|
303
938
|
const tokens = new CommonTokenStream(lexer);
|
|
304
|
-
|
|
939
|
+
parser = new ApexParser(tokens);
|
|
305
940
|
parser.removeErrorListeners();
|
|
306
941
|
parser.addErrorListener(new ThrowingErrorListener());
|
|
307
942
|
parsedTree = parser.compilationUnit();
|
|
308
943
|
}
|
|
309
944
|
catch (err) {
|
|
310
|
-
const errMsg =
|
|
945
|
+
const errMsg = formatParseError(err);
|
|
311
946
|
nodes.push(makeNode(ctx, "ParseError", `ParseError:ApexClass:${className}`, { className, message: errMsg }, sha256(`${className}:${errMsg}`)));
|
|
947
|
+
// Keep a stub for the type itself so grants, references and calls to it
|
|
948
|
+
// still resolve; `parseFailed` marks it as having no extracted body.
|
|
949
|
+
const stubSrc = stripCommentsAndStrings(input.body);
|
|
950
|
+
const stubHeader = extractClassHeader(stubSrc);
|
|
951
|
+
const stubLabel = stubHeader.isInterface ? "ApexInterface" : "ApexClass";
|
|
952
|
+
// Cheap fallback when the broken body defeats the header scan: an
|
|
953
|
+
// `@isTest` annotation before the first class keyword.
|
|
954
|
+
const stubIsTest = stubHeader.isTest || /@istest\b[^{;]*?\bclass\b/i.test(stubSrc);
|
|
955
|
+
nodes.push(makeNode(ctx, stubLabel, `${stubLabel}:${className}`, {
|
|
956
|
+
name: className,
|
|
957
|
+
modifiers: [],
|
|
958
|
+
isTest: stubIsTest,
|
|
959
|
+
apiVersion: extractApiVersion(input.metaXml),
|
|
960
|
+
parseFailed: true,
|
|
961
|
+
...shadowAttr,
|
|
962
|
+
}, fileHash));
|
|
312
963
|
return { nodes, edges };
|
|
313
964
|
}
|
|
965
|
+
finally {
|
|
966
|
+
// The tree (if kept) does not depend on the DFA cache.
|
|
967
|
+
if (parser)
|
|
968
|
+
noteApexSyntaxCheck(lexer, parser);
|
|
969
|
+
}
|
|
314
970
|
const cleaned = stripCommentsAndStrings(input.body);
|
|
315
971
|
const header = extractClassHeader(cleaned);
|
|
316
972
|
const effectiveName = header.name || className;
|
|
@@ -318,21 +974,78 @@ export class ApexClassParser {
|
|
|
318
974
|
? `ApexInterface:${effectiveName}`
|
|
319
975
|
: `ApexClass:${effectiveName}`;
|
|
320
976
|
const apiVersion = extractApiVersion(input.metaXml);
|
|
977
|
+
const dispatch = extractVlocityDispatch(input.body, header.implementsList);
|
|
978
|
+
// `final` String fields: an IP / DataRaptor name passed as a constant.
|
|
979
|
+
const stringConstants = classStringConstants(input.body);
|
|
321
980
|
nodes.push(makeNode(ctx, header.isInterface ? "ApexInterface" : "ApexClass", classQname, {
|
|
322
981
|
name: effectiveName,
|
|
323
982
|
modifiers: header.modifiers,
|
|
324
983
|
isTest: header.isTest,
|
|
325
984
|
apiVersion,
|
|
985
|
+
...(dispatch ? { vlocityDispatch: dispatch } : {}),
|
|
986
|
+
...shadowAttr,
|
|
326
987
|
}, fileHash));
|
|
327
|
-
if (header.
|
|
988
|
+
if (header.isInterface) {
|
|
989
|
+
// An interface extends interfaces, possibly several.
|
|
990
|
+
for (const parent of header.extendsList) {
|
|
991
|
+
edges.push(makeEdge(ctx, classQname, REL_TYPES.EXTENDS, `ApexInterface:${stripNs(parent, ctx.namespace)}`));
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
else if (header.extendsClass) {
|
|
328
995
|
edges.push(makeEdge(ctx, classQname, REL_TYPES.EXTENDS, `ApexClass:${stripNs(header.extendsClass, ctx.namespace)}`));
|
|
329
996
|
}
|
|
330
997
|
for (const impl of header.implementsList) {
|
|
331
998
|
edges.push(makeEdge(ctx, classQname, REL_TYPES.IMPLEMENTS, `ApexInterface:${stripNs(impl, ctx.namespace)}`));
|
|
332
999
|
}
|
|
333
1000
|
// Methods
|
|
334
|
-
const
|
|
1001
|
+
const usedDecls = new Set();
|
|
1002
|
+
const maskedBody = maskApex(input.body);
|
|
1003
|
+
const methods = findMethods(cleaned, effectiveName);
|
|
1004
|
+
// Member-access field refs: each method falls back to the fields/properties
|
|
1005
|
+
// declared directly in ITS enclosing class body (constructor/initializer
|
|
1006
|
+
// locals and other inner classes' fields are excluded); types declared in
|
|
1007
|
+
// this file (inner classes, enums, interfaces) are never SObjects.
|
|
1008
|
+
const scopes = classScopes(cleaned);
|
|
1009
|
+
const localTypes = new Set([...cleaned.matchAll(/\b(?:class|enum|interface)\s+([A-Za-z_]\w*)/gi)].map((x) => (x[1] ?? "").toLowerCase()));
|
|
1010
|
+
// Fields of the method's class and of every enclosing class: an inner
|
|
1011
|
+
// class can use its outer class's (static) fields.
|
|
1012
|
+
const enclosingFields = (pos) => {
|
|
1013
|
+
const out = [];
|
|
1014
|
+
const seen = new Set();
|
|
1015
|
+
for (let c = scopes.classAt(pos); c >= 0 && !seen.has(c); c = scopes.classAt(c)) {
|
|
1016
|
+
seen.add(c);
|
|
1017
|
+
out.push(...scopes.fields(c));
|
|
1018
|
+
}
|
|
1019
|
+
return out;
|
|
1020
|
+
};
|
|
1021
|
+
const refScope = {
|
|
1022
|
+
selfName: effectiveName,
|
|
1023
|
+
localTypes,
|
|
1024
|
+
// File-wide set: used for code outside method bodies and for test-class
|
|
1025
|
+
// linkage. Each method gets its own set below (its params, its body's
|
|
1026
|
+
// declarations and its enclosing class's fields).
|
|
1027
|
+
localVars: localVarNames([
|
|
1028
|
+
...collectDecls(cleaned),
|
|
1029
|
+
...methods.flatMap((m) => paramDecls(m.params)),
|
|
1030
|
+
]),
|
|
1031
|
+
};
|
|
1032
|
+
// Methods declared in this file (inner classes' too: all are qnamed
|
|
1033
|
+
// under the outer class), for same-class call targets.
|
|
1034
|
+
const declared = new Map();
|
|
335
1035
|
for (const m of methods) {
|
|
1036
|
+
const d = declared.get(m.name.toLowerCase()) ?? { name: m.name, arities: new Set() };
|
|
1037
|
+
d.arities.add(m.params.length);
|
|
1038
|
+
declared.set(m.name.toLowerCase(), d);
|
|
1039
|
+
}
|
|
1040
|
+
const parentName = header.isInterface ? null : header.extendsClass;
|
|
1041
|
+
emitPageRefEdges(ctx, classQname, cleaned, edges);
|
|
1042
|
+
const methodQueries = [];
|
|
1043
|
+
const methodNodes = new Map();
|
|
1044
|
+
const classPaths = [];
|
|
1045
|
+
const classChildren = [];
|
|
1046
|
+
const declRanges = [];
|
|
1047
|
+
for (const m of methods) {
|
|
1048
|
+
const methodEdgeStart = edges.length;
|
|
336
1049
|
const methodQname = `ApexMethod:${effectiveName}.${m.name}(${m.params.length})`;
|
|
337
1050
|
const annotationsLower = m.annotations.map((a) => a.toLowerCase());
|
|
338
1051
|
const auraEnabled = annotationsLower.includes("auraenabled");
|
|
@@ -342,7 +1055,7 @@ export class ApexClassParser {
|
|
|
342
1055
|
const isTestMethod = annotationsLower.includes("istest") || annotationsLower.includes("testsetup");
|
|
343
1056
|
const httpVerbs = ["httpget", "httppost", "httpput", "httppatch", "httpdelete"].filter((v) => annotationsLower.includes(v));
|
|
344
1057
|
const isTestLabel = isTestMethod || (header.isTest && m.modifiers.includes("static"));
|
|
345
|
-
|
|
1058
|
+
const methodNode = makeNode(ctx, isTestLabel ? "TestMethod" : "ApexMethod", methodQname, {
|
|
346
1059
|
name: m.name,
|
|
347
1060
|
arity: m.params.length,
|
|
348
1061
|
returnType: m.returnType,
|
|
@@ -361,7 +1074,9 @@ export class ApexClassParser {
|
|
|
361
1074
|
// or @isTest on the enclosing class with the method static.
|
|
362
1075
|
// Never derived from filename.
|
|
363
1076
|
isTest: isTestLabel,
|
|
364
|
-
}, sha256(m.body))
|
|
1077
|
+
}, sha256(`${APEX_NODE_HASH_SALT}\n${m.body}`));
|
|
1078
|
+
nodes.push(methodNode);
|
|
1079
|
+
methodNodes.set(methodQname, methodNode);
|
|
365
1080
|
edges.push(makeEdge(ctx, classQname, REL_TYPES.CONTAINS_METHOD, methodQname));
|
|
366
1081
|
// Snippet: emit the raw method body from the ORIGINAL source (preserves
|
|
367
1082
|
// comments + strings) keyed on the method's qname.
|
|
@@ -394,9 +1109,37 @@ export class ApexClassParser {
|
|
|
394
1109
|
if (endLine != null)
|
|
395
1110
|
snippetRec.endLine = endLine;
|
|
396
1111
|
snippets.push(snippetRec);
|
|
1112
|
+
// Apex -> Vlocity calls (IP / DataRaptor), extracted from the raw body.
|
|
1113
|
+
// `rawBody` above locates the method by first textual occurrence of its
|
|
1114
|
+
// name (which can be a call site), so locate the declaration properly here.
|
|
1115
|
+
const decl = declRawBody(input.body, maskedBody, m, usedDecls);
|
|
1116
|
+
if (decl)
|
|
1117
|
+
declRanges.push(decl);
|
|
1118
|
+
const rawDecl = decl?.text ?? null;
|
|
1119
|
+
const vc = extractVlocityCalls(rawDecl ?? "", stringConstants, m.params);
|
|
1120
|
+
for (const k of vc.ipKeys)
|
|
1121
|
+
edges.push(makeEdge(ctx, methodQname, REL_TYPES.APEX_CALLS_IP, `IntegrationProcedure:${k}`));
|
|
1122
|
+
for (const d of vc.drNames)
|
|
1123
|
+
edges.push(makeEdge(ctx, methodQname, REL_TYPES.APEX_CALLS_DR, `DataRaptor:${d}`));
|
|
1124
|
+
if (rawDecl !== null) {
|
|
1125
|
+
setFieldNameLiterals(methodNode, rawDecl, [
|
|
1126
|
+
...paramDecls(m.params),
|
|
1127
|
+
...collectDecls(m.body),
|
|
1128
|
+
]);
|
|
1129
|
+
}
|
|
397
1130
|
// Body analysis (regex path — gated when parserMode === "ast").
|
|
398
1131
|
const body = m.body;
|
|
1132
|
+
const methodPaths = [];
|
|
1133
|
+
const accessPaths = [];
|
|
1134
|
+
const fieldAccesses = extractFieldAccesses(body, {
|
|
1135
|
+
decls: [...paramDecls(m.params), ...collectDecls(body)],
|
|
1136
|
+
outer: scopes.fields(scopes.classAt(m.startIdx)),
|
|
1137
|
+
excludedTypes: localTypes,
|
|
1138
|
+
paths: accessPaths,
|
|
1139
|
+
});
|
|
399
1140
|
if (parserMode === "ast") {
|
|
1141
|
+
setRelationshipPaths(methodNode, accessPaths);
|
|
1142
|
+
emitFieldAccessEdges(ctx, methodQname, fieldAccesses, edges, methodEdgeStart);
|
|
400
1143
|
// AST mode handles per-method edges in a single tree walk after this loop;
|
|
401
1144
|
// skip the regex extraction here to avoid duplicate / inconsistent edges.
|
|
402
1145
|
continue;
|
|
@@ -406,44 +1149,27 @@ export class ApexClassParser {
|
|
|
406
1149
|
// same signal so governor_risk_check works without SFGRAPH_APEX_PARSER=ast.
|
|
407
1150
|
const loopRanges = computeLoopRanges(body);
|
|
408
1151
|
const inLoopAt = (idx) => loopRanges.some((r) => idx > r.start && idx < r.end);
|
|
409
|
-
// SOQL
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
.filter((s) => s.length > 0 && !/[()*]/.test(s));
|
|
423
|
-
for (const f of selectFields) {
|
|
424
|
-
const fname = f.split(/\s+/)[0] ?? "";
|
|
425
|
-
if (/^[A-Za-z_][\w]*$/.test(fname)) {
|
|
426
|
-
edges.push(makeEdge(ctx, methodQname, REL_TYPES.READS_FIELD, `CustomField:${obj}.${fname}`));
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
sq = soqlRe.exec(body);
|
|
430
|
-
}
|
|
431
|
-
// SOSL
|
|
432
|
-
const soslRe = new RegExp(SOSL_RE.source, "gi");
|
|
433
|
-
if (soslRe.test(body)) {
|
|
434
|
-
edges.push(makeEdge(ctx, methodQname, REL_TYPES.EXECUTES_SOSL, "SOSL:*"));
|
|
435
|
-
}
|
|
436
|
-
// DML
|
|
437
|
-
const dmlRe = new RegExp(DML_RE.source, "gi");
|
|
438
|
-
let dm = dmlRe.exec(body);
|
|
439
|
-
while (dm !== null) {
|
|
440
|
-
const op = (dm[1] ?? "").toLowerCase();
|
|
441
|
-
edges.push(makeEdge(ctx, methodQname, REL_TYPES.EXECUTES_DML, `DML:${op}`, {
|
|
442
|
-
target: dm[2],
|
|
443
|
-
...(inLoopAt(dm.index) ? { inLoop: true } : {}),
|
|
444
|
-
}));
|
|
445
|
-
dm = dmlRe.exec(body);
|
|
1152
|
+
// SOQL: EXECUTES_SOQL + READS_FIELD for the outer query's fields.
|
|
1153
|
+
const methodChildren = [];
|
|
1154
|
+
emitInlineSoqlEdges(ctx, methodQname, body, inLoopAt, edges, methodPaths, methodChildren);
|
|
1155
|
+
setChildRelationships(methodNode, methodChildren);
|
|
1156
|
+
mergePaths(methodPaths, accessPaths);
|
|
1157
|
+
setRelationshipPaths(methodNode, methodPaths);
|
|
1158
|
+
emitFieldAccessEdges(ctx, methodQname, fieldAccesses, edges, methodEdgeStart);
|
|
1159
|
+
// SOSL (inLoop when any SOSL sits in a loop body) and dynamic queries
|
|
1160
|
+
// in a loop body (governor risks, T15 I-5).
|
|
1161
|
+
emitSoslEdge(ctx, methodQname, body, inLoopAt, edges);
|
|
1162
|
+
const dynInLoop = dynamicQueriesInLoop(body, inLoopAt);
|
|
1163
|
+
if (dynInLoop.length > 0) {
|
|
1164
|
+
methodNode.attributes.dynamicQueryInLoop = dynInLoop;
|
|
446
1165
|
}
|
|
1166
|
+
// DML (statements and Database.<op>(…) calls), linked to the object
|
|
1167
|
+
// when the record expression's type is known.
|
|
1168
|
+
const outerFields = enclosingFields(m.startIdx);
|
|
1169
|
+
emitDmlEdges(ctx, methodQname, body, inLoopAt, edges, {
|
|
1170
|
+
lookup: makeTypeLookup([...paramDecls(m.params), ...collectDecls(body)], outerFields),
|
|
1171
|
+
excludedTypes: localTypes,
|
|
1172
|
+
});
|
|
447
1173
|
// Named credentials (scan the ORIGINAL method body — strings are stripped from `body`).
|
|
448
1174
|
const ncSource = (() => {
|
|
449
1175
|
const idx = input.body.indexOf(m.name);
|
|
@@ -459,28 +1185,89 @@ export class ApexClassParser {
|
|
|
459
1185
|
let nc = ncRe.exec(ncSource);
|
|
460
1186
|
while (nc !== null) {
|
|
461
1187
|
edges.push(makeEdge(ctx, methodQname, REL_TYPES.CALLS_NAMED_CREDENTIAL, `NamedCredential:${stripNs(nc[1] ?? "", ctx.namespace)}`));
|
|
462
|
-
|
|
1188
|
+
// Continue over the SAME source we started on (ncSource). Re-executing a
|
|
1189
|
+
// stateful /g regex against a different string (`body`) reset its
|
|
1190
|
+
// lastIndex semantics and dropped every named credential after the first.
|
|
1191
|
+
nc = ncRe.exec(ncSource);
|
|
463
1192
|
}
|
|
464
|
-
// Calls to other static methods (best-effort)
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
1193
|
+
// Calls to other static methods (best-effort), constructors and
|
|
1194
|
+
// static-member references.
|
|
1195
|
+
// Scanned on the length-preserving mask (quotes kept, contents blanked)
|
|
1196
|
+
// so a string argument still counts toward the call's arity.
|
|
1197
|
+
const callCode = rawDecl === null ? body : maskApex(rawDecl);
|
|
1198
|
+
const methodScope = {
|
|
1199
|
+
...refScope,
|
|
1200
|
+
localVars: localVarNames([
|
|
1201
|
+
...paramDecls(m.params),
|
|
1202
|
+
...collectDecls(body),
|
|
1203
|
+
...enclosingFields(m.startIdx),
|
|
1204
|
+
]),
|
|
1205
|
+
};
|
|
1206
|
+
emitStaticCallEdges(ctx, methodQname, callCode, effectiveName, edges, methodScope.localVars);
|
|
1207
|
+
const callLookup = makeTypeLookup([...paramDecls(m.params), ...collectDecls(callCode)], outerFields);
|
|
1208
|
+
emitInstanceCallEdges(ctx, methodQname, callCode, {
|
|
1209
|
+
lookup: callLookup,
|
|
1210
|
+
scope: methodScope,
|
|
1211
|
+
selfName: effectiveName,
|
|
1212
|
+
parentName,
|
|
1213
|
+
declared,
|
|
1214
|
+
fields: outerFields,
|
|
1215
|
+
}, edges, methodEdgeStart);
|
|
1216
|
+
emitNameRefEdges(ctx, methodQname, callCode, rawDecl, { lookup: callLookup, scope: methodScope }, edges, methodEdgeStart);
|
|
1217
|
+
emitClassRefEdges(ctx, methodQname, callCode, methodScope, edges);
|
|
1218
|
+
// The signature is outside `callCode`: prepend it so parameter and
|
|
1219
|
+
// return types count for the method too.
|
|
1220
|
+
const sig = `${m.returnType} ${m.name}(${m.params.join(", ")});\n`;
|
|
1221
|
+
emitTypeRefEdges(ctx, methodQname, sig + callCode, methodScope, edges, methodEdgeStart);
|
|
1222
|
+
emitDescribeRefEdges(ctx, methodQname, callCode, edges);
|
|
1223
|
+
const queries = rawDecl === null ? [] : foldDynamicSoql(rawDecl);
|
|
1224
|
+
methodQueries.push(...queries);
|
|
1225
|
+
setDynamicSoqlSkips(methodNode, emitDynamicSoqlEdges(ctx, methodQname, queries, edges, methodEdgeStart));
|
|
1226
|
+
}
|
|
1227
|
+
// Code outside method bodies (field initializers, constructors, static
|
|
1228
|
+
// blocks, method signatures) is attributed to the class itself.
|
|
1229
|
+
if (parserMode !== "ast") {
|
|
1230
|
+
const outside = maskedBody.split("");
|
|
1231
|
+
for (const d of declRanges) {
|
|
1232
|
+
for (let k = d.start; k < d.start + d.text.length; k++) {
|
|
1233
|
+
if (outside[k] !== "\n")
|
|
1234
|
+
outside[k] = " ";
|
|
481
1235
|
}
|
|
482
|
-
sc = scRe.exec(body);
|
|
483
1236
|
}
|
|
1237
|
+
const rest = outside.join("");
|
|
1238
|
+
emitStaticCallEdges(ctx, classQname, rest, effectiveName, edges, refScope.localVars);
|
|
1239
|
+
// Outside method bodies a name is typed only when every declaration of
|
|
1240
|
+
// it agrees (no positional scoping across constructors / initialisers).
|
|
1241
|
+
const restDecls = collectDecls(rest);
|
|
1242
|
+
const restLookup = makeTypeLookup([], restDecls);
|
|
1243
|
+
emitInstanceCallEdges(ctx, classQname, rest, {
|
|
1244
|
+
lookup: restLookup,
|
|
1245
|
+
scope: refScope,
|
|
1246
|
+
selfName: effectiveName,
|
|
1247
|
+
parentName,
|
|
1248
|
+
declared,
|
|
1249
|
+
fields: restDecls,
|
|
1250
|
+
}, edges);
|
|
1251
|
+
emitNameRefEdges(ctx, classQname, rest, input.body, { lookup: restLookup, scope: refScope }, edges);
|
|
1252
|
+
emitClassRefEdges(ctx, classQname, rest, refScope, edges);
|
|
1253
|
+
emitTypeRefEdges(ctx, classQname, rest, refScope, edges);
|
|
1254
|
+
emitDescribeRefEdges(ctx, classQname, rest, edges);
|
|
1255
|
+
// Dynamic SOQL outside method bodies (constants, constructors): every
|
|
1256
|
+
// folded query in the file minus those already attributed to a method.
|
|
1257
|
+
const left = new Map();
|
|
1258
|
+
for (const q of methodQueries)
|
|
1259
|
+
left.set(q, (left.get(q) ?? 0) + 1);
|
|
1260
|
+
const classQueries = foldDynamicSoql(input.body).filter((q) => {
|
|
1261
|
+
const n = left.get(q) ?? 0;
|
|
1262
|
+
if (n > 0)
|
|
1263
|
+
left.set(q, n - 1);
|
|
1264
|
+
return n === 0;
|
|
1265
|
+
});
|
|
1266
|
+
setDynamicSoqlSkips(nodes.find((n) => String(n.qualifiedName) === classQname), emitDynamicSoqlEdges(ctx, classQname, classQueries, edges, 0));
|
|
1267
|
+
// Inline SOQL outside method bodies (static field initialisers,
|
|
1268
|
+
// constructors, initializer blocks) belongs to the class.
|
|
1269
|
+
const restLoops = computeLoopRanges(rest);
|
|
1270
|
+
emitInlineSoqlEdges(ctx, classQname, rest, (idx) => restLoops.some((r) => idx > r.start && idx < r.end), edges, classPaths, classChildren);
|
|
484
1271
|
}
|
|
485
1272
|
// AST-mode (and shadow mode): walk the parsed compilationUnit and emit
|
|
486
1273
|
// edges that the regex pass cannot derive (real call arity, dotted field
|
|
@@ -497,13 +1284,33 @@ export class ApexClassParser {
|
|
|
497
1284
|
if (parserMode === "both") {
|
|
498
1285
|
// Tag AST edges so a shadow diff can distinguish overlap.
|
|
499
1286
|
for (const e of extraction.edges) {
|
|
500
|
-
e.attributes.
|
|
1287
|
+
e.attributes.callResolution = "ast-shadow";
|
|
501
1288
|
edges.push(e);
|
|
502
1289
|
}
|
|
503
1290
|
}
|
|
504
1291
|
else {
|
|
505
|
-
|
|
1292
|
+
// Member-access field edges were already emitted per method; the AST
|
|
1293
|
+
// dotted-ref / select-list READS_FIELD to the same (src, dst) would
|
|
1294
|
+
// only duplicate them.
|
|
1295
|
+
const fieldKeys = new Set(edges
|
|
1296
|
+
.filter((e) => e.relType === REL_TYPES.READS_FIELD || e.relType === REL_TYPES.WRITES_FIELD)
|
|
1297
|
+
.map((e) => `${e.relType}|${String(e.srcQualifiedName)}|${String(e.dstQualifiedName).toLowerCase()}`));
|
|
1298
|
+
for (const e of extraction.edges) {
|
|
1299
|
+
const key = `${e.relType}|${String(e.srcQualifiedName)}|${String(e.dstQualifiedName).toLowerCase()}`;
|
|
1300
|
+
if (fieldKeys.has(key))
|
|
1301
|
+
continue;
|
|
1302
|
+
if (e.relType === REL_TYPES.READS_FIELD || e.relType === REL_TYPES.WRITES_FIELD)
|
|
1303
|
+
fieldKeys.add(key);
|
|
506
1304
|
edges.push(e);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
if (parserMode === "ast") {
|
|
1308
|
+
// "both" keeps the regex paths (regex is authoritative there).
|
|
1309
|
+
for (const [qn, paths] of extraction.relationshipPaths) {
|
|
1310
|
+
const target = methodNodes.get(qn) ?? nodes.find((n) => String(n.qualifiedName) === classQname);
|
|
1311
|
+
if (target)
|
|
1312
|
+
setRelationshipPaths(target, paths);
|
|
1313
|
+
}
|
|
507
1314
|
}
|
|
508
1315
|
if (process.env.SFGRAPH_DEBUG_INGEST === "1" && extraction.diagnostics.length > 0) {
|
|
509
1316
|
ctx.logger?.debug?.("apex ast diagnostics", {
|
|
@@ -521,16 +1328,51 @@ export class ApexClassParser {
|
|
|
521
1328
|
}
|
|
522
1329
|
// Test class linkage
|
|
523
1330
|
if (header.isTest) {
|
|
524
|
-
const { testTargets } = parseClassNameAndIsTest(cleaned);
|
|
1331
|
+
const { testTargets, lowerTargets } = parseClassNameAndIsTest(cleaned);
|
|
1332
|
+
// Lowercase-initial names (`new myController()`) are legal class
|
|
1333
|
+
// names but indistinguishable from variables here: keep them as
|
|
1334
|
+
// candidates the post-merge resolver matches against known classes.
|
|
1335
|
+
const candidates = [];
|
|
1336
|
+
for (const t of lowerTargets) {
|
|
1337
|
+
if (t.toLowerCase() === effectiveName.toLowerCase())
|
|
1338
|
+
continue;
|
|
1339
|
+
if (!isUserClassName(t, refScope) || refScope.localVars.has(t.toLowerCase()))
|
|
1340
|
+
continue;
|
|
1341
|
+
if (candidates.some((c) => c.toLowerCase() === t.toLowerCase()))
|
|
1342
|
+
continue;
|
|
1343
|
+
candidates.push(t);
|
|
1344
|
+
}
|
|
1345
|
+
const classNode = nodes.find((n) => String(n.qualifiedName) === classQname);
|
|
1346
|
+
if (classNode && candidates.length > 0)
|
|
1347
|
+
classNode.attributes.testTargetCandidates = candidates;
|
|
525
1348
|
for (const t of testTargets) {
|
|
526
1349
|
if (t === effectiveName)
|
|
527
1350
|
continue;
|
|
528
|
-
|
|
1351
|
+
// Only plausible user classes: not Apex system types/namespaces
|
|
1352
|
+
// (Database, System, Test, String), SObjects, types declared in this
|
|
1353
|
+
// file, or declared variables/fields used as call receivers.
|
|
1354
|
+
if (!isUserClassName(t, refScope) || refScope.localVars.has(t.toLowerCase()))
|
|
529
1355
|
continue;
|
|
530
1356
|
edges.push(makeEdge(ctx, classQname, REL_TYPES.IS_TEST_FOR, `ApexClass:${stripNs(t, ctx.namespace)}`));
|
|
531
1357
|
}
|
|
532
1358
|
}
|
|
533
|
-
|
|
1359
|
+
setChildRelationships(nodes.find((n) => String(n.qualifiedName) === classQname), classChildren);
|
|
1360
|
+
if (classPaths.length > 0) {
|
|
1361
|
+
const classNode = nodes.find((n) => String(n.qualifiedName) === classQname);
|
|
1362
|
+
if (classNode)
|
|
1363
|
+
setRelationshipPaths(classNode, classPaths);
|
|
1364
|
+
}
|
|
1365
|
+
return { nodes, edges: aggregateGovernorFacts(edges), snippets };
|
|
534
1366
|
}
|
|
535
1367
|
}
|
|
1368
|
+
/** Store relationship paths on a node (only when there are any). */
|
|
1369
|
+
function setRelationshipPaths(node, paths) {
|
|
1370
|
+
if (paths.length === 0)
|
|
1371
|
+
return;
|
|
1372
|
+
const attrs = node.attributes;
|
|
1373
|
+
const cur = attrs.apexRelationshipPaths ?? [];
|
|
1374
|
+
const merged = [...cur];
|
|
1375
|
+
mergePaths(merged, paths);
|
|
1376
|
+
attrs.apexRelationshipPaths = merged;
|
|
1377
|
+
}
|
|
536
1378
|
//# sourceMappingURL=class.js.map
|