Merge pull request 'Finish the console object-argument sweep' (#108) from fix/log-object-args-sweep into dev
Reviewed-on: #108
This commit is contained in:
commit
48c71f7902
4 changed files with 43 additions and 17 deletions
|
|
@ -5,3 +5,26 @@ import { twMerge } from 'tailwind-merge'
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs))
|
return twMerge(clsx(inputs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a denomination/count list for the journal.
|
||||||
|
*
|
||||||
|
* Electron's console bridge stringifies every console argument on its way to
|
||||||
|
* the journal, so passing the array itself arrives as `[object Object]` and
|
||||||
|
* the numbers are lost. Interpolate one of these instead. See CLAUDE.md,
|
||||||
|
* "Useful invariants when debugging".
|
||||||
|
*/
|
||||||
|
export function formatBays(rows: { denomination: number; count?: number }[]): string {
|
||||||
|
if (!rows?.length) return '(none)'
|
||||||
|
// `count` is optional on a device-config cassette: a preset can declare the
|
||||||
|
// denomination a bay holds without claiming how many notes are in it. Show
|
||||||
|
// that as unknown rather than as zero, which would read as a drained bay.
|
||||||
|
return rows.map((r) => `${r.denomination}x${r.count ?? '?'}`).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Same, for a denomination-keyed count map as `getInventory()` returns. */
|
||||||
|
export function formatInventory(inv: Record<number, number>): string {
|
||||||
|
const entries = Object.entries(inv ?? {})
|
||||||
|
if (!entries.length) return '(none)'
|
||||||
|
return entries.map(([denom, count]) => `${denom}x${count}`).join(' ')
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { ATMServices } from '@bitSpire/state-machine'
|
import type { ATMServices } from '@bitSpire/state-machine'
|
||||||
|
import { formatBays } from '@/lib/utils'
|
||||||
|
|
||||||
export interface CassetteConfig {
|
export interface CassetteConfig {
|
||||||
denomination: number
|
denomination: number
|
||||||
|
|
@ -126,7 +127,7 @@ export async function initializeHalServices(config: HalConfig): Promise<HalServi
|
||||||
|
|
||||||
const atmServices: Pick<ATMServices, 'dispenseCash' | 'getInventory'> = {
|
const atmServices: Pick<ATMServices, 'dispenseCash' | 'getInventory'> = {
|
||||||
dispenseCash: async (amounts) => {
|
dispenseCash: async (amounts) => {
|
||||||
console.log('[HAL] Dispensing:', amounts)
|
console.log(`[HAL] Dispensing: ${formatBays(amounts)}`)
|
||||||
|
|
||||||
// Re-initialize dispenser if it was closed after a previous error
|
// Re-initialize dispenser if it was closed after a previous error
|
||||||
if (!dispenser.initialized) {
|
if (!dispenser.initialized) {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import type { ATMServices, ATMContext } from '@bitSpire/state-machine'
|
||||||
|
|
||||||
// Import Electron types
|
// Import Electron types
|
||||||
import type {} from '@/types/electron'
|
import type {} from '@/types/electron'
|
||||||
|
import { formatBays, formatInventory } from '@/lib/utils'
|
||||||
|
|
||||||
// Check if we're running in Electron (electronAPI is exposed via preload)
|
// Check if we're running in Electron (electronAPI is exposed via preload)
|
||||||
const isElectron = typeof window !== 'undefined' && window.electronAPI !== undefined
|
const isElectron = typeof window !== 'undefined' && window.electronAPI !== undefined
|
||||||
|
|
@ -1092,7 +1093,7 @@ export function createATMServices(
|
||||||
* Dispense cash (mock for development)
|
* Dispense cash (mock for development)
|
||||||
*/
|
*/
|
||||||
dispenseCash: async (amounts) => {
|
dispenseCash: async (amounts) => {
|
||||||
console.log('[ATM Service] Dispensing cash:', amounts)
|
console.log(`[ATM Service] Dispensing cash: ${formatBays(amounts)}`)
|
||||||
|
|
||||||
// In production, this would interface with the Rust HAL
|
// In production, this would interface with the Rust HAL
|
||||||
// For now, simulate dispense delay
|
// For now, simulate dispense delay
|
||||||
|
|
@ -1269,7 +1270,7 @@ export function createATMServices(
|
||||||
20: 50, // 50 x $20 bills = $1000 capacity
|
20: 50, // 50 x $20 bills = $1000 capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[ATM Service] Inventory:', inventory)
|
console.log(`[ATM Service] Inventory: ${formatInventory(inventory)}`)
|
||||||
return inventory
|
return inventory
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import { initializeLightningServices, fetchBtcPrice } from '@/services/lightning
|
||||||
import { classifyInitError } from '@/services/init-error'
|
import { classifyInitError } from '@/services/init-error'
|
||||||
import { startOperatorConfigService, type OperatorConfigService } from '@/services/operator-config'
|
import { startOperatorConfigService, type OperatorConfigService } from '@/services/operator-config'
|
||||||
import { startOperatorFeesService, type OperatorFeesService } from '@/services/operator-fees'
|
import { startOperatorFeesService, type OperatorFeesService } from '@/services/operator-fees'
|
||||||
|
import { formatBays, formatInventory } from '@/lib/utils'
|
||||||
import type { HalConfig, HalServices } from '@/services/hal'
|
import type { HalConfig, HalServices } from '@/services/hal'
|
||||||
import type { MachineModel } from '@/config'
|
import type { MachineModel } from '@/config'
|
||||||
import type { LightningBackend } from '@/services/lightning'
|
import type { LightningBackend } from '@/services/lightning'
|
||||||
|
|
@ -179,7 +180,7 @@ async function loadInventoryFromDb(): Promise<Record<number, number> | null> {
|
||||||
if (isElectron && window.electronAPI) {
|
if (isElectron && window.electronAPI) {
|
||||||
try {
|
try {
|
||||||
const inv = await window.electronAPI.getInventory()
|
const inv = await window.electronAPI.getInventory()
|
||||||
console.log('[ATM] Loaded inventory from DB:', inv)
|
console.log(`[ATM] Loaded inventory from DB: ${formatInventory(inv)}`)
|
||||||
return inv
|
return inv
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[ATM] Failed to load inventory from DB:', e)
|
console.warn('[ATM] Failed to load inventory from DB:', e)
|
||||||
|
|
@ -245,7 +246,7 @@ const mockServices: ATMServices = {
|
||||||
},
|
},
|
||||||
|
|
||||||
dispenseCash: async (amounts) => {
|
dispenseCash: async (amounts) => {
|
||||||
console.log('[Mock] Dispensing cash:', amounts)
|
console.log(`[Mock] Dispensing cash: ${formatBays(amounts)}`)
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||||
return {
|
return {
|
||||||
bills: amounts.map((a) => ({
|
bills: amounts.map((a) => ({
|
||||||
|
|
@ -474,7 +475,7 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
// the availability beacon kept advertising a full cassette.
|
// the availability beacon kept advertising a full cassette.
|
||||||
if (inv === null) return
|
if (inv === null) return
|
||||||
persistedInventory.value = inv
|
persistedInventory.value = inv
|
||||||
console.log('[ATM] Persisted inventory updated:', inv)
|
console.log(`[ATM] Persisted inventory updated: ${formatInventory(inv)}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Detect Bitcoin network from a BOLT-11 invoice prefix (called once, persisted) */
|
/** Detect Bitcoin network from a BOLT-11 invoice prefix (called once, persisted) */
|
||||||
|
|
@ -1589,7 +1590,7 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
console.log('[ATM] Fiat currency:', devConfig.fiatCode)
|
console.log('[ATM] Fiat currency:', devConfig.fiatCode)
|
||||||
console.log('[ATM] Validator device:', devConfig.validator.device)
|
console.log('[ATM] Validator device:', devConfig.validator.device)
|
||||||
console.log('[ATM] Dispenser device:', devConfig.dispenser.device)
|
console.log('[ATM] Dispenser device:', devConfig.dispenser.device)
|
||||||
console.log('[ATM] Cassettes:', devConfig.dispenser.cassettes)
|
console.log(`[ATM] Cassettes: ${formatBays(devConfig.dispenser.cassettes)}`)
|
||||||
|
|
||||||
const halConfig = toHalConfig(devConfig)
|
const halConfig = toHalConfig(devConfig)
|
||||||
await initializeWithHalIpc(halConfig)
|
await initializeWithHalIpc(halConfig)
|
||||||
|
|
@ -1662,7 +1663,7 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
// HAL dispenseCash via IPC
|
// HAL dispenseCash via IPC
|
||||||
const halAtmServices: Pick<ATMServices, 'dispenseCash' | 'getInventory'> = {
|
const halAtmServices: Pick<ATMServices, 'dispenseCash' | 'getInventory'> = {
|
||||||
dispenseCash: async (amounts) => {
|
dispenseCash: async (amounts) => {
|
||||||
console.log('[ATM] Dispensing via IPC:', amounts)
|
console.log(`[ATM] Dispensing via IPC: ${formatBays(amounts)}`)
|
||||||
return await api.halDispense(amounts)
|
return await api.halDispense(amounts)
|
||||||
},
|
},
|
||||||
getInventory: async () => {
|
getInventory: async () => {
|
||||||
|
|
@ -1850,15 +1851,15 @@ export const useAtmStore = defineStore('atm', () => {
|
||||||
credentialIdHash: string
|
credentialIdHash: string
|
||||||
reason?: string
|
reason?: string
|
||||||
}) {
|
}) {
|
||||||
console.info('[Access] audit', {
|
// Truncate the hash — it's already non-reversible, but no need to splash
|
||||||
result: outcome.result,
|
// the full value across the journal. Interpolated rather than passed as an
|
||||||
role: outcome.role ?? null,
|
// object: the console bridge would stringify it to `[object Object]` and
|
||||||
// Truncate the hash in logs — it's already non-reversible, but no need to
|
// this line is the audit trail until #90 persists it to state.db.
|
||||||
// splash the full value across the journal.
|
console.info(
|
||||||
credentialIdHash: outcome.credentialIdHash.slice(0, 12),
|
`[Access] audit result=${outcome.result} role=${outcome.role ?? 'none'} ` +
|
||||||
reason: outcome.reason ?? null,
|
`hash=${outcome.credentialIdHash.slice(0, 12)} ` +
|
||||||
at: Date.now(),
|
`reason=${outcome.reason ?? 'none'} at=${Date.now()}`
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Grant terminal access after a credential (and any PIN) is authorized. */
|
/** Grant terminal access after a credential (and any PIN) is authorized. */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue