feat: define housekeeping foundation contracts
This commit is contained in:
1 parent
3b3d7780c9
commit
7d62a04f0e
10 files changed
+338
No files matched your search
@@ -0,0 +1,36 @@
|
||||
export type CapabilityRequirement =
|
||||
| { mode: "all"; slugs: readonly string[] }
|
||||
| { mode: "any"; slugs: readonly string[] };
|
||||
|
||||
export interface HousekeepingActor {
|
||||
id: number;
|
||||
username: string;
|
||||
rank: number;
|
||||
}
|
||||
|
||||
export interface HousekeepingCapabilityContext {
|
||||
actor: HousekeepingActor;
|
||||
isSuperAdmin: boolean;
|
||||
has(slug: string): boolean;
|
||||
hasAny(...slugs: string[]): boolean;
|
||||
hasAll(...slugs: string[]): boolean;
|
||||
}
|
||||
|
||||
function createCapabilityRequirement(
|
||||
mode: CapabilityRequirement["mode"],
|
||||
slugs: readonly string[],
|
||||
): CapabilityRequirement {
|
||||
if (slugs.length === 0) {
|
||||
throw new Error("capability requirement is empty");
|
||||
}
|
||||
|
||||
return { mode, slugs };
|
||||
}
|
||||
|
||||
export function anyCapability(...slugs: string[]): CapabilityRequirement {
|
||||
return createCapabilityRequirement("any", slugs);
|
||||
}
|
||||
|
||||
export function allCapabilities(...slugs: string[]): CapabilityRequirement {
|
||||
return createCapabilityRequirement("all", slugs);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { HousekeepingDomainId } from "../../migration/types";
|
||||
import type {
|
||||
CapabilityRequirement,
|
||||
HousekeepingCapabilityContext,
|
||||
} from "./capability";
|
||||
import type { HousekeepingResult } from "./result";
|
||||
|
||||
export interface HousekeepingCommand<I, O> {
|
||||
id: string;
|
||||
owner: HousekeepingDomainId;
|
||||
risk: "safe" | "sensitive";
|
||||
capability: CapabilityRequirement;
|
||||
requiresReason: boolean;
|
||||
execute(
|
||||
context: HousekeepingCapabilityContext,
|
||||
input: I,
|
||||
): Promise<HousekeepingResult<O>>;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,20 @@
|
||||
import type { HousekeepingDomainId } from "../../migration/types";
|
||||
import type { CapabilityRequirement } from "./capability";
|
||||
|
||||
export interface HousekeepingRouteDefinition {
|
||||
id: string;
|
||||
labelKey: string;
|
||||
href: string;
|
||||
capability: CapabilityRequirement;
|
||||
matchPrefixes?: readonly string[];
|
||||
}
|
||||
|
||||
export interface HousekeepingDomainManifest {
|
||||
id: HousekeepingDomainId;
|
||||
labelKey: string;
|
||||
descriptionKey: string;
|
||||
iconId: "inbox" | "users" | "file-text" | "gem" | "hotel" | "settings";
|
||||
previewHref: `/admin-next/${HousekeepingDomainId}`;
|
||||
capability: CapabilityRequirement;
|
||||
routes: readonly HousekeepingRouteDefinition[];
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { HousekeepingDomainId } from "../../migration/types";
|
||||
import type {
|
||||
CapabilityRequirement,
|
||||
HousekeepingCapabilityContext,
|
||||
} from "./capability";
|
||||
import type { HousekeepingResult } from "./result";
|
||||
|
||||
export type HousekeepingWorkItemSeverity = "info" | "warning" | "critical";
|
||||
export type HousekeepingWorkItemFreshness = "fresh" | "stale";
|
||||
export type HousekeepingInboxSourceAvailability = "available" | "unavailable";
|
||||
|
||||
export interface HousekeepingWorkItem {
|
||||
sourceId: string;
|
||||
itemId: string;
|
||||
deduplicationKey: string;
|
||||
domain: HousekeepingDomainId;
|
||||
capability: CapabilityRequirement;
|
||||
severity: HousekeepingWorkItemSeverity;
|
||||
occurredAt: string;
|
||||
titleKey: string;
|
||||
context?: Readonly<Record<string, unknown>>;
|
||||
href: string;
|
||||
freshness: HousekeepingWorkItemFreshness;
|
||||
}
|
||||
|
||||
export interface HousekeepingInboxSourceResult {
|
||||
items: readonly HousekeepingWorkItem[];
|
||||
availability: HousekeepingInboxSourceAvailability;
|
||||
}
|
||||
|
||||
export interface HousekeepingInboxSource {
|
||||
getItems(
|
||||
context: HousekeepingCapabilityContext,
|
||||
signal: AbortSignal,
|
||||
): Promise<HousekeepingResult<HousekeepingInboxSourceResult>>;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
export {
|
||||
allCapabilities,
|
||||
anyCapability,
|
||||
type CapabilityRequirement,
|
||||
type HousekeepingActor,
|
||||
type HousekeepingCapabilityContext,
|
||||
} from "./capability";
|
||||
export type { HousekeepingCommand } from "./command";
|
||||
export type {
|
||||
HousekeepingDomainManifest,
|
||||
HousekeepingRouteDefinition,
|
||||
} from "./domain";
|
||||
export type {
|
||||
HousekeepingInboxSource,
|
||||
HousekeepingInboxSourceAvailability,
|
||||
HousekeepingInboxSourceResult,
|
||||
HousekeepingWorkItem,
|
||||
HousekeepingWorkItemFreshness,
|
||||
HousekeepingWorkItemSeverity,
|
||||
} from "./inbox";
|
||||
export type { HousekeepingQuery } from "./query";
|
||||
export {
|
||||
fail,
|
||||
type HousekeepingErrorCode,
|
||||
type HousekeepingResult,
|
||||
ok,
|
||||
} from "./result";
|
||||
export type {
|
||||
HousekeepingSearchInput,
|
||||
HousekeepingSearchProvider,
|
||||
HousekeepingSearchResult,
|
||||
} from "./search";
|
||||
export type { HousekeepingWidgetDefinition } from "./widget";
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { HousekeepingDomainId } from "../../migration/types";
|
||||
import type {
|
||||
CapabilityRequirement,
|
||||
HousekeepingCapabilityContext,
|
||||
} from "./capability";
|
||||
import type { HousekeepingResult } from "./result";
|
||||
|
||||
export interface HousekeepingQuery<I, O> {
|
||||
id: string;
|
||||
owner: HousekeepingDomainId;
|
||||
capability: CapabilityRequirement;
|
||||
run(
|
||||
context: HousekeepingCapabilityContext,
|
||||
input: I,
|
||||
): Promise<HousekeepingResult<O>>;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
export type HousekeepingErrorCode =
|
||||
| "VALIDATION_FAILED"
|
||||
| "AUTHENTICATION_REQUIRED"
|
||||
| "CAPABILITY_DENIED"
|
||||
| "NOT_FOUND"
|
||||
| "CONFLICT"
|
||||
| "DEPENDENCY_UNAVAILABLE"
|
||||
| "PARTIAL_RESULT"
|
||||
| "INTERNAL_ERROR";
|
||||
|
||||
export type HousekeepingResult<T> =
|
||||
| { ok: true; data: T; correlationId: string }
|
||||
| {
|
||||
ok: false;
|
||||
error: { code: HousekeepingErrorCode };
|
||||
correlationId: string;
|
||||
};
|
||||
|
||||
export function ok<T>(data: T, correlationId: string): HousekeepingResult<T> {
|
||||
return { ok: true, data, correlationId };
|
||||
}
|
||||
|
||||
export function fail(
|
||||
code: HousekeepingErrorCode,
|
||||
correlationId: string,
|
||||
): HousekeepingResult<never> {
|
||||
return { ok: false, error: { code }, correlationId };
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { HousekeepingDomainId } from "../../migration/types";
|
||||
import type { HousekeepingCapabilityContext } from "./capability";
|
||||
import type { HousekeepingResult } from "./result";
|
||||
|
||||
export interface HousekeepingSearchInput {
|
||||
term: string;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export interface HousekeepingSearchResult {
|
||||
id: string;
|
||||
domain: HousekeepingDomainId;
|
||||
title: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export interface HousekeepingSearchProvider {
|
||||
search(
|
||||
context: HousekeepingCapabilityContext,
|
||||
input: HousekeepingSearchInput,
|
||||
): Promise<HousekeepingResult<readonly HousekeepingSearchResult[]>>;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { HousekeepingCapabilityContext } from "./capability";
|
||||
import type { HousekeepingResult } from "./result";
|
||||
|
||||
export interface HousekeepingWidgetDefinition {
|
||||
load(
|
||||
context: HousekeepingCapabilityContext,
|
||||
): Promise<HousekeepingResult<unknown>>;
|
||||
}
|
||||
Reference in new issue
Block a user