feat: close application architecture gaps
Quality / quality (ubuntu-latest) (push) Failing after 9m56s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-23 11:45:57 +05:30
parent d87b197224
commit 2e12656060
20 changed files with 548 additions and 23 deletions
+144
View File
@@ -0,0 +1,144 @@
export interface EntitlementPlan {
code: string;
name: string;
features: readonly string[];
allowance?: number;
price?: number;
[key: string]: unknown;
}
export interface EntitlementsOptions<TPlan extends EntitlementPlan> {
plans: () => Promise<readonly TPlan[]> | readonly TPlan[];
subscriptionFor: (subjectId: string) => Promise<string | null | undefined>;
fallback: string;
}
/** Resolve plans, features, and allowances from one server-owned catalog. */
export function defineEntitlements<TPlan extends EntitlementPlan>(
options: EntitlementsOptions<TPlan>,
) {
const all = async () => [...(await options.plans())];
const planFor = async (subjectId: string): Promise<TPlan> => {
const plans = await all();
const requested = await options.subscriptionFor(subjectId);
const plan =
plans.find((entry) => entry.code === requested) ??
plans.find((entry) => entry.code === options.fallback);
if (!plan) throw new Error(`WRN-ENTITLEMENTS: fallback plan '${options.fallback}' is missing`);
return plan;
};
return {
plans: all,
planFor,
async enabled(subjectId: string, feature: string) {
return (await planFor(subjectId)).features.includes(feature);
},
async allowance(subjectId: string) {
return Number((await planFor(subjectId)).allowance ?? 0);
},
};
}
export interface MeterPack {
code: string;
units: number;
[key: string]: unknown;
}
/** Prototype-safe, immutable pack lookup; clients select a code, never an amount. */
export function definePacks<TPack extends MeterPack>(entries: readonly TPack[]) {
const packs = new Map<string, Readonly<TPack>>();
for (const entry of entries) {
if (!entry.code.trim()) throw new TypeError("meter pack code cannot be empty");
if (!Number.isSafeInteger(entry.units) || entry.units < 1)
throw new RangeError(`meter pack '${entry.code}' units must be a positive safe integer`);
if (packs.has(entry.code)) throw new TypeError(`duplicate meter pack '${entry.code}'`);
packs.set(entry.code, Object.freeze({ ...entry }));
}
return Object.freeze({
get: (code: string) => packs.get(code),
has: (code: string) => packs.has(code),
list: () => [...packs.values()],
});
}
export type MeterResult = { ok: true } | { ok: false; reason: string; fault?: true };
export type MeterKind = "grant" | "purchase" | "refund" | "adjust";
export interface MeterStore {
balance(subjectId: string): Promise<number>;
reserve(subjectId: string, units: number, reason: string, reference: string): Promise<boolean>;
write(
subjectId: string,
units: number,
kind: MeterKind,
reason: string,
reference: string,
): Promise<void>;
}
export interface MeterOptions {
store: MeterStore;
label?: string;
onFault?: (error: unknown, operation: string) => void;
}
/** Non-throwing metering facade with validated units and explicit storage faults. */
export function defineMeter(options: MeterOptions) {
const label = options.label ?? "units";
const valid = (units: number): MeterResult | null =>
Number.isSafeInteger(units) && units > 0
? null
: { ok: false, reason: `${label} must be a positive whole number` };
const fault = (error: unknown, operation: string): MeterResult => {
options.onFault?.(error, operation);
return {
ok: false,
reason: error instanceof Error ? error.message : String(error),
fault: true,
};
};
const add = async (
kind: MeterKind,
subjectId: string,
units: number,
reason: string,
reference = "",
): Promise<MeterResult> => {
const invalid = valid(units);
if (invalid) return invalid;
try {
await options.store.write(subjectId, units, kind, reason, reference);
return { ok: true };
} catch (error) {
return fault(error, kind);
}
};
return {
balance: options.store.balance,
async reserve(
subjectId: string,
units: number,
reason: string,
reference = "",
): Promise<MeterResult> {
const invalid = valid(units);
if (invalid) return invalid;
try {
return (await options.store.reserve(subjectId, units, reason, reference))
? { ok: true }
: { ok: false, reason: "insufficient balance" };
} catch (error) {
return fault(error, "reserve");
}
},
grant: (subjectId: string, units: number, reason: string, reference = "") =>
add("grant", subjectId, units, reason, reference),
purchase: (subjectId: string, units: number, reason: string, reference = "") =>
add("purchase", subjectId, units, reason, reference),
refund: (subjectId: string, units: number, reason: string, reference = "") =>
add("refund", subjectId, units, reason, reference),
adjust: (subjectId: string, units: number, reason: string, reference = "") =>
add("adjust", subjectId, units, reason, reference),
};
}