fix: expose client fetch and signed adjustments
This commit is contained in:
@@ -235,7 +235,7 @@
|
||||
},
|
||||
"packages/auth": {
|
||||
"name": "@wrnexus/auth",
|
||||
"version": "0.8.17",
|
||||
"version": "0.8.18",
|
||||
"dependencies": {
|
||||
"@wrnexus/authz": "workspace:*",
|
||||
"@wrnexus/captcha": "workspace:*",
|
||||
@@ -342,7 +342,7 @@
|
||||
},
|
||||
"packages/csr": {
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.31",
|
||||
"version": "0.8.32",
|
||||
"dependencies": {
|
||||
"@wrnexus/core": "workspace:*",
|
||||
},
|
||||
@@ -501,7 +501,7 @@
|
||||
},
|
||||
"packages/metering": {
|
||||
"name": "@wrnexus/metering",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.3",
|
||||
},
|
||||
"packages/mobile": {
|
||||
"name": "@wrnexus/mobile",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/auth",
|
||||
"version": "0.8.17",
|
||||
"version": "0.8.18",
|
||||
"description": "Complete authentication, account security, MFA, passkeys, recovery, devices, risk, and audit system for WRNexusJS.",
|
||||
"type": "module",
|
||||
"sideEffects": false,
|
||||
|
||||
@@ -1672,6 +1672,11 @@ export function createAuthEngine(options: AuthEngineOptions): AuthEngine {
|
||||
(typeof metadata.deviceId === "string" ? metadata.deviceId : undefined),
|
||||
trusted: Boolean(rememberedDevice) || input.session?.trusted === true,
|
||||
fingerprint: typeof metadata.fingerprint === "string" ? metadata.fingerprint : undefined,
|
||||
metadata: {
|
||||
...(input.session?.metadata ?? {}),
|
||||
mfaVerifiedAt: now(),
|
||||
mfaMethod: input.method,
|
||||
},
|
||||
});
|
||||
user.lastLoginAt = now();
|
||||
user.updatedAt = now();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/csr",
|
||||
"version": "0.8.31",
|
||||
"version": "0.8.32",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -19,6 +19,21 @@ export { ACTION_RUNTIME } from "./action-runtime.ts";
|
||||
export { createApiClient } from "./api-client.ts";
|
||||
export type { ApiClientOptions, ApiRequest } from "./api-client.ts";
|
||||
|
||||
/** Public client transport used by `.wrn` client functions for dynamic URLs. */
|
||||
export function useFetch<T = unknown>(
|
||||
path: string,
|
||||
methodOrOptions?:
|
||||
string | { method?: string; query?: unknown; params?: unknown; body?: unknown; data?: unknown },
|
||||
input?: unknown,
|
||||
): Promise<T> {
|
||||
const client = (
|
||||
globalThis as typeof globalThis & { useFetch?: (...args: unknown[]) => Promise<T> }
|
||||
).useFetch;
|
||||
if (!client)
|
||||
return Promise.reject(new Error("WRN-CSR: useFetch is only available in the browser runtime"));
|
||||
return client(path, methodOrOptions, input);
|
||||
}
|
||||
|
||||
const CONTROLLER_SECTIONS = ["PRIMARY", "UI", "PIN"] as const;
|
||||
|
||||
function runtimeForMode(development: boolean): string {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/metering",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.3",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
|
||||
@@ -90,6 +90,10 @@ export function defineMeter(options: MeterOptions) {
|
||||
Number.isSafeInteger(units) && units > 0
|
||||
? null
|
||||
: { ok: false, reason: `${label} must be a positive whole number` };
|
||||
const validSigned = (units: number): MeterResult | null =>
|
||||
Number.isSafeInteger(units) && units !== 0
|
||||
? null
|
||||
: { ok: false, reason: `${label} adjustment must be a non-zero whole number` };
|
||||
const fault = (error: unknown, operation: string): MeterResult => {
|
||||
options.onFault?.(error, operation);
|
||||
return {
|
||||
@@ -138,7 +142,15 @@ export function defineMeter(options: MeterOptions) {
|
||||
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),
|
||||
async adjust(subjectId: string, units: number, reason: string, reference = "") {
|
||||
const invalid = validSigned(units);
|
||||
if (invalid) return invalid;
|
||||
try {
|
||||
await options.store.write(subjectId, units, "adjust", reason, reference);
|
||||
return { ok: true } as const;
|
||||
} catch (error) {
|
||||
return fault(error, "adjust");
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,3 +40,27 @@ test("meter distinguishes refusals from storage faults", async () => {
|
||||
fault: true,
|
||||
});
|
||||
});
|
||||
|
||||
test("adjust accepts signed non-zero amounts while other additions stay positive", async () => {
|
||||
const writes: number[] = [];
|
||||
const meter = defineMeter({
|
||||
store: {
|
||||
balance: async () => 0,
|
||||
reserve: async () => true,
|
||||
write: async (_subject, units) => {
|
||||
writes.push(units);
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(await meter.adjust("u1", -25, "correction")).toEqual({ ok: true });
|
||||
expect(await meter.adjust("u1", 10, "correction")).toEqual({ ok: true });
|
||||
expect(await meter.adjust("u1", 0, "correction")).toEqual({
|
||||
ok: false,
|
||||
reason: "units adjustment must be a non-zero whole number",
|
||||
});
|
||||
expect(await meter.grant("u1", -1, "invalid")).toEqual({
|
||||
ok: false,
|
||||
reason: "units must be a positive whole number",
|
||||
});
|
||||
expect(writes).toEqual([-25, 10]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user