feat: add removable reference feature vertical slice

This commit is contained in:
donghyeon-ka
2026-07-26 14:56:34 +09:00
parent 980981bc86
commit c11be43f20
87 changed files with 1881 additions and 1114 deletions
+14
View File
@@ -15,6 +15,7 @@ export type { ApplicationApi, ApplicationOutputPorts };
*/
export function createApplication(
outputPorts: ApplicationOutputPorts,
featureInputs: Readonly<Record<string, unknown>> = {},
): ApplicationApi {
const session = Object.freeze({
getSnapshot: () => outputPorts.session.getState(),
@@ -107,6 +108,18 @@ export function createApplication(
}
},
});
const installedFeatureInputs = Object.freeze({ ...featureInputs });
const features = Object.freeze({
has(featureId: string) {
return Object.hasOwn(installedFeatureInputs, featureId);
},
get(featureId: string) {
if (!Object.hasOwn(installedFeatureInputs, featureId)) {
throw new Error(`Application feature is not installed: ${featureId}`);
}
return installedFeatureInputs[featureId];
},
});
return Object.freeze({
session,
@@ -114,5 +127,6 @@ export function createApplication(
diagnostics,
runtime,
recovery,
features,
});
}
@@ -47,4 +47,8 @@ export type ApplicationApi = Readonly<{
| Readonly<{ action: "support"; reason: string }>
>;
}>;
features: Readonly<{
has(featureId: string): boolean;
get(featureId: string): unknown;
}>;
}>;
-6
View File
@@ -7,11 +7,5 @@ export type {
export type { ClockPort } from "../clock-port.js";
export type { QueryCachePort } from "../query-cache-port.js";
export type { ReleaseInfoPort } from "../release-info-port.js";
export type {
RequestContext,
ResourceCommandPort,
ResourceQueryPort,
Result,
} from "../resource-ports.js";
export type { StoragePort } from "../storage-port.js";
export type { TelemetryPort } from "../telemetry-port.js";
-28
View File
@@ -1,28 +0,0 @@
/**
* @template Query
* @template Model
* @typedef {{ execute(query: Query, context?: RequestContext): Promise<Result<Model>> }} ResourceQueryPort
*/
/**
* @template Command
* @template Model
* @typedef {{ execute(command: Command, context?: RequestContext): Promise<Result<Model>> }} ResourceCommandPort
*/
/**
* @typedef {{
* operationId: string,
* routeId: string,
* signal?: AbortSignal,
* idempotencyKey?: string
* }} RequestContext
*/
/**
* @template Value
* @typedef {{ ok: true, value: Value, meta?: Record<string, unknown> } |
* { ok: false, error: import("../../contracts/errors.js").ApiFailure }} Result
*/
export {};
@@ -1,16 +0,0 @@
/**
* @param {import("../../domain/models/resource.js").Resource} resource
* @param {(value: Date) => string} [formatDate]
*/
export function toResourceViewModel(
resource,
formatDate = (value) => new Intl.DateTimeFormat("ko-KR").format(value),
) {
return Object.freeze({
resourceId: resource.id,
title: resource.displayName,
createdAtLabel: resource.createdAt
? formatDate(new Date(resource.createdAt))
: null,
});
}