API reference@evolu/commonResource › SharedResourceByKeyWithClaims

Defined in: packages/common/src/Resource.ts:979

Shared keyed Resources retained through claims.

A claim identifies an application-level owner such as an account, tenant, or open document. Resource keys identify the shared resources that owner needs, such as relay URLs, database names, or worker IDs.

Use this abstraction when each owner needs a set of keyed resources and those sets can overlap. Calling SharedResourceByKeyWithClaims.claim | claim retains every resource in one owner's set. Overlapping owners share one resource instance per key, and the resource remains alive until every ClaimLease retaining that key is released.

Relation queries reflect active claims, not physical resource liveness. With idleDisposeAfter, a resource can outlive its last claim.

Use SharedResourceByKey instead when callers only need independent leases by key and the application does not need to associate those leases with logical owners.

Two accounts can share a relay connection while one also uses a local-network transport:

import {
  assertEqual,
  createRun,
  createSharedResourceByKeyWithClaims,
  ok,
  type Brand,
  type Task,
} from "@evolu/common";

type AccountId = string & Brand<"AccountId">;
type TransportUrl = string & Brand<"TransportUrl">;

interface Connection extends Disposable {
  readonly url: TransportUrl;
}

const createConnection =
  (url: TransportUrl): Task<Connection> =>
  () =>
    ok({
      url,
      [Symbol.dispose]: () => {},
    });
// These literals stand in for values validated at application boundaries.
const accountA = "account-a" as AccountId;
const accountB = "account-b" as AccountId;
const relay = "wss://relay.example.com" as TransportUrl;
const localNetwork = "ws://local-network" as TransportUrl;

await using run = createRun();
await using transports = await run.ok(
  createSharedResourceByKeyWithClaims<TransportUrl, AccountId, Connection>(
    createConnection,
  ),
);

{
  // Account A retains both transports.
  using _accountATransports = await run.ok(
    transports.claim(accountA, [relay, localNetwork]),
  );

  {
    // Account B reuses the relay already retained by account A.
    using _accountBRelay = await run.ok(transports.claim(accountB, [relay]));
    assertEqual(
      transports.getClaimsForResource(relay),
      new Set([accountA, accountB]),
    );
    assertEqual(
      transports.getResourceKeysForClaim(accountA),
      new Set([relay, localNetwork]),
    );
  }

  // Releasing account B leaves account A's relay retain intact.
  assertEqual(transports.getClaimsForResource(relay), new Set([accountA]));
}

// Releasing the final claim removes the relation for both transports.
assertEqual(transports.getClaimsForResource(relay), new Set<AccountId>());
assertEqual(
  transports.getClaimsForResource(localNetwork),
  new Set<AccountId>(),
);

Extends

Methods

[asyncDispose]()

asyncDispose: PromiseLike<void>;

Defined in: node_modules/@typescript/old/lib/lib.esnext.disposable.d.ts:38

Inherited from

AsyncDisposable.[asyncDispose]

Properties

claim

readonly claim: (claim: C, resourceKeys: readonly [K, K]) => Task<ClaimLease>;

Defined in: packages/common/src/Resource.ts:997

Retains every resource key for claim, creating absent resources lazily.

Keys are snapshotted when this Task starts and acquired sequentially in input order.

resourceKeys must not contain logical duplicates according to SharedResourceByKeyWithClaimsOptions.resourceLookup. A duplicate is a programmer defect that panics the owning Run.

The returned ClaimLease releases all retains added by this call. Resource creation must succeed, so this Task has no recoverable error.

forEachResourceForClaim

readonly forEachResourceForClaim: (claim: C, callback: (resource: DistributiveOmit<T>, resourceKey: K) => void) => void;

Defined in: packages/common/src/Resource.ts:1046

Calls callback for each current resource retained by claim.

The keys and resources are snapshotted before the first callback runs. Resources remain valid until this synchronous iteration returns, even if a callback releases the final retaining ClaimLease. Callbacks must not retain or asynchronously use borrowed resources afterward. Does nothing when the claim retains no resources, including after this registry is disposed.

getClaimsForResource

readonly getClaimsForResource: (resourceKey: K) => ReadonlySet<C>;

Defined in: packages/common/src/Resource.ts:1027

Returns the current unique claims retaining resourceKey.

Returns an empty snapshot when no claim retains the key, including after this registry is disposed.

getResourceKeysForClaim

readonly getResourceKeysForClaim: (claim: C) => ReadonlySet<K>;

Defined in: packages/common/src/Resource.ts:1035

Returns the current unique resource keys retained by claim.

Returns an empty snapshot when the claim retains no keys, including after this registry is disposed.

snapshot

readonly snapshot: () => SharedResourceByKeyWithClaimsSnapshot<K, C>;

Defined in: packages/common/src/Resource.ts:1058

Returns current claim-retain and keyed-resource states.

While a claim is acquiring keys, resourcesByKey can include inner leases not yet reflected by claimLeaseCount or retainCountsByResourceKeyByClaim.

use

readonly use: <R, E, D>(claim: C, resourceKeys: readonly [K, K], callback: (resources: readonly [readonly [DistributiveOmit<T, typeof dispose | typeof asyncDispose>, K], readonly [DistributiveOmit<T, typeof dispose | typeof asyncDispose>, K]]) => Task<R, E, D>) => Task<R, E, D>;

Defined in: packages/common/src/Resource.ts:1013

Retains every resource key for claim, runs a Task with those resources, and releases the resulting ClaimLease after the Task settles.

The callback receives only the resources retained by this call, in input order. Other active ClaimLeases for the same logical claim are excluded. While this registry remains running, every borrowed resource remains valid until the callback Task settles. Disposing this registry is forceful: it drains the ClaimLease and may dispose resources before or while the callback Task runs.