@cclabsnz/sf-core
v0.11.0
Published
Shared platform layer for CloudCounsel Salesforce sf plugins — API clients, org context, event log pull, report shell, and versioned IR schemas.
Maintainers
Readme
@cclabsnz/sf-core
Shared platform layer for the CloudCounsel Salesforce
sfplugins.
Architecture · Assurance case · Release tracking · Governance · Roadmap · Contributing · Security · Changelog
The read-only API surface, platform-behaviour knowledge, report shell and versioned IR
contracts behind @cclabsnz/sf-audit
(security auditing) and @cclabsnz/sf-orgintel (org intelligence).
What's in it
| Area | What it provides |
| --- | --- |
| API clients | SoqlClient, ToolingClient, RestClient, MetadataClient: read-only wrappers over a @salesforce/core Connection |
| Platform behaviour | FlowRepository, ApexRepository, isSalesforceId, describeSalesforceError, mapWithConcurrency |
| Event capture | EventBaselineStore, RTE_CATALOG, pullEventLogs, pullRealtimeEvents, CaptureManifest: bounded, resumable EventLogFile and Real-Time Event pulls |
| Activity engine | Turns captured EventLogFile rows into a classified, sessionised account of one actor's API activity. Classifies each call as a read, write, destructive, control or unknown operation — unknown is a real answer for undecomposed /composite requests, not a failure to classify — resolves them into their subrequests, groups events into runs and cycles, recovers the repeating sequence, and flags outliers on median and MAD. Pure functions; no org connection. |
| IR contracts | Typed interfaces plus JSON Schemas for coupling-graph, landscape-manifest and process-graph |
| Report shell | Branding resolution and embedded webfonts for self-contained HTML reports |
| Test invariants | Static guards that fail a build on org writes or network egress |
Why the platform layer exists
Salesforce has behaviours that are easy to get wrong and expensive to discover, and each one here was learned by running against a real org:
FlowDefinitionViewis a standard object, so querying it through Tooling answers "sObject type 'FlowDefinitionView' is not supported."Flow.Metadatais Tooling, and is strictly one row per query. AnId IN (...)batch is rejected outright, so bulk reads need bounded concurrency.ApexClasshas aSymbolTablecolumn;ApexTriggerdoes not, and selecting it fails the entire query.- Managed-package flows return a durable name (
ns__Flow-1) where an Id is expected; feeding that to aWHEREclause yieldsinvalid ID field. expr0is Salesforce's own aggregate alias and cannot be requested. An explicitCOUNT(Id) expr0is rejected with "alias is reserved: expr0".- Real-Time Event objects split unevenly between the streaming base and a retained-rows
*Store, and the split cannot be derived from the name. Roughly half the base objects reject aSELECToutright while their*Storeanswers fine.ListViewEventis queryable and has no Store at all;GuestUserAnomalyEventis the exact inverse.
Encoding these once means a consumer cannot rediscover them by shipping the bug first. The contract tests are the point: their mocks refuse exactly as an org refuses, so a query sent to the wrong API or asking for a non-existent column fails the build rather than degrading silently in production.
That guarantee has one edge, and it is worth stating because this package has been caught by
it. A contract test can only refuse what the code asks for; it cannot notice an object the
catalog never lists. A missing entry raises no error anywhere — the read is simply never
attempted, and the resulting absence is indistinguishable from an org that had nothing to
report. So the catalogs are re-probed against live orgs in both directions, asking not
only whether every listed object still behaves as listed but what describe returns that is
not listed at all. See docs/RELEASE_TRACKING.md.
Install
pnpm add @cclabsnz/sf-coreUsage
import { FlowRepository, ApexRepository, mapWithConcurrency } from '@cclabsnz/sf-core';
const flows = new FlowRepository(soqlClient, toolingClient);
const definitions = await flows.listDefinitions();
const { versions, managedSkipped } = FlowRepository.selectVersions(definitions);
// Flow.Metadata is one row per query; concurrency is the only lever.
await mapWithConcurrency(versions, 8, async (v) => {
const metadata = await flows.fetchMetadata(v.id);
// ...
});JSON Schemas are reachable as a subpath export:
import { loadSchema, schemaPath } from '@cclabsnz/sf-core';
const couplingGraphSchema = loadSchema('coupling-graph');Guarantees
Both are enforced as tests, not asserted in prose. Run them yourself with pnpm test:
- Read-only. No jsforce mutation API, HTTP write verb, or bulk/composite write path may appear in source. Every org request is a SOQL query, a REST GET, or a Metadata read.
- Local-first. No third-party HTTP client, telemetry endpoint, LLM call, websocket, or remote asset in a generated report. The only network destination is the org the operator authenticated against.
Versioning
The IR contracts (coupling-graph, landscape-manifest, process-graph) carry an explicit
version field and are the stable product surface. A breaking change to their shape is a
version bump on the contract, not a silent edit.
Licence
Apache-2.0, see LICENSE. Bundled fonts are SIL OFL 1.1; see
src/assets/fonts/NOTICE.md.
