Examples

RoleResolverPlugin Scenario

Use a resolve scenario to show why some extension points must produce exactly one final answer

RoleResolverPlugin Scenario

Scenario

You want one runtime path to ask:

  • "what is this user's current role?"

That is not a collaborative transformation problem. It is a single-answer resolution problem.

That makes it a classic resolve case.

Example

import {
  Plugin,
  type PluginResolveHook,
} from "@downcity/city/plugin";

class RoleResolverPlugin extends Plugin {
  readonly name = "role_resolver";
  readonly title = "Role Resolver";
  readonly description = "Resolves one final role for a user.";

  private readonly resolveRole: PluginResolveHook = async ({ value }) => {
    const body = value as { userId?: unknown };
    const userId = String(body.userId || "").trim();
    if (!userId) {
      throw new Error("userId is required");
    }
    if (userId === "owner") return "admin";
    return "member";
  };

  readonly resolves = {
    "auth.resolve_role": this.resolveRole,
  };
}

// City has registered role_resolver and bound it to entry's Agent/Workspace.
const role = await city.plugins.scope({ agent_id: agent.id, workspace_id: workspace.id }).resolve<{ userId: string }, string>(
  "auth.resolve_role",
  { userId: "owner" },
);

Why there cannot be two resolvers

Because the semantic question is not:

  • "let several plugins each add something"

It is:

  • "who is the final authority for this answer?"

That is why duplicate resolve registration fails in the current implementation.