feat(core-shared): add Analyzed brand and isAnalyzed type guard
Adds the `Analyzed<F>` phantom-type brand to brands.ts and the `isAnalyzed` type-guard to brand-runtime.ts, mirroring the existing Instrumented/Captured/Audited pattern. Exports both from the conformance index so Story 04 (wireUseCase) can key off the brand without a circular dep. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
|||||||
isInstrumented,
|
isInstrumented,
|
||||||
isCaptured,
|
isCaptured,
|
||||||
isAudited,
|
isAudited,
|
||||||
|
isAnalyzed,
|
||||||
} from "@/conformance/brand-runtime";
|
} from "@/conformance/brand-runtime";
|
||||||
|
|
||||||
describe("brand-runtime", () => {
|
describe("brand-runtime", () => {
|
||||||
@@ -21,6 +22,7 @@ describe("brand-runtime", () => {
|
|||||||
expect(isInstrumented(fn)).toBe(false);
|
expect(isInstrumented(fn)).toBe(false);
|
||||||
expect(isCaptured(fn)).toBe(false);
|
expect(isCaptured(fn)).toBe(false);
|
||||||
expect(isAudited(fn)).toBe(false);
|
expect(isAudited(fn)).toBe(false);
|
||||||
|
expect(isAnalyzed(fn)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("predicates discriminate between brands", () => {
|
it("predicates discriminate between brands", () => {
|
||||||
@@ -39,6 +41,10 @@ describe("brand-runtime", () => {
|
|||||||
const audited = () => {};
|
const audited = () => {};
|
||||||
attachBrand(audited, "__audited");
|
attachBrand(audited, "__audited");
|
||||||
expect(isAudited(audited)).toBe(true);
|
expect(isAudited(audited)).toBe(true);
|
||||||
|
|
||||||
|
const analyzed = () => {};
|
||||||
|
attachBrand(analyzed, "__analyzed");
|
||||||
|
expect(isAnalyzed(analyzed)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("composing brands stacks them on the same function", () => {
|
it("composing brands stacks them on the same function", () => {
|
||||||
@@ -46,9 +52,11 @@ describe("brand-runtime", () => {
|
|||||||
attachBrand(fn, "__instrumented");
|
attachBrand(fn, "__instrumented");
|
||||||
attachBrand(fn, "__captured");
|
attachBrand(fn, "__captured");
|
||||||
attachBrand(fn, "__audited");
|
attachBrand(fn, "__audited");
|
||||||
|
attachBrand(fn, "__analyzed");
|
||||||
expect(isInstrumented(fn)).toBe(true);
|
expect(isInstrumented(fn)).toBe(true);
|
||||||
expect(isCaptured(fn)).toBe(true);
|
expect(isCaptured(fn)).toBe(true);
|
||||||
expect(isAudited(fn)).toBe(true);
|
expect(isAudited(fn)).toBe(true);
|
||||||
|
expect(isAnalyzed(fn)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("attached brands are non-writable and non-configurable", () => {
|
it("attached brands are non-writable and non-configurable", () => {
|
||||||
@@ -67,5 +75,8 @@ describe("brand-runtime", () => {
|
|||||||
expect(isInstrumented(42)).toBe(false);
|
expect(isInstrumented(42)).toBe(false);
|
||||||
expect(isInstrumented("string")).toBe(false);
|
expect(isInstrumented("string")).toBe(false);
|
||||||
expect(isInstrumented({})).toBe(false);
|
expect(isInstrumented({})).toBe(false);
|
||||||
|
expect(isAnalyzed(null)).toBe(false);
|
||||||
|
expect(isAnalyzed(undefined)).toBe(false);
|
||||||
|
expect(isAnalyzed(42)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
* commitment, not a mutable flag.
|
* commitment, not a mutable flag.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type Brand = "__instrumented" | "__captured" | "__audited";
|
import type { Analyzed } from "./brands";
|
||||||
|
|
||||||
|
type Brand = "__instrumented" | "__captured" | "__audited" | "__analyzed";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches the brand as a non-enumerable property on the given function.
|
* Attaches the brand as a non-enumerable property on the given function.
|
||||||
@@ -51,3 +53,7 @@ export function isCaptured(fn: unknown): boolean {
|
|||||||
export function isAudited(fn: unknown): boolean {
|
export function isAudited(fn: unknown): boolean {
|
||||||
return hasBrand(fn, "__audited");
|
return hasBrand(fn, "__audited");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isAnalyzed<F extends object>(fn: unknown): fn is Analyzed<F> {
|
||||||
|
return hasBrand(fn, "__analyzed");
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,3 +9,4 @@
|
|||||||
*/
|
*/
|
||||||
export type Instrumented<F> = F & { readonly __instrumented: true };
|
export type Instrumented<F> = F & { readonly __instrumented: true };
|
||||||
export type Captured<F> = F & { readonly __captured: true };
|
export type Captured<F> = F & { readonly __captured: true };
|
||||||
|
export type Analyzed<F> = F & { readonly __analyzed: true };
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export type { Instrumented, Captured } from "./brands";
|
export type { Instrumented, Captured, Analyzed } from "./brands";
|
||||||
export type { FeatureManifest, UseCaseManifest } from "./define-feature";
|
export type { FeatureManifest, UseCaseManifest } from "./define-feature";
|
||||||
export { defineFeature } from "./define-feature";
|
export { defineFeature } from "./define-feature";
|
||||||
export type {
|
export type {
|
||||||
@@ -22,6 +22,7 @@ export {
|
|||||||
isInstrumented,
|
isInstrumented,
|
||||||
isCaptured,
|
isCaptured,
|
||||||
isAudited,
|
isAudited,
|
||||||
|
isAnalyzed,
|
||||||
} from "./brand-runtime";
|
} from "./brand-runtime";
|
||||||
export { ConformanceError } from "./conformance-error";
|
export { ConformanceError } from "./conformance-error";
|
||||||
export { assertFeatureConformance } from "./assert-bindings";
|
export { assertFeatureConformance } from "./assert-bindings";
|
||||||
|
|||||||
Reference in New Issue
Block a user