Skip to content

v6.1.0-edge.4: Identity Claims, layered views on PostgreSQL, and hardened networking

The fifth Edge build of the 6.1 line, and a tighter one than its predecessor — 44 changesets, weighted toward closing gaps rather than opening surface. Three things stand out: Identity Claims arrives as core infrastructure for guest-record claiming and account linking; layered base views now work on PostgreSQL, which had thrown outright since the feature shipped; and outbound HTTP gets a real security boundary, with axios removed from the repository in favour of a new SSRF-aware @memberjunction/network-utils. Underneath, two genuine memory leaks in MJServer and a run of integration-connector fixes whose common theme is a run that stops should not lose its place.

Edge builds are prereleases. They publish under the edge dist-tag and never move latest.

  • Identity Claims. Core infrastructure for guest record claiming, account linking and invite verification (#4012), shipped with its redemption surface rather than as a schema-only foundation. Reads of MJ: Identity Claims are scoped to the requesting user, and redemption is decoupled from that read grant so claiming never requires handing out visibility of other people’s claims. Domain-specific GuestOrder / PersonAccountLink claim types were deliberately removed from core — those belong to BizApps, not to the platform.
  • @memberjunction/network-utils, and axios is gone from the repository. A new server-side package providing SSRF-safe URL validation and a hardened fetch. Server-side web/HTTP actions now block private, loopback, link-local (including the cloud metadata address 169.254.169.254) and reserved ranges, and re-validate on redirect — the step that makes an allowlist actually hold.
  • Layered base views work on PostgreSQL. CodeGen writes the inner view and restars the application-owned outer wrapper so g.* re-expands after the inner one regenerates. Previously PostgreSQLCodeGenProvider.generateBaseView threw on a layered entity, so the arrangement was SQL-Server-only.
  • CodeGen emits a composite index over an entity’s soft primary key — a gap where nothing in the stack had ever indexed one.
  • entityImportPackages — CodeGen now imports peer entity classes (embeds, related-record collections) from the npm package that owns them, instead of self-importing a string entityPackage.
  • A connector can declare a source field excluded from sync, and integration-object fields are indexed with the per-record field view memoised.
  • EscapeSQLString in @memberjunction/global — one canonical escape for string literals in SQL statements, clauses and ExtraFilter predicates, adopted across core packages.
  • Optional @IncludedSchemaNames on the CodeGen metadata-heal procedures, so an Open App migration can positively scope its heal instead of photographing sibling apps, with cascade-delete kept inside the schema.
  • The weekly AI model refresh (2026-08-24 report) lands with the release rather than after it.
  • Two real memory leaks in MJServer, both from process-wide subscriptions. ProviderBase.ensureInflightViewInvalidation() subscribed to MJGlobal’s event bus once per provider instance and never unsubscribed; SessionManager.heartbeatLastWrite was a plain unbounded Map in a class constructed fresh by every resolver that needs one, plus SessionJanitor. Both grew for the life of the process.
  • A restart no longer turns concurrent syncs into a queue — resume preserves the concurrency the original run had, instead of serialising what was parallel.
  • Mid-run watermark durability floor. A watermark-based connector previously had no durable position at all until its run ended, so a SIGKILL, OOM or container eviction lost the entire run’s progress. This is the watermark twin of the existing keyset checkpoint.
  • Discovery is bounded and observable — a deadline plus a watchdog — and it can recognise a rate limit again: its throttle test could never return true, so a throttled source looked like a slow one.
  • Retry-After is honoured between fetch attempts, with a rate token re-acquired on each retry, and a transport failure is retryable again — the classifier now reads the error’s cause chain rather than only its message, which undici moved the detail out of.
  • Reactivating a connection no longer blocks on a live schema introspect, and a failed refresh is no longer reported as a clean zero-count one.
  • Geocoding stops re-attempting an address it has already determined has no location.
  • Custom-column promotion is one RSU pass. PromoteForSync used to run the full migrate + CodeGen + compile pipeline once per entity; it also now clears the staging JSON and stops re-offering columns it already created, and an interrupted spread is recoverable rather than a dead end.
  • Open App migrations heal afterwards. mj migrate --schema and mj app install now run the core metadata-heal steps on both platforms, and CodeGen stops generating GRANT files for excludeSchemas entities — which had been failing Open App runs with Cannot find the object 'vw…' on sibling-schema permission files.
  • One covering index for every record-map lookup, with the per-record write path matching the batched one.
  • Two hot-path costs removed from MJGlobal with byte-identical behaviour (lazy ClassFactory diagnostics).
  • mj codegen manifest recognises @RegisterClassEx alongside @RegisterClass, in both the TypeScript-source and compiled-__decorate scan paths — classes registered the newer way were being tree-shaken out.
  • The T-SQL→PostgreSQL converter now FAILS on a statement it cannot parse instead of continuing. Silent partial conversion is the failure mode behind several of this cycle’s PostgreSQL defects.
  • PostgreSQL counterparts for this release’s migrations, verified by a clean mj migrate of all 72 migrations against a database built from nothing, then mj sync push writing 13,854 records with zero errors.
  • __mj.FileEntityRecordLink’s unique key omitted RecordID, so a file could attach to only one record per entity — in the table whose entire purpose is the generic file-to-any-record many-to-many (#3943). The v5.37 junction-table sweep’s “natural key is a pair of foreign keys” heuristic mechanically picked (EntityID, FileID) and dropped the soft key that makes a row distinct. Fixed on both platforms.
  • Ad-hoc SQL is refused for scope-limited sessions, and ResolverBase now escapes the filters it builds itself.
  • spRebindLayeredOuterView could never restar a layered outer view on PostgreSQL — it shipped with an undeclared v_starred variable, and PL/pgSQL compiles the whole body on first call, so the routine failed as a unit.
  • A new schema now gets its Application row on PostgreSQL (identifier quoting in the insert).
  • Records with no value for their soft primary key are refused rather than written unaddressable, and within-batch ExternalID identity is enforced before mapping.
  • An explicit MAX width (-1) survives discovery instead of being silently narrowed.
  • ProviderBase’s write-invalidation fan-out re-subscribes when the MJGlobal event bus is replaced. Its one-time wiring guard was a boolean, which cannot notice that MJGlobal.Reset() swapped the bus underneath it.
  • UserInfoEngine’s debounced settings flush no longer raises a process-level unhandled rejection.
  • DownloadMigrations fetches what skyway will actually run (recursively), and an empty download now fails instead of passing.
  • The UserCache export is kept alive for published consumers.
  • The retired GLM-4.7 Cerebras cost record uses a Status its entity actually allows.