File size: 3,353 Bytes
5cb63c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Gateway method descriptor types define the reusable contract shared by core, plugin, channel, and auxiliary methods.
import { normalizePluginGatewayMethodScope } from "../../shared/gateway-method-policy.js";
import { ADMIN_SCOPE, type OperatorScope } from "../operator-scopes.js";

/** Scope marker for methods that only authenticated node clients may call. */
export const NODE_GATEWAY_METHOD_SCOPE = "node" as const;
/** Scope marker for methods whose handler derives the required operator scope at runtime. */
export const DYNAMIC_GATEWAY_METHOD_SCOPE = "dynamic" as const;

/** Authorization scope attached to a gateway method descriptor. */
export type GatewayMethodScope =
  | OperatorScope
  | typeof NODE_GATEWAY_METHOD_SCOPE
  | typeof DYNAMIC_GATEWAY_METHOD_SCOPE;

/** Owner metadata used to keep core, plugin, channel, and auxiliary methods distinguishable. */
export type GatewayMethodOwner =
  | { kind: "core"; area: string }
  | { kind: "plugin"; pluginId: string }
  | { kind: "channel"; channelId: string }
  | { kind: "aux"; area: string };

/** Startup availability flag exposed to clients as retryable startup-unavailable errors. */
type GatewayMethodStartupAvailability = "available" | "unavailable-until-sidecars";
export type GatewayMethodProfileAccess = "independent" | "required";

export type GatewayMethodHandler = (opts: never) => unknown;

/** Complete metadata for one dispatchable gateway method. */
export type GatewayMethodDescriptor = {
  name: string;
  handler: GatewayMethodHandler;
  scope: GatewayMethodScope;
  owner: GatewayMethodOwner;
  profileAccess: GatewayMethodProfileAccess;
  since?: string;
  startup?: GatewayMethodStartupAvailability;
  controlPlaneWrite?: boolean;
  advertise?: boolean;
  description?: string;
};

/** Input descriptor shape before registry normalization trims and validates the method name. */
export type GatewayMethodDescriptorInput = Omit<
  GatewayMethodDescriptor,
  "name" | "profileAccess"
> & {
  name: string;
  profileAccess?: GatewayMethodProfileAccess;
};

/** Creates a plugin-owned method descriptor with plugin namespace scope normalization. */
export function createPluginGatewayMethodDescriptor(params: {
  pluginId: string;
  name: string;
  handler: GatewayMethodHandler;
  scope?: OperatorScope;
  profileAccess?: GatewayMethodProfileAccess;
}): GatewayMethodDescriptor {
  const normalizedScope = normalizePluginGatewayMethodScope(params.name, params.scope).scope;
  return {
    name: params.name,
    handler: params.handler,
    owner: { kind: "plugin", pluginId: params.pluginId },
    profileAccess: params.profileAccess ?? "required",
    scope: normalizedScope ?? ADMIN_SCOPE,
  };
}

/** Read-only method registry view used by request dispatch and method listing. */
export type GatewayMethodRegistryView = {
  /** Opaque registry handle carried into request scope by the gateway composition root. */
  pluginRegistry?: object;
  getHandler: (name: string) => GatewayMethodHandler | undefined;
  listMethods: () => string[];
  listAdvertisedMethods: () => string[];
  getScope: (name: string) => GatewayMethodScope | undefined;
  isStartupUnavailable: (name: string) => boolean;
  isControlPlaneWrite: (name: string) => boolean;
  requiresAuthenticatedProfile: (name: string) => boolean;
  descriptors: () => readonly GatewayMethodDescriptor[];
};