feat: implement comprehensive logging system#16
Conversation
Phase 1 - RxDB Audit Plugin: - Add automatic audit logging for all document changes (insert/update/delete) - Track before/after state with change diff calculation - Exclude internal collections (logs, sync, notifications) Phase 2 - Enhanced Logger API: - Add getLogger() for hierarchical category-based loggers - Add .getChild() for creating child loggers - Add .with() for binding persistent context - Add lazy evaluation support for expensive computations Phase 3 - User-Facing Logs: - Add checkout started log in pay.tsx - Add payment completed log in payment-webview.tsx - Add customer assigned log in use-add-customer.ts - Add fee/shipping added success logs
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughAdds a hierarchical CategoryLogger API and migrates many modules to scoped/module- and order-scoped loggers (plus some i18n usage). Introduces a new RxDB audit-log plugin with tests that records insert/update/delete audit entries per collection and registers the plugin. Changes
Sequence Diagram(s)sequenceDiagram
participant App as RxDB (collections)
participant Plugin as RxDBAuditLogPlugin
participant Collector as Subscription Collector
participant Logs as Logs Collection
participant Err as Error Sink
App->>Plugin: collection created (setup hooks)
Plugin->>Collector: register insert$, update$, remove$ subscriptions
Note over App,Plugin: Insert flow
App->>Plugin: insert event (after)
Plugin->>Plugin: compute identifier & prepare entry
Plugin->>Logs: write audit entry { operation: "insert", collection, documentId, after }
Logs-->>Err: on write error -> swallow/log
Note over App,Plugin: Update flow
App->>Plugin: update event (before & after)
Plugin->>Plugin: calculateChanges(before, after)
Plugin->>Logs: write audit entry { operation: "update", changes, before, after }
Logs-->>Err: on write error -> swallow/log
Note over App,Plugin: Delete flow
App->>Plugin: remove event (before)
Plugin->>Logs: write audit entry { operation: "delete", collection, documentId, before }
Logs-->>Err: on write error -> swallow/log
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/core/src/screens/main/pos/hooks/use-add-customer.ts`:
- Around line 66-73: The log currently includes PII (customerEmail) in the
orderLogger.info call in use-add-customer.ts; remove or redact the email before
logging to avoid persisting identifiable data. Update the orderLogger.info
invocation (the block that logs "Customer assigned: {customerName}" and context
containing customerId, customerEmail, isGuest) to omit customerEmail or replace
it with a non-identifying value (e.g., a masked or hashed version) and keep only
safe fields like customerId and isGuest; ensure any masking/hashing is done via
a helper (e.g., maskEmail or hashEmail) so the change is localized and testable.
In `@packages/database/src/plugins/audit-log.ts`:
- Around line 148-152: In the collection.preSave hook (the function passed to
collection.preSave), remove the invalid "return data;" so the hook matches the
RxDB v16 signature (plainData, rxDocument) => void | Promise<void>; keep the
existing logic that stores the prior state via documentStatesBeforeSave.set(doc,
doc.toJSON()) and do not return or replace the incoming plainData in the preSave
handler for RxDocument.
🧹 Nitpick comments (7)
packages/utils/src/logger/index.ts (2)
36-39: Unused type export.
LazyContextis defined and exported but never used anywhere in the codebase or in theLoggerOptionsinterface. Consider removing it to avoid dead code, or integrate it intoLoggerOptions.contextif lazy context evaluation is intended.
448-451: Inconsistent lazy evaluation optimization.The
debug()method (lines 436-443) correctly skips lazy message resolution when the log level is disabled and no toast/db output is requested. However,info(),warn(),error(), andsuccess()always resolve the lazy message eagerly, negating the lazy evaluation benefit for these levels.For consistency, consider applying the same optimization pattern to higher-severity methods, or document that lazy evaluation is only beneficial for
debug().♻️ Example fix for info() — apply similar pattern to warn/error/success
info(message: LazyMessage, options?: LoggerOptions): void { + if (!shouldLog('info') && !options?.showToast && !options?.saveToDb) { + return; + } const resolvedMessage = resolveLazy(message); log.info(resolvedMessage, this.buildOptions(options)); }packages/core/src/screens/main/pos/cart/buttons/pay.tsx (3)
35-38: Inconsistent logger creation pattern.The
orderLoggeris created inside the callback, which differs from the pattern used inuse-add-fee.ts,use-add-shipping.ts, anduse-add-customer.tswhereorderLoggeris memoized withReact.useMemooutside the callback.While both approaches work, consider using
React.useMemofor consistency across the codebase and to avoid recreating the logger on every button press.♻️ Suggested refactor for consistency
+ const orderLogger = React.useMemo( + () => + checkoutLogger.with({ + orderId: currentOrder.uuid, + orderNumber: currentOrder.number, + }), + [currentOrder.uuid, currentOrder.number] + ); + const handlePay = React.useCallback(async () => { setLoading(true); - const orderLogger = checkoutLogger.with({ - orderId: currentOrder.uuid, - orderNumber: currentOrder.number, - }); - try {Then update the dependency array:
- }, [pushDocument, currentOrder, router, t, total]); + }, [pushDocument, currentOrder, router, t, total, orderLogger]);
58-65: Unusual translation pattern for error messages.Using
t('{message}', { message: error.message })passes the raw error message through the translation system, which won't provide any localization benefit since error messages are typically not in the translation catalog. Consider using a fixed translated string instead.♻️ Suggested fix
- orderLogger.error(t('{message}', { _tags: 'core', message: error.message || 'Error' }), { + orderLogger.error(t('Checkout failed', { _tags: 'core' }), { showToast: true, saveToDb: true, context: { errorCode: ERROR_CODES.TRANSACTION_FAILED, error: error instanceof Error ? error.message : String(error), }, });
43-55: Consider logging before navigation.The "Checkout started" log is recorded after
pushDocumentsucceeds but therouter.pushhappens immediately after. If navigation fails or the user quickly navigates away, the log timing may be confusing. Consider moving the log beforerouter.pushor adding a separate "Checkout navigation" log.packages/database/src/plugins/audit-log.ts (2)
110-114: Replaceconsole.errorwith the logger library.Per coding guidelines, use the logger library exclusively for logging instead of
console.log/console.error. While this is a catch block for audit log failures, it should still use the project's logging infrastructure.♻️ Suggested fix
+import log from '@wcpos/utils/logger'; + // ... in logAuditEvent function } catch (e) { // Don't let audit logging errors break the main operation - console.error('[AuditLog] Failed to write audit log:', e); + log.warn('[AuditLog] Failed to write audit log', { error: e }); }Based on coding guidelines: "Use the logger library exclusively for logging; DO NOT use
console.log."
42-46: Consider defensive handling for JSON.stringify.
JSON.stringifycan throw on circular references (though unlikely in RxDB documents). Consider wrapping in try/catch for robustness, or document the assumption that documents are always JSON-safe.♻️ Optional defensive fix
- // Use JSON comparison for deep equality - if (JSON.stringify(beforeVal) !== JSON.stringify(afterVal)) { - changes[key] = { old: beforeVal, new: afterVal }; - } + // Use JSON comparison for deep equality + try { + if (JSON.stringify(beforeVal) !== JSON.stringify(afterVal)) { + changes[key] = { old: beforeVal, new: afterVal }; + } + } catch { + // Fallback to reference comparison if JSON.stringify fails + if (beforeVal !== afterVal) { + changes[key] = { old: beforeVal, new: afterVal }; + } + }
| // Log customer assignment | ||
| orderLogger.info(t('Customer assigned: {customerName}', { _tags: 'core', customerName }), { | ||
| context: { | ||
| customerId: data.id, | ||
| customerEmail: data.email, | ||
| isGuest, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
PII logging concern: customer email.
Logging customerEmail (line 70) may raise compliance/privacy concerns (GDPR/CCPA) as email addresses are personally identifiable information. Consider whether this is necessary for debugging, or redact/omit it from logs that may be persisted or transmitted.
🔒 Suggested fix
orderLogger.info(t('Customer assigned: {customerName}', { _tags: 'core', customerName }), {
context: {
customerId: data.id,
- customerEmail: data.email,
isGuest,
},
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Log customer assignment | |
| orderLogger.info(t('Customer assigned: {customerName}', { _tags: 'core', customerName }), { | |
| context: { | |
| customerId: data.id, | |
| customerEmail: data.email, | |
| isGuest, | |
| }, | |
| }); | |
| // Log customer assignment | |
| orderLogger.info(t('Customer assigned: {customerName}', { _tags: 'core', customerName }), { | |
| context: { | |
| customerId: data.id, | |
| isGuest, | |
| }, | |
| }); |
🤖 Prompt for AI Agents
In `@packages/core/src/screens/main/pos/hooks/use-add-customer.ts` around lines 66
- 73, The log currently includes PII (customerEmail) in the orderLogger.info
call in use-add-customer.ts; remove or redact the email before logging to avoid
persisting identifiable data. Update the orderLogger.info invocation (the block
that logs "Customer assigned: {customerName}" and context containing customerId,
customerEmail, isGuest) to omit customerEmail or replace it with a
non-identifying value (e.g., a masked or hashed version) and keep only safe
fields like customerId and isGuest; ensure any masking/hashing is done via a
helper (e.g., maskEmail or hashEmail) so the change is localized and testable.
The preSave hook was receiving inconsistent parameters (rxDocument could be undefined or not a proper RxDocument instance) in certain cases like bulk operations or incremental patches. Changes: - Replace hooks (preSave, postInsert, postSave, postRemove) with Observable streams (insert$, update$, remove$) which are more reliable - Observable change events include previousDocumentData for updates, eliminating the need to manually track document state - Add comprehensive test suite with 17 tests covering: - Change calculation and field exclusion - Document identifier generation - Insert, update, delete event logging - Excluded collections (logs don't log themselves) - Bulk operations
Flexsearch index collections (e.g., logs-search-en_flexsearch) were being logged, which created a feedback loop when the logs collection's flexsearch index was updated. Added EXCLUDE_COLLECTION_PATTERNS with regex pattern /flexsearch$/ to skip any collection name ending with "flexsearch".
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/database/src/plugins/audit-log.ts`:
- Around line 131-134: In the catch block that currently calls
console.error('[AuditLog] Failed to write audit log:', e) replace that console
call with the project's logger error API (e.g., logger.error) so audit logging
failures use the central logger; keep the same descriptive message, include the
caught error object/stack, and ensure the handler still swallows the error (no
rethrow). Target the catch (e) handling in the audit-log.ts audit logging
function where the message '[AuditLog] Failed to write audit log:' is emitted.
🧹 Nitpick comments (9)
packages/database/src/plugins/audit-log.test.ts (1)
49-50: Consider using deterministic waiting patterns instead of fixed timeouts.The
waitForhelper with hardcoded milliseconds (100ms, 200ms used throughout) can lead to flaky tests on slower CI runners or under load. Consider polling the logs collection until the expected audit entry appears, with a timeout for failure cases.♻️ Suggested deterministic wait helper
-// Helper to wait for async operations -const waitFor = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); +// Helper to wait for a condition with timeout +const waitForCondition = async ( + condition: () => Promise<boolean>, + timeoutMs = 1000, + intervalMs = 10 +): Promise<void> => { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + if (await condition()) return; + await new Promise((resolve) => setTimeout(resolve, intervalMs)); + } + throw new Error('Condition not met within timeout'); +};packages/database/src/plugins/audit-log.ts (8)
38-39: Module-level state is acceptable but consider cleanup on database destruction.The
collectionSubscriptionsMap correctly tracks subscriptions per collection. However, there's no cleanup when a database is destroyed (only when collections are re-created). This could lead to stale entries in long-running processes with database recreation.
44-48: Replaceanywith stricter types.Per coding guidelines, avoid using
any. Consider usingunknownor a more specific record type.♻️ Suggested type improvement
function calculateChanges( - before: Record<string, any>, - after: Record<string, any>, + before: Record<string, unknown>, + after: Record<string, unknown>, excludeFields: string[] -): Record<string, { old: any; new: any }> | null { +): Record<string, { old: unknown; new: unknown }> | null {Based on coding guidelines, do not use 'any' type.
72-89: Replaceanyin parameter type.♻️ Suggested fix
-function getDocumentIdentifier(collectionName: string, data: Record<string, any>): string { +function getDocumentIdentifier(collectionName: string, data: Record<string, unknown>): string {Based on coding guidelines, do not use 'any' type.
94-102: Replaceanytypes in interface.♻️ Suggested fix
async function logAuditEvent( collection: RxCollection, operation: 'insert' | 'update' | 'delete', data: { documentId: string; - before?: Record<string, any>; - after?: Record<string, any>; - changes?: Record<string, { old: any; new: any }> | null; + before?: Record<string, unknown>; + after?: Record<string, unknown>; + changes?: Record<string, { old: unknown; new: unknown }> | null; } ): Promise<void> {Based on coding guidelines, do not use 'any' type.
150-159: Replaceanyin generic type parameter.♻️ Suggested fix
- const insertSub = collection.insert$.subscribe((changeEvent: RxChangeEvent<any>) => { + const insertSub = collection.insert$.subscribe((changeEvent: RxChangeEvent<unknown>) => {Based on coding guidelines, do not use 'any' type.
155-158: Fire-and-forget async call is intentional but could silently fail.The
logAuditEventcall is intentionally not awaited to avoid blocking the Observable stream. The function has internal error handling, so this is acceptable. However, consider adding a comment clarifying this is intentional.♻️ Optional: Add clarifying comment
+ // Fire-and-forget: errors are caught internally in logAuditEvent logAuditEvent(collection, 'insert', { documentId, after: documentData, });
162-180: Replaceanyin generic type parameter.♻️ Suggested fix
- const updateSub = collection.update$.subscribe((changeEvent: RxChangeEvent<any>) => { + const updateSub = collection.update$.subscribe((changeEvent: RxChangeEvent<unknown>) => {Based on coding guidelines, do not use 'any' type.
183-193: Replaceanyin generic type parameter.♻️ Suggested fix
- const removeSub = collection.remove$.subscribe((changeEvent: RxChangeEvent<any>) => { + const removeSub = collection.remove$.subscribe((changeEvent: RxChangeEvent<unknown>) => {Based on coding guidelines, do not use 'any' type.
| } catch (e) { | ||
| // Don't let audit logging errors break the main operation | ||
| console.error('[AuditLog] Failed to write audit log:', e); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Use logger library instead of console.error.
Per coding guidelines, use the logger library exclusively for logging.
♻️ Suggested fix
+import log from '@wcpos/utils/logger';
+
// ... in logAuditEvent function:
} catch (e) {
// Don't let audit logging errors break the main operation
- console.error('[AuditLog] Failed to write audit log:', e);
+ log.error('[AuditLog] Failed to write audit log:', e);
}Based on coding guidelines, use the logger library exclusively for logging.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (e) { | |
| // Don't let audit logging errors break the main operation | |
| console.error('[AuditLog] Failed to write audit log:', e); | |
| } | |
| } catch (e) { | |
| // Don't let audit logging errors break the main operation | |
| log.error('[AuditLog] Failed to write audit log:', e); | |
| } |
🤖 Prompt for AI Agents
In `@packages/database/src/plugins/audit-log.ts` around lines 131 - 134, In the
catch block that currently calls console.error('[AuditLog] Failed to write audit
log:', e) replace that console call with the project's logger error API (e.g.,
logger.error) so audit logging failures use the central logger; keep the same
descriptive message, include the caught error object/stack, and ensure the
handler still swallows the error (no rethrow). Target the catch (e) handling in
the audit-log.ts audit logging function where the message '[AuditLog] Failed to
write audit log:' is emitted.
- Add saveToDb: true to use-add-customer.ts so logs persist to database
- Migrate use-add-product.ts to use CategoryLogger pattern for consistency
- Both now use orderLogger.with({ orderId, orderNumber }) for context
Migrate 12 auth-related files to use getLogger() with namespaces: - wcpos.auth.login - Login/credential handling - wcpos.auth.site - Site connection/discovery - wcpos.auth.discovery - API endpoint discovery - wcpos.auth.testing - Auth method testing - wcpos.auth.user - User management - wcpos.auth.demo - Demo mode - wcpos.auth.oauth - OAuth flow (web/electron) - wcpos.auth.token - Token refresh - wcpos.auth.error - Auth error handling Also updates logger README with namespace convention documentation explaining the rationale for hierarchical categories.
Migrate POS-related files to use getLogger() with namespaces: - wcpos.pos.cart.save - Order save operations - wcpos.pos.cart.void - Order void/restore - wcpos.pos.cart.variation - Variation addition - wcpos.pos.cart.remove - Line item removal - wcpos.pos.cart.customer - Customer operations - wcpos.pos.utils - POS utility functions - wcpos.barcode.pos - POS barcode scanning
…paces Migrate sync and query-related files to use getLogger(): - wcpos.sync.state - Sync state management - wcpos.sync.collection - Collection replication - wcpos.sync.query - Query replication - wcpos.query.state - Query execution - wcpos.query.manager - Query registration Also migrate: - wcpos.db.create - Database creation - wcpos.http.client - HTTP client
Migrate to hierarchical logger namespaces: Mutations: - wcpos.mutations.document - Remote document mutations - wcpos.mutations.local - Local optimistic updates - wcpos.sync.push - Push documents to server - wcpos.sync.pull - Pull documents from server HTTP: - wcpos.http.error - HTTP error handler - wcpos.http.queue - Request queue - wcpos.http.state - Request state manager Database: - wcpos.db.clear - Database clearing - wcpos.db.parse - REST response parsing - wcpos.db.delete - Database deletion - wcpos.db.adapter - Storage adapter
Migrate to hierarchical logger namespaces: Print: - wcpos.print.native - Native printing (iOS/Android) - wcpos.print.web - Web printing - wcpos.print.external - External URL printing App: - wcpos.app.state - App state management - wcpos.app.hydration - Startup hydration steps - wcpos.app.translations - i18n loading Notifications: - wcpos.notifications.novu - Novu integration Barcode: - wcpos.barcode.detection - Scanner detection - wcpos.barcode.product - Product lookup
Migrate remaining files to hierarchical logger namespaces: Mutations (forms): - wcpos.mutations.product - Product edit form - wcpos.mutations.variation - Variation edit form - wcpos.mutations.customer - Customer add/edit forms - wcpos.mutations.order - Order edit form App: - wcpos.app.site - Site info hook - wcpos.app.validation - User validation - wcpos.app.stores - Store merging - wcpos.app.error - Root error boundary UI: - wcpos.ui.header - Header components - wcpos.ui.menu - User menu - wcpos.ui.settings - Settings/tax screen - wcpos.ui.filter - Filter bar - wcpos.ui.dnd - Drag and drop - wcpos.ui.product - Product components HTTP: - wcpos.http.rest - REST HTTP client Sync: - wcpos.sync.delete - Document deletion Debug: - wcpos.debug - Why-did-you-update hook - wcpos.debug.perf - Performance timing This completes the migration of all logger usages to the new hierarchical namespace convention.
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
packages/core/src/screens/main/pos/cart/buttons/void.tsx (1)
27-45: Indentation inconsistency in try-catch block.The code inside the
tryblock has inconsistent indentation. Lines 30-31 appear to be missing the proper indentation level, which affects readability.Suggested fix
const undoRemove = React.useCallback( async (orderJson) => { try { - await currentOrder.collection.insert(orderJson); - router.setParams({ orderID: orderJson.uuid }); - } catch (err) { + await currentOrder.collection.insert(orderJson); + router.setParams({ orderID: orderJson.uuid }); + } catch (err) { cartLogger.error('Failed to restore order', { showToast: true, saveToDb: true, context: { errorCode: ERROR_CODES.TRANSACTION_FAILED, orderId: orderJson.uuid, error: err instanceof Error ? err.message : String(err), }, }); } }, [router, currentOrder.collection] );packages/core/src/screens/main/pos/cart/add-customer.tsx (1)
65-106: Indentation inconsistency in handleSave callback.The try-catch block has inconsistent indentation. The code inside
try(lines 69-91) andcatch(lines 92-100) is not properly aligned with the block structure, and thefinallyblock (lines 101-103) has extra indentation.Suggested fix
const handleSave = React.useCallback( async (data: z.infer<typeof customerFormSchema>) => { setLoading(true); try { - const savedDoc = await create({ data }); - if (isRxDocument(savedDoc)) { + const savedDoc = await create({ data }); + if (isRxDocument(savedDoc)) { cartLogger.success(t('{name} saved', { _tags: 'core', name: format(savedDoc) }), { showToast: true, saveToDb: true, context: { customerId: savedDoc.id, customerName: format(savedDoc), }, }); if (currentOrder) { const json = savedDoc.toJSON(); await localPatch({ document: currentOrder, data: { customer_id: json.id, billing: json.billing, shipping: json.shipping, }, }); setOpen(false); } } - } catch (error) { + } catch (error) { cartLogger.error(t('{message}', { _tags: 'core', message: error.message || 'Error' }), { showToast: true, saveToDb: true, context: { errorCode: ERROR_CODES.TRANSACTION_FAILED, error: error instanceof Error ? error.message : String(error), }, }); } finally { - setLoading(false); - } + setLoading(false); + } }, [create, currentOrder, format, localPatch, t] );packages/core/src/screens/auth/hooks/use-login-handler.ts (1)
129-154: Unsafe property access onunknownerror type.In TypeScript strict mode,
errin catch clauses is typed asunknown. Accessingerr.message,err.name, anderr.codewithout type narrowing can cause runtime errors if the thrown value isn't an Error object.Suggested fix with proper type narrowing
} catch (err) { - const errorMessage = err.message || 'Failed to save WordPress credentials'; + const errorMessage = + err instanceof Error ? err.message : 'Failed to save WordPress credentials'; // Determine error type and code based on error characteristics let errorCode = ERROR_CODES.TRANSACTION_FAILED; // Default for DB operations - if (err.message?.includes('missing required parameters')) { + if (errorMessage.includes('missing required parameters')) { errorCode = ERROR_CODES.MISSING_REQUIRED_FIELD; - } else if (err.name === 'ValidationError') { + } else if (err instanceof Error && err.name === 'ValidationError') { errorCode = ERROR_CODES.CONSTRAINT_VIOLATION; - } else if (err.name === 'RxError') { + } else if ( + err instanceof Error && + err.name === 'RxError' && + 'code' in err && + typeof (err as { code?: string }).code === 'string' + ) { // Check for specific RxDB error codes - switch (err.code) { + switch ((err as { code: string }).code) { case 'RX1':
🤖 Fix all issues with AI agents
In `@packages/core/src/screens/auth/components/add-user-button.tsx`:
- Around line 9-11: Move the module-level constant declaration authLogger =
getLogger(['wcpos', 'auth', 'user']) so it appears after all import statements;
specifically, relocate the authLogger line below the import of useLoginHandler
(and any other imports) to restore proper import grouping and keep imports
contiguous while leaving getLogger usage intact.
In `@packages/core/src/screens/auth/components/demo-button.tsx`:
- Around line 9-11: The import order is wrong: move the useWcposAuth import so
all import statements appear before any runtime declarations; specifically,
place the "useWcposAuth" import line above the const authLogger = getLogger(...)
declaration in demo-button.tsx so that getLogger and authLogger remain after all
imports and avoid the syntax error.
In `@packages/core/src/screens/auth/components/url-input.tsx`:
- Around line 14-16: The file has an executable declaration siteLogger =
getLogger([...]) before the import useSiteConnect; move the import statement for
useSiteConnect so all imports (including useSiteConnect and getLogger) are
grouped at the top of url-input.tsx above any executable code; ensure the
getLogger call that defines siteLogger remains after the imports and that no
other executable statements precede the import block (update any import ordering
to keep related imports together).
In `@packages/core/src/screens/auth/components/wp-user.tsx`:
- Around line 28-30: Move the import so all imports are declared before any
runtime code: place "import { useT } from '../../../contexts/translations';"
(and any other imports) above the const authLogger = getLogger(['wcpos', 'auth',
'user']); declaration; ensure authLogger and getLogger remain unchanged and that
no other top-level statements precede imports in wp-user.tsx.
In `@packages/core/src/screens/auth/hooks/use-site-connect.ts`:
- Around line 7-9: The module has an import ordering bug: the const siteLogger =
getLogger(['wcpos', 'auth', 'site']) is declared before the import of useT which
will cause a syntax error; move the import statement for useT (and any other
imports) above all top-level statements or relocate the siteLogger declaration
below the imports so that all import declarations (including useT) appear at the
top of the file before calling getLogger/siteLogger.
In
`@packages/core/src/screens/main/hooks/use-rest-http-client/auth-error-handler.ts`:
- Around line 58-63: The file has a module parsing error because const
authLogger is declared before all imports; move the declaration of authLogger
below the import statements so all import ... from ... lines (including
getLogger, ERROR_CODES, and requestStateManager from
'@wcpos/hooks/use-http-client') appear first, then declare const authLogger =
getLogger(['wcpos', 'auth', 'error']);; ensure no other top-level executable
code appears before imports and keep the identifier names authLogger, getLogger,
ERROR_CODES, and requestStateManager intact.
In `@packages/core/src/screens/main/pos/cart/add-customer.tsx`:
- Around line 23-25: The declaration of the module-level constant cartLogger
interrupts the import block; move the cartLogger =
getLogger(['wcpos','pos','cart','customer']) statement so it appears after all
import statements (e.g., after the import of CustomerForm and
customerFormSchema) to restore proper import ordering and avoid having
executable code between imports.
In `@packages/core/src/screens/main/pos/cart/buttons/save-order.tsx`:
- Line 33: Fix the typo in the inline TODO comment in save-order.tsx: change
"geenric" to "generic" in the comment that reads "TODO; move this geenric
sanckbar to the pushDocument hook" (refer to the TODO near the save-order
component / pushDocument hook reference).
- Around line 11-13: The file declares the module-level constant cartLogger
(created via getLogger) between import statements; move the cartLogger
declaration so all import statements come first and then define cartLogger.
Locate the cartLogger variable and the getLogger usage and relocate that line
below the imports (after usePushDocument and any other imports) so imports
remain grouped, keeping the symbol names cartLogger and getLogger unchanged.
In `@packages/core/src/screens/main/pos/cart/buttons/void.tsx`:
- Around line 10-12: The file declares the module-level constant cartLogger
(created via getLogger) in the middle of import statements; move the cartLogger
declaration so all import statements (e.g., the import that pulls in
useDeleteDocument and any others) come first, then declare const cartLogger =
getLogger(['wcpos','pos','cart','void']) after the imports to restore correct
import ordering.
In `@packages/core/src/screens/main/pos/cart/edit-cart-customer.tsx`:
- Around line 20-22: The module-level constant cartLogger (created via
getLogger) is declared between import statements; move the cartLogger
declaration so all imports (including BillingAddressForm and
billingAddressSchema) appear first, then declare const cartLogger =
getLogger(['wcpos','pos','cart','customer']) after the import block to restore
proper import ordering and keep module-level constants below imports.
In `@packages/hooks/src/use-http-client/create-token-refresh-handler.ts`:
- Around line 77-79: The import for requestStateManager is placed after the
const declaration tokenLogger (created via getLogger), which will cause a syntax
error; move the import statement for requestStateManager above the tokenLogger
declaration (or move the tokenLogger declaration below all imports) so all
imports appear before any top-level const/variable declarations — update
create-token-refresh-handler.ts to ensure requestStateManager is imported before
tokenLogger/getLogger are used.
In `@packages/utils/src/logger/README.md`:
- Around line 91-155: The fenced namespace tree block in the README is missing a
language tag which triggers markdownlint MD040; update the opening fence for the
tree diagram to include a language specifier (use "text" or "plaintext") so the
block becomes ```text and leave the contents unchanged, i.e., modify the
namespace tree fenced code block in README.md accordingly.
🧹 Nitpick comments (7)
packages/core/src/screens/main/pos/products/use-barcode.ts (2)
3-9: Move the logger declaration after all imports.The
barcodeLoggerdeclaration on line 8 is placed between import statements. All imports should be grouped at the top of the file, followed by module-level declarations.Proposed fix
import { useObservableEagerState, useSubscription } from 'observable-hooks'; import { getLogger } from '@wcpos/utils/logger'; import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; import { useT } from '../../../../contexts/translations'; - -const barcodeLogger = getLogger(['wcpos', 'barcode', 'pos']); import { useUISettings } from '../../contexts/ui-settings'; import { useBarcodeDetection, useBarcodeSearch } from '../../hooks/barcodes'; import { useCollection } from '../../hooks/use-collection'; import { useAddProduct } from '../hooks/use-add-product'; import { useAddVariation } from '../hooks/use-add-variation'; import type { QuerySearchInput } from '../../components/query-search-input'; + +const barcodeLogger = getLogger(['wcpos', 'pos', 'barcode']);
8-8: Logger namespace order appears inconsistent with PR convention.Based on the PR's documented pattern (e.g.,
getLogger(['wcpos', 'pos', 'cart'])), the hierarchy should bewcpos→ domain (pos) → sub-domain (barcode). The current order['wcpos', 'barcode', 'pos']hasbarcodeandposswapped.Proposed fix
-const barcodeLogger = getLogger(['wcpos', 'barcode', 'pos']); +const barcodeLogger = getLogger(['wcpos', 'pos', 'barcode']);packages/core/src/screens/main/hooks/use-rest-http-client/auth-error-handler.ts (1)
111-135: Add explicit “why” comments for bothuseEffecthooks.Section headers describe behavior but not why the effects must run (vs. direct event handling or render). Please add a short rationale above each effect. As per coding guidelines, ...
✍️ Example comment additions
-// EFFECT: Trigger OAuth when flag is set +// EFFECT: Trigger OAuth when flag is set +// WHY: promptAsync must run as a side-effect after state flips, not during error handling. React.useEffect(() => { if (shouldTriggerAuth) { authLogger.debug('[AUTH_HANDLER] shouldTriggerAuth is true, calling promptAsync', { context: { siteName: site.name }, }); @@ -// EFFECT: Process OAuth response +// EFFECT: Process OAuth response +// WHY: response arrives asynchronously; side-effects (token save/toast) must run on change. React.useEffect(() => { if (!response) return;Also applies to: 141-251
packages/core/src/hooks/use-wcpos-auth/index.web.ts (1)
114-183: Add explicit “why” notes for the twouseEffectblocks. The current comments say what they do, but the guideline requires a brief rationale for usinguseEffecthere.✍️ Suggested comment updates
- // Handle expo-auth-session response + // Handle expo-auth-session response. + // WHY: response is produced asynchronously by expo-auth-session and must be reconciled when it changes. React.useEffect(() => { ... }, [response]); - // Check URL on mount for auth tokens (handles redirect return) + // Check URL on mount for auth tokens (handles redirect return). + // WHY: redirect modifies the URL outside React; a one-time mount check is required. React.useEffect(() => { ... }, []);As per coding guidelines, please add a short “why” note for each
useEffect.packages/core/src/screens/auth/components/add-user-button.tsx (1)
1-1: Use namespace import for React.Per coding guidelines, always use namespace imports for React (e.g.,
import * as React from 'react') instead of default imports.Suggested fix
-import React from 'react'; +import * as React from 'react';packages/core/src/screens/main/logs/filter-bar.tsx (1)
7-8: Type import interleaved with component imports.The
Querytype import is placed between component imports. While not a syntax error, grouping type imports separately or with related module imports improves readability.💡 Suggested organization
import { HStack } from '@wcpos/components/hstack'; -import type { Query } from '@wcpos/query'; import { ButtonPill, ButtonText } from '@wcpos/components/button'; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, } from '@wcpos/components/dropdown-menu'; import { Text } from '@wcpos/components/text'; +import type { Query } from '@wcpos/query';packages/core/src/screens/main/logs/cells/level.tsx (1)
24-24: Avoid usinganytype.Per coding guidelines, use strict types instead of
any. Consider using a more specific type or a generic constraint.Suggested fix
- const query = table.options.meta?.query as Query<any> | undefined; + const query = table.options.meta?.query as Query<import('@wcpos/database').LogCollection> | undefined;Alternatively, if the collection type varies, you could use
Query<RxCollection>with the appropriate import.
packages/hooks/src/use-http-client/create-token-refresh-handler.ts
Outdated
Show resolved
Hide resolved
| ``` | ||
| wcpos | ||
| ├── app # App initialization, state, hydration | ||
| │ ├── state # App state management | ||
| │ ├── hydration # Startup/hydration steps | ||
| │ ├── validation # User/site validation | ||
| │ └── translations # i18n loading | ||
| ├── auth # Authentication & authorization | ||
| │ ├── login # Login/logout flows | ||
| │ ├── oauth # OAuth popup/redirect handling | ||
| │ ├── token # Token refresh, expiry | ||
| │ ├── site # Site connection/discovery | ||
| │ └── discovery # API endpoint discovery | ||
| ├── pos # Point of Sale | ||
| │ ├── cart # Cart operations | ||
| │ │ ├── product # Add/remove products | ||
| │ │ ├── customer # Customer assignment | ||
| │ │ ├── fee # Fee line items | ||
| │ │ ├── shipping # Shipping line items | ||
| │ │ └── void # Order void/restore | ||
| │ └── checkout # Checkout flow | ||
| │ ├── init # Checkout started | ||
| │ └── payment # Payment processing | ||
| ├── sync # Data synchronization | ||
| │ ├── state # Sync state updates | ||
| │ ├── collection # Collection replication | ||
| │ ├── query # Query replication | ||
| │ ├── push # Push to server | ||
| │ ├── pull # Pull from server | ||
| │ └── delete # Delete operations | ||
| ├── db # Database operations | ||
| │ ├── create # DB creation | ||
| │ ├── clear # DB clearing | ||
| │ ├── adapter # Storage adapters | ||
| │ └── parse # Response parsing | ||
| ├── http # HTTP client & API | ||
| │ ├── client # Request handling | ||
| │ ├── error # Error parsing | ||
| │ ├── queue # Request queue | ||
| │ └── rest # REST API client | ||
| ├── mutations # Document CRUD | ||
| │ ├── document # Generic mutations | ||
| │ ├── local # Local optimistic updates | ||
| │ ├── product # Product edits | ||
| │ ├── customer # Customer edits | ||
| │ └── order # Order edits | ||
| ├── query # Query management | ||
| │ ├── state # Query execution | ||
| │ └── manager # Query registration | ||
| ├── ui # User interface | ||
| │ ├── menu # Menu interactions | ||
| │ ├── settings # Settings UI | ||
| │ ├── filter # Filter operations | ||
| │ └── dnd # Drag and drop | ||
| ├── barcode # Barcode scanning | ||
| │ ├── detection # Scanner detection | ||
| │ ├── product # Product lookup | ||
| │ └── pos # POS barcode handling | ||
| ├── print # Print operations | ||
| │ ├── native # Native printing | ||
| │ ├── web # Web printing | ||
| │ └── external # External URL printing | ||
| └── notifications # Push notifications | ||
| └── novu # Novu integration | ||
| ``` |
There was a problem hiding this comment.
Add a language tag to the fenced code block.
The namespace tree block lacks a language specifier, which breaks markdownlint MD040. Use text (or plaintext) for the tree diagram.
✅ Suggested fix
-```
+```text
wcpos
├── app # App initialization, state, hydration
│ ├── state # App state management
│ ├── hydration # Startup/hydration steps
│ ├── validation # User/site validation
│ └── translations # i18n loading
├── auth # Authentication & authorization
│ ├── login # Login/logout flows
│ ├── oauth # OAuth popup/redirect handling
│ ├── token # Token refresh, expiry
│ ├── site # Site connection/discovery
│ └── discovery # API endpoint discovery
├── pos # Point of Sale
│ ├── cart # Cart operations
│ │ ├── product # Add/remove products
│ │ ├── customer # Customer assignment
│ │ ├── fee # Fee line items
│ │ ├── shipping # Shipping line items
│ │ └── void # Order void/restore
│ └── checkout # Checkout flow
│ ├── init # Checkout started
│ └── payment # Payment processing
├── sync # Data synchronization
│ ├── state # Sync state updates
│ ├── collection # Collection replication
│ ├── query # Query replication
│ ├── push # Push to server
│ ├── pull # Pull from server
│ └── delete # Delete operations
├── db # Database operations
│ ├── create # DB creation
│ ├── clear # DB clearing
│ ├── adapter # Storage adapters
│ └── parse # Response parsing
├── http # HTTP client & API
│ ├── client # Request handling
│ ├── error # Error parsing
│ ├── queue # Request queue
│ └── rest # REST API client
├── mutations # Document CRUD
│ ├── document # Generic mutations
│ ├── local # Local optimistic updates
│ ├── product # Product edits
│ ├── customer # Customer edits
│ └── order # Order edits
├── query # Query management
│ ├── state # Query execution
│ └── manager # Query registration
├── ui # User interface
│ ├── menu # Menu interactions
│ ├── settings # Settings UI
│ ├── filter # Filter operations
│ └── dnd # Drag and drop
├── barcode # Barcode scanning
│ ├── detection # Scanner detection
│ ├── product # Product lookup
│ └── pos # POS barcode handling
├── print # Print operations
│ ├── native # Native printing
│ ├── web # Web printing
│ └── external # External URL printing
└── notifications # Push notifications
└── novu # Novu integration
-```
+```🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
91-91: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In `@packages/utils/src/logger/README.md` around lines 91 - 155, The fenced
namespace tree block in the README is missing a language tag which triggers
markdownlint MD040; update the opening fence for the tree diagram to include a
language specifier (use "text" or "plaintext") so the block becomes ```text and
leave the contents unchanged, i.e., modify the namespace tree fenced code block
in README.md accordingly.
The hierarchical namespace (wcpos.ui.dnd) already provides this context, making the bracketed prefix redundant.
These bracketed prefixes are now redundant since the hierarchical namespaces (wcpos.auth.error, wcpos.auth.oauth) provide this context.
Updated all POS cart hooks to include consistent order context: - orderUUID: RxDB document UUID (primary key) - orderID: WooCommerce order ID - orderNumber: Order number for display Files updated: - use-add-customer.ts - use-add-product.ts - use-add-shipping.ts - use-add-fee.ts - README.md (documentation)
Replace custom deepEqual function with lodash/isEqual which: - Is already used elsewhere in the codebase - Properly handles object key order differences - Is battle-tested This fixes false positives in audit logs when nested objects have the same values but different key orders (e.g., shipping address).
There was a problem hiding this comment.
Actionable comments posted: 16
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
packages/hooks/src/use-why-did-you-update.tsx (1)
11-39: Add a WHY comment for theuseEffect(required by guidelines).This effect intentionally runs after every render to capture previous props—please document that explicitly. As per coding guidelines, ...
💡 Minimal documentation fix
- React.useEffect(() => { + // WHY: Needs to run after each render to compare previous vs current props for debug logging. + React.useEffect(() => { if (previousProps.current) { // Get all keys from previous and current props // `@ts-ignore` const allKeys = Object.keys({ ...previousProps.current, ...props }); @@ // Finally update previousProps with current props for next hook call previousProps.current = props; - }); + }, [name, props]);packages/core/src/screens/main/contexts/use-push-document.ts (2)
34-42: Inconsistent error message extraction.Line 34 accesses
err.messagedirectly (which will beundefinediferris not an Error), while line 41 properly uses a type guard. Use consistent handling for both.🔧 Proposed fix
- syncLogger.error(t('Failed to prepare document data: {error}', { _tags: 'core', error: err.message }), { + const errorMessage = err instanceof Error ? err.message : String(err); + syncLogger.error(t('Failed to prepare document data: {error}', { _tags: 'core', error: errorMessage }), { showToast: true, saveToDb: true, context: { errorCode: ERROR_CODES.TRANSACTION_FAILED, documentId: doc.id, collectionName: doc.collection.name, - error: err instanceof Error ? err.message : String(err), + error: errorMessage, }, });
108-116: Same inconsistent error message extraction as inprepareDocumentData.Line 108 accesses
err.messagedirectly while line 115 uses a proper type guard. Apply the same fix pattern suggested earlier for consistency.🔧 Proposed fix
- syncLogger.error(t('Failed to update local document: {error}', { _tags: 'core', error: err.message }), { + const errorMessage = err instanceof Error ? err.message : String(err); + syncLogger.error(t('Failed to update local document: {error}', { _tags: 'core', error: errorMessage }), { showToast: true, saveToDb: true, context: { errorCode: ERROR_CODES.TRANSACTION_FAILED, documentId: latestDoc.id, collectionName: latestDoc.collection.name, - error: err instanceof Error ? err.message : String(err), + error: errorMessage, }, });packages/core/src/screens/main/components/product/variable-price.tsx (1)
75-80: Null dereference will crash whengetVariablePricesreturnsnull.When
variablePricesisnull(due to missing/invalid metaData), the guard on line 75 evaluates tofalseand falls through to line 80, wherevariablePrices[column.id]throws aTypeError.Proposed fix
/** * No variable prices found?! */ - if (variablePrices && !variablePrices[column.id]) { + if (!variablePrices || !variablePrices[column.id]) { return null; }packages/core/src/screens/main/receipt/email.tsx (1)
60-70: Consider deriving error code from actual error type.
ERROR_CODES.CONNECTION_REFUSEDis hardcoded, but email sending can fail for various reasons (timeout, 4xx/5xx responses, network issues). This may misrepresent the actual failure in logs and diagnostics.Suggested improvement
} catch (error) { + const errorCode = error?.code === 'ECONNREFUSED' + ? ERROR_CODES.CONNECTION_REFUSED + : ERROR_CODES.HTTP_REQUEST_FAILED; // or derive from error type httpLogger.error('Failed to send receipt email', { showToast: true, saveToDb: true, context: { - errorCode: ERROR_CODES.CONNECTION_REFUSED, + errorCode, orderId: orderID, email, error: error instanceof Error ? error.message : String(error), }, }); }
🤖 Fix all issues with AI agents
In `@packages/core/src/hooks/use-novu-notifications.ts`:
- Around line 6-9: The code references log[level](...) inside the
processNotificationBehavior callback but never imports the default logger,
causing a compile error; import the default logger (the same logger used
elsewhere) and replace or alias usages so processNotificationBehavior uses the
imported logger variable (or map novuLogger to log) — locate
processNotificationBehavior and the log[level] call, add the missing import for
the default logger, and ensure calls use that imported symbol consistently
alongside getLogger/novuLogger.
In `@packages/core/src/hooks/use-user-validation.ts`:
- Around line 12-14: Move the executable declaration "const appLogger =
getLogger(['wcpos', 'app', 'validation']);" to after all import statements so
imports come first; specifically ensure the import of "mergeStoresWithResponse"
and any other imports appear before the "appLogger" declaration (update
use-user-validation.ts so the symbol appLogger is declared only after all import
lines, keeping mergeStoresWithResponse and other imports at the top).
In `@packages/core/src/hooks/use-wcpos-auth/index.web.ts`:
- Around line 20-26: The constant oauthLogger is declared between import
statements which breaks the import block; move the declaration "const
oauthLogger = getLogger(['wcpos', 'auth', 'oauth']);" below the import group so
all imports (e.g., getLogger and the named imports buildAuthUrl, generateState,
getRedirectUri, parseAuthResult, and the types UseWcposAuthReturn,
WcposAuthConfig, WcposAuthResult) appear first, then declare oauthLogger,
ensuring no statements interrupt the import block.
In `@packages/core/src/screens/main/components/header/user-menu.tsx`:
- Around line 29-33: The const declaration uiLogger = getLogger([...]) is placed
before subsequent imports causing invalid module syntax; move the uiLogger
initialization below all import statements (after importing getLogger and
useAppState) so that all import declarations come first and then call getLogger
to assign uiLogger in the file (look for getLogger and uiLogger in
header/user-menu.tsx and ensure useAppState import remains above the const).
In `@packages/core/src/screens/main/contexts/ui-settings/provider.tsx`:
- Around line 10-12: The const uiLogger declaration is placed between import
statements which breaks ES module ordering; move the uiLogger initialization
(const uiLogger = getLogger(['wcpos','ui','settings'])) so it occurs after all
import statements have completed (i.e., below the import block and after the
last import), ensuring getLogger is still referenced correctly and no other
top-level code appears before imports.
In `@packages/core/src/screens/main/contexts/use-push-document.ts`:
- Around line 11-13: Move the import so all ES module imports come before any
module-level declarations: place the line importing useRestHttpClient (and any
other imports) above the const syncLogger = getLogger(['wcpos', 'sync', 'push'])
declaration in use-push-document.ts so that getLogger and syncLogger remain
after all import statements.
In `@packages/core/src/screens/main/customers/add.tsx`:
- Around line 16-18: The top-level const mutationLogger is declared before the
import statements causing a module syntax error; move the declaration of
mutationLogger (const mutationLogger = getLogger(['wcpos', 'mutations',
'customer'])) so it appears after all import statements (including imports of
CustomerForm and customerFormSchema) so that all imports run first and then the
logger is initialized.
In `@packages/core/src/screens/main/customers/edit/form.tsx`:
- Around line 12-15: The file has a module syntax error because const
mutationLogger (from getLogger) is declared before all import statements; move
the declaration of mutationLogger so it appears after the import block (i.e.,
after imports like useT, CustomerForm, customerFormSchema and any other
imports), ensuring all import statements remain at the top and then call
getLogger to assign mutationLogger.
In `@packages/core/src/screens/main/hooks/mutations/use-mutation.ts`:
- Around line 13-19: The file places the module-level constant mutationLogger
between import statements which breaks ESM/TS import ordering; move the const
mutationLogger = getLogger(['wcpos', 'mutations', 'document']) so that all
import statements (getLogger, ERROR_CODES, useLocalMutation, useT) appear first,
then declare mutationLogger afterwards; ensure no other top-level executable
code interrupts the import block and keep the exact identifier mutationLogger
and the getLogger call intact.
In `@packages/core/src/screens/main/hooks/use-rest-http-client/index.ts`:
- Around line 14-16: The file has a top-level statement (const httpLogger =
getLogger([...])) placed before import declarations, causing an ES module syntax
error; move the httpLogger initialization so all import statements (including
errorSubject, useAuthErrorHandler, createRefreshHttpClient) come first, then
call getLogger to assign httpLogger; ensure the unique symbols httpLogger and
getLogger remain unchanged and that imports for errorSubject,
useAuthErrorHandler, and createRefreshHttpClient appear above the httpLogger
line.
In `@packages/core/src/screens/main/orders/edit/form.tsx`:
- Around line 27-29: The module has an import-order error: the executable
declaration const mutationLogger = getLogger(['wcpos', 'mutations', 'order'])
comes before the import of BillingAddressForm and billingAddressSchema; move all
import statements (including the import that brings BillingAddressForm and
billingAddressSchema) to the top of the file so no executable code (like
mutationLogger/getLogger usage) appears before imports, ensuring imports precede
the mutationLogger declaration.
In `@packages/core/src/screens/main/products/edit/product/form.tsx`:
- Around line 20-26: The file currently declares const mutationLogger =
getLogger([...]) in the middle of import statements which breaks ESM/TypeScript
rules; move the declaration of mutationLogger (and any other runtime statements)
below all import lines so that imports like getLogger, ERROR_CODES, useT, and
CurrencyInput remain in a contiguous import block, then add the const
mutationLogger = getLogger(['wcpos','mutations','product']) immediately after
the final import.
In `@packages/core/src/screens/main/products/use-barcode.ts`:
- Around line 6-9: Move the const barcodeLogger declaration so all import
statements come first: keep the import of useT and import { useBarcodeDetection,
useBarcodeSearch } at the top, then declare const barcodeLogger =
getLogger([...]); (and ensure getLogger is imported if it isn't already).
Specifically, relocate the barcodeLogger line out of the import block and place
it after the import statements in use-barcode.ts so ES module imports remain
before any other statements.
In `@packages/core/src/screens/main/receipt/email.tsx`:
- Around line 14-17: The const declaration for httpLogger (const httpLogger =
getLogger(['wcpos', 'http', 'rest'])) is placed before an import (import {
FormErrors }) which causes a syntax error; move the httpLogger declaration below
all import statements (after imports like import { useT } and import {
FormErrors }) so all import declarations appear first, and ensure getLogger is
available in scope where you place httpLogger.
In `@packages/core/src/services/novu/client.ts`:
- Around line 152-154: The debug logging currently emits raw
subscriber/notification objects via novuLogger.debug('Novu: Subscriber
metadata', { context: { metadata } }) and a similar log around notification.data
(lines noted in review); replace these raw logs with a sanitized payload by
implementing and using a redaction helper (e.g., redactSensitive(obj) or
pickSafeKeys(obj)) that either whitelists safe keys or masks known sensitive
keys (licenseKey, email, name, payment/customer identifiers, notification.data
fields) before passing to novuLogger.debug; update both the metadata log and the
notification.data log calls to log only the redacted or whitelisted object and
include brief context text indicating redaction.
In `@packages/database/src/plugins/audit-log.ts`:
- Around line 39-40: The module-level collectionSubscriptions Map is never
cleaned up, causing leaks; add a preDestroyRxDatabase hook implementation (RxDB
plugin hook) that runs when a database is destroyed, locates the subscriptions
for that database (use the same key you used when adding entries to
collectionSubscriptions—e.g., a db name or identifier), iterates each
Subscription in the array and calls unsubscribe(), then removes the Map entry;
update the plugin (audit-log.ts) to register this preDestroyRxDatabase hook so
collectionSubscriptions is cleaned and memory is freed when RxDatabase instances
are torn down.
🟡 Minor comments (8)
packages/core/src/screens/main/hooks/barcodes/use-barcode-detection.ts-12-14 (1)
12-14: Import statement must precede non-import statements.The
import { useT }on line 14 appears after theconst barcodeLoggerdeclaration on line 13. ES modules require all imports to be at the top of the file before any other statements. Move the logger initialization after all imports.Proposed fix
import { getLogger } from '@wcpos/utils/logger'; import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; import { useAppState } from '../../../../contexts/app-state'; - -const barcodeLogger = getLogger(['wcpos', 'barcode', 'detection']); import { useT } from '../../../../contexts/translations'; + +const barcodeLogger = getLogger(['wcpos', 'barcode', 'detection']);packages/core/src/contexts/app-state/index.tsx-8-10 (1)
8-10: Import ordering issue and unused logger.Two issues with this change:
The
appLoggerdeclaration (line 9) is placed between import statements. All imports should be grouped at the top of the file before any executable code.
appLoggeris created but never used anywhere in this file. Either add logging calls where appropriate (e.g., inlogin,logout,switchStore) or remove the unused variable.🔧 Proposed fix
Move the logger declaration after all imports:
import * as React from 'react'; import type { StoreDocument } from '@wcpos/database'; import Platform from '@wcpos/utils/platform'; import { getLogger } from '@wcpos/utils/logger'; - -const appLogger = getLogger(['wcpos', 'app', 'state']); import { hydrateUserSession } from './use-hydration-suspense'; +import { hydrateUserSession } from './hydration-steps'; import type { HydrationContext } from './hydration-steps'; + +const appLogger = getLogger(['wcpos', 'app', 'state']);Then use
appLoggerfor state transitions, e.g.:const login = React.useCallback(async (...) => { appLogger.info('User login', { siteID, storeID }); // ... existing code }, [...]);packages/core/src/screens/main/hooks/use-print/use-print.web.ts-33-36 (1)
33-36: Typo in JSDoc comment.The documentation string contains a typo: "diaprintLogger" should be "dialog".
📝 Proposed fix
/** * Web implementation of usePrint hook using react-to-print. - * Prints DOM content via browser print diaprintLogger. + * Prints DOM content via browser print dialog. */packages/core/src/screens/main/contexts/use-delete-document.tsx-4-10 (1)
4-10: Import statement placement issue.The
useRestHttpClientimport on line 10 is placed after the module-scoped logger initialization on line 9. All imports should be grouped together before any module-level code.🔧 Suggested fix
import { getLogger } from '@wcpos/utils/logger'; import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; import { useT } from '../../../contexts/translations'; - -const syncLogger = getLogger(['wcpos', 'sync', 'delete']); import { useRestHttpClient } from '../hooks/use-rest-http-client'; + +const syncLogger = getLogger(['wcpos', 'sync', 'delete']);packages/core/src/screens/main/hooks/mutations/use-local-mutation.ts-13-19 (1)
13-19: Import statement placement issue.The
convertLocalDateToUTCStringimport on line 19 is placed after the module-scoped logger initialization on line 18. All imports should be grouped together at the top of the file before any module-level code execution.🔧 Suggested fix
import { getLogger } from '@wcpos/utils/logger'; import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; import { useT } from '../../../../contexts/translations'; - -const mutationLogger = getLogger(['wcpos', 'mutations', 'local']); import { convertLocalDateToUTCString } from '../../../../hooks/use-local-date'; + +const mutationLogger = getLogger(['wcpos', 'mutations', 'local']);packages/core/src/screens/main/components/order/filter-bar/cashier-pill.tsx-15-20 (1)
15-20: Import statement placement issue.The
useCustomerNameFormatimport on line 20 is placed after the module-scoped logger initialization on line 19. All imports should be grouped together before any module-level code.🔧 Suggested fix
import { getLogger } from '@wcpos/utils/logger'; import { useT } from '../../../../../contexts/translations'; - -const uiLogger = getLogger(['wcpos', 'ui', 'filter']); import useCustomerNameFormat from '../../../hooks/use-customer-name-format'; import { CustomerList } from '../../customer-select'; + +const uiLogger = getLogger(['wcpos', 'ui', 'filter']);packages/core/src/screens/main/settings/tax.tsx-23-28 (1)
23-28: Import statement placement issue.The
useTimport on line 28 is placed after the module-scoped logger initialization on line 27. All imports should be grouped together before any module-level code.🔧 Suggested fix
import { getLogger } from '@wcpos/utils/logger'; import { useAppState } from '../../../contexts/app-state'; - -const uiLogger = getLogger(['wcpos', 'ui', 'settings']); import { useT } from '../../../contexts/translations'; + +const uiLogger = getLogger(['wcpos', 'ui', 'settings']); import { FormErrors } from '../components/form-errors';packages/core/src/screens/main/components/header/user-menu.tsx-92-94 (1)
92-94: Incorrect error logging pattern.The
erris passed as a second argument, but theCategoryLogger.error()method expects an options object with acontextproperty. The error won't be properly captured in the log.🔧 Proposed fix
} catch (err) { - uiLogger.error('Failed to clear database:', err); + uiLogger.error('Failed to clear database', { + context: { + error: err instanceof Error ? err.message : String(err), + }, + }); }
🧹 Nitpick comments (12)
packages/hooks/src/use-why-did-you-update.tsx (1)
7-29: Replaceanywith strict props typing (avoid@ts-ignore).This violates the “no
any” rule and forces repeated ignores. Prefer a typed record/generic to keep type safety. As per coding guidelines, ...♻️ Suggested typing cleanup
-function useWhyDidYouUpdate(name: string, props: any) { +function useWhyDidYouUpdate<T extends Record<string, unknown>>(name: string, props: T) { // Get a mutable ref object where we can store props ... // ... for comparison next time this hook runs. - const previousProps = React.useRef(); + const previousProps = React.useRef<T>(); React.useEffect(() => { if (previousProps.current) { // Get all keys from previous and current props - // `@ts-ignore` - const allKeys = Object.keys({ ...previousProps.current, ...props }); + const allKeys = Object.keys({ ...previousProps.current, ...props }); // Use this object to keep track of changed props - const changesObj = {}; + const changesObj: Record<string, { from: unknown; to: unknown }> = {}; // Iterate through keys allKeys.forEach((key) => { // If previous is different from current - // `@ts-ignore` if (previousProps.current[key] !== props[key]) { // Add to changesObj - // `@ts-ignore` changesObj[key] = { - // `@ts-ignore` from: previousProps.current[key], to: props[key], }; } });packages/core/src/contexts/translations/index.tsx (1)
10-12: Move logger instantiation after all imports.The
const appLoggerdeclaration is placed between import statements, which breaks standard import grouping conventions. Move it after all imports for consistent code organization.Suggested reordering
import { getLogger } from '@wcpos/utils/logger'; import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; import CustomCache from './cache'; - -const appLogger = getLogger(['wcpos', 'app', 'translations']); import { useLocale } from '../../hooks/use-locale'; import { useAppState } from '../app-state'; + +const appLogger = getLogger(['wcpos', 'app', 'translations']); export const TranslationContext = React.createContext<TxNative['translate']>(null);packages/core/src/screens/main/contexts/use-push-document.ts (1)
54-54: Avoid usinganytype.The
json: anyparameter type violates the coding guideline requiring strict types. Consider using a generic or a more specific type for the document data being sent to the server.As per coding guidelines: "Do not use 'any' type; use strict types and generics instead."
packages/core/src/screens/main/components/product/variable-price.tsx (2)
12-14: Move the type import above the logger initialization.The type import on line 14 should be grouped with other imports at the top of the file, before any module-level code executes.
Suggested reordering
import { getLogger } from '@wcpos/utils/logger'; import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; + +import type { CellContext } from '@tanstack/react-table'; import { PriceWithTax } from './price-with-tax'; const uiLogger = getLogger(['wcpos', 'ui', 'product']); - -import type { CellContext } from '@tanstack/react-table';
21-21: Add type annotation tometaDataparameter.The
metaDataparameter is implicitly typed asany. Per coding guidelines, use strict types instead.Suggested fix
-function getVariablePrices(metaData) { +function getVariablePrices(metaData: ProductDocument['meta_data'] | undefined) {packages/core/src/screens/main/receipt/email.tsx (1)
44-73: Inconsistent indentation in try-catch-finally block.The indentation is inconsistent: line 45 uses 4 tabs while lines 46-59 use 3 tabs. Consider normalizing for readability.
packages/core/src/hooks/use-novu-notifications.ts (1)
270-277: Add an explicit “why useEffect” comment for compliance.The effect sets up external subscriptions and async side effects; per guidelines, add a brief comment stating why this must live in
useEffectand can’t be done during render or an event handler.As per coding guidelines, add a one‑line justification.
✍️ Suggested comment
- React.useEffect(() => { + // useEffect is required here to manage external subscriptions and async side effects. + React.useEffect(() => {packages/core/src/screens/main/settings/tax.tsx (1)
137-141: Consider adding user feedback for restore failure.The error is logged but the user receives no visual feedback when the restore operation fails. Consider adding
showToast: trueand an appropriate error code for better user experience.♻️ Suggested improvement
} catch (error) { uiLogger.error('Failed to restore server settings', { + showToast: true, context: { error: error instanceof Error ? error.message : String(error) }, }); } finally {packages/core/src/screens/main/contexts/use-delete-document.tsx (1)
43-53: Consider dynamic error code mapping.The error code is hardcoded to
CONNECTION_REFUSED, but deletion can fail for various reasons (permissions, not found, validation errors, etc.). Consider using the error mapping utilities mentioned in the README (mapToInternalCode) to derive a more accurate error code from the server response.packages/hooks/src/use-http-client/use-http-error-handler.tsx (1)
110-110: Consider adding a proper type for the Axios config.Using
anyfor the config type bypasses type checking.♻️ Suggested improvement
- const config = get(error, 'config') as any; + const config = get(error, 'config') as import('axios').InternalAxiosRequestConfig | undefined;packages/database/src/plugins/audit-log.ts (1)
151-160: Fire-and-forget logging may cause unhandled promise rejections.The
logAuditEventcall in subscriptions is not awaited. While errors are caught inside the function, if the promise is rejected before entering the try block (unlikely but possible), it would be unhandled.♻️ Consider adding .catch() for safety
const insertSub = collection.insert$.subscribe((changeEvent: RxChangeEvent<any>) => { const documentData = changeEvent.documentData; const documentId = changeEvent.documentId; - logAuditEvent(collection, 'insert', { + logAuditEvent(collection, 'insert', { documentId, after: documentData, - }); + }).catch(() => { + // Error already handled inside logAuditEvent + }); });packages/database/src/plugins/audit-log.test.ts (1)
49-50: Consider using a more robust async wait pattern.The simple
setTimeout-basedwaitForcan be flaky in CI environments. Consider using RxDB's built-in utilities or polling until the expected condition is met.♻️ Alternative approach using polling
// Helper to wait for a condition with timeout const waitForCondition = async ( condition: () => Promise<boolean>, timeout = 1000, interval = 50 ): Promise<void> => { const start = Date.now(); while (Date.now() - start < timeout) { if (await condition()) return; await new Promise((resolve) => setTimeout(resolve, interval)); } throw new Error('Condition not met within timeout'); }; // Usage example: await waitForCondition(async () => { const logs = await logsCollection.find().exec(); return logs.some((log) => log.code === 'AUDIT_INSERT'); });
| import { getLogger } from '@wcpos/utils/logger'; | ||
| import { openExternalURL } from '@wcpos/utils/open-external-url'; | ||
|
|
||
| const novuLogger = getLogger(['wcpos', 'notifications', 'novu']); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/core/src/hooks/use-novu-notifications.tsRepository: wcpos/monorepo
Length of output: 18242
Missing log import causes compile error on line 161.
The code uses log[level](...) in the processNotificationBehavior callback, but log is not imported. Only getLogger is imported on line 6, and novuLogger is used elsewhere. Import the default logger to fix:
-import { getLogger } from '@wcpos/utils/logger';
+import log, { getLogger } from '@wcpos/utils/logger';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { getLogger } from '@wcpos/utils/logger'; | |
| import { openExternalURL } from '@wcpos/utils/open-external-url'; | |
| const novuLogger = getLogger(['wcpos', 'notifications', 'novu']); | |
| import log, { getLogger } from '@wcpos/utils/logger'; | |
| import { openExternalURL } from '@wcpos/utils/open-external-url'; | |
| const novuLogger = getLogger(['wcpos', 'notifications', 'novu']); |
🤖 Prompt for AI Agents
In `@packages/core/src/hooks/use-novu-notifications.ts` around lines 6 - 9, The
code references log[level](...) inside the processNotificationBehavior callback
but never imports the default logger, causing a compile error; import the
default logger (the same logger used elsewhere) and replace or alias usages so
processNotificationBehavior uses the imported logger variable (or map novuLogger
to log) — locate processNotificationBehavior and the log[level] call, add the
missing import for the default logger, and ensure calls use that imported symbol
consistently alongside getLogger/novuLogger.
|
|
||
| const appLogger = getLogger(['wcpos', 'app', 'validation']); | ||
| import { mergeStoresWithResponse } from '../utils/merge-stores'; |
There was a problem hiding this comment.
Import order is invalid (module syntax error).
The const appLogger declaration at line 13 appears before the import statement at line 14. All imports must precede executable code.
🔧 Proposed fix
import { useAppState } from '../contexts/app-state';
-
-const appLogger = getLogger(['wcpos', 'app', 'validation']);
import { mergeStoresWithResponse } from '../utils/merge-stores';
+
+const appLogger = getLogger(['wcpos', 'app', 'validation']);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const appLogger = getLogger(['wcpos', 'app', 'validation']); | |
| import { mergeStoresWithResponse } from '../utils/merge-stores'; | |
| import { useAppState } from '../contexts/app-state'; | |
| import { mergeStoresWithResponse } from '../utils/merge-stores'; | |
| const appLogger = getLogger(['wcpos', 'app', 'validation']); |
🤖 Prompt for AI Agents
In `@packages/core/src/hooks/use-user-validation.ts` around lines 12 - 14, Move
the executable declaration "const appLogger = getLogger(['wcpos', 'app',
'validation']);" to after all import statements so imports come first;
specifically ensure the import of "mergeStoresWithResponse" and any other
imports appear before the "appLogger" declaration (update use-user-validation.ts
so the symbol appLogger is declared only after all import lines, keeping
mergeStoresWithResponse and other imports at the top).
| import { getLogger } from '@wcpos/utils/logger'; | ||
|
|
||
| import { buildAuthUrl, generateState, getRedirectUri, parseAuthResult } from './utils'; | ||
|
|
||
| const oauthLogger = getLogger(['wcpos', 'auth', 'oauth']); | ||
|
|
||
| import type { UseWcposAuthReturn, WcposAuthConfig, WcposAuthResult } from './types'; |
There was a problem hiding this comment.
Fix import ordering — logger constant splits import block.
Imports must come before any statements; const oauthLogger between imports will fail type-check/compile.
🔧 Proposed fix
import AppInfo from '@wcpos/utils/app-info';
import { getLogger } from '@wcpos/utils/logger';
import { buildAuthUrl, generateState, getRedirectUri, parseAuthResult } from './utils';
-
-const oauthLogger = getLogger(['wcpos', 'auth', 'oauth']);
-
import type { UseWcposAuthReturn, WcposAuthConfig, WcposAuthResult } from './types';
+
+const oauthLogger = getLogger(['wcpos', 'auth', 'oauth']);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { getLogger } from '@wcpos/utils/logger'; | |
| import { buildAuthUrl, generateState, getRedirectUri, parseAuthResult } from './utils'; | |
| const oauthLogger = getLogger(['wcpos', 'auth', 'oauth']); | |
| import type { UseWcposAuthReturn, WcposAuthConfig, WcposAuthResult } from './types'; | |
| import { getLogger } from '@wcpos/utils/logger'; | |
| import { buildAuthUrl, generateState, getRedirectUri, parseAuthResult } from './utils'; | |
| import type { UseWcposAuthReturn, WcposAuthConfig, WcposAuthResult } from './types'; | |
| const oauthLogger = getLogger(['wcpos', 'auth', 'oauth']); |
🤖 Prompt for AI Agents
In `@packages/core/src/hooks/use-wcpos-auth/index.web.ts` around lines 20 - 26,
The constant oauthLogger is declared between import statements which breaks the
import block; move the declaration "const oauthLogger = getLogger(['wcpos',
'auth', 'oauth']);" below the import group so all imports (e.g., getLogger and
the named imports buildAuthUrl, generateState, getRedirectUri, parseAuthResult,
and the types UseWcposAuthReturn, WcposAuthConfig, WcposAuthResult) appear
first, then declare oauthLogger, ensuring no statements interrupt the import
block.
| import { getLogger } from '@wcpos/utils/logger'; | ||
|
|
||
| const uiLogger = getLogger(['wcpos', 'ui', 'menu']); | ||
|
|
||
| import { useAppState } from '../../../../contexts/app-state'; |
There was a problem hiding this comment.
Import order is invalid (module syntax error).
The const uiLogger declaration at line 31 appears before the import statement at line 33. Move the const below all imports.
🔧 Proposed fix
import { clearAllDB } from '@wcpos/database';
import { getLogger } from '@wcpos/utils/logger';
-
-const uiLogger = getLogger(['wcpos', 'ui', 'menu']);
import { useAppState } from '../../../../contexts/app-state';
import { useTheme } from '../../../../contexts/theme';
import { useT } from '../../../../contexts/translations';
import { useImageAttachment } from '../../hooks/use-image-attachment';
+
+const uiLogger = getLogger(['wcpos', 'ui', 'menu']);🤖 Prompt for AI Agents
In `@packages/core/src/screens/main/components/header/user-menu.tsx` around lines
29 - 33, The const declaration uiLogger = getLogger([...]) is placed before
subsequent imports causing invalid module syntax; move the uiLogger
initialization below all import statements (after importing getLogger and
useAppState) so that all import declarations come first and then call getLogger
to assign uiLogger in the file (look for getLogger and uiLogger in
header/user-menu.tsx and ensure useAppState import remains above the const).
|
|
||
| const uiLogger = getLogger(['wcpos', 'ui', 'settings']); | ||
| import { |
There was a problem hiding this comment.
Syntax error: const declaration placed between import statements.
ES modules require all import statements to appear before any other statements. The const uiLogger initialization on line 11 is placed between imports (line 9 and line 12), which is invalid JavaScript/TypeScript and will cause a module parsing error.
Move the logger initialization after all imports (after line 19).
🐛 Proposed fix
Move line 11 after line 19:
import { getLogger } from '@wcpos/utils/logger';
import { useUILabel } from './use-ui-label';
-
-const uiLogger = getLogger(['wcpos', 'ui', 'settings']);
import {
mergeWithInitalValues,
patchState,
resetToInitialValues,
UISettingID,
UISettingSchema,
UISettingState,
} from './utils';
import { useAppState } from '../../../../contexts/app-state';
+const uiLogger = getLogger(['wcpos', 'ui', 'settings']);
+
interface UISettingsProviderProps {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const uiLogger = getLogger(['wcpos', 'ui', 'settings']); | |
| import { | |
| import { getLogger } from '@wcpos/utils/logger'; | |
| import { useUILabel } from './use-ui-label'; | |
| import { | |
| mergeWithInitalValues, | |
| patchState, | |
| resetToInitialValues, | |
| UISettingID, | |
| UISettingSchema, | |
| UISettingState, | |
| } from './utils'; | |
| import { useAppState } from '../../../../contexts/app-state'; | |
| const uiLogger = getLogger(['wcpos', 'ui', 'settings']); | |
| interface UISettingsProviderProps { |
🤖 Prompt for AI Agents
In `@packages/core/src/screens/main/contexts/ui-settings/provider.tsx` around
lines 10 - 12, The const uiLogger declaration is placed between import
statements which breaks ES module ordering; move the uiLogger initialization
(const uiLogger = getLogger(['wcpos','ui','settings'])) so it occurs after all
import statements have completed (i.e., below the import block and after the
last import), ensuring getLogger is still referenced correctly and no other
top-level code appears before imports.
| import { getLogger } from '@wcpos/utils/logger'; | ||
| import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; | ||
|
|
||
| import { useT } from '../../../../../contexts/translations'; | ||
|
|
||
| const mutationLogger = getLogger(['wcpos', 'mutations', 'product']); | ||
| import { CurrencyInput } from '../../../components/currency-input'; |
There was a problem hiding this comment.
Fix import ordering — logger constant breaks the import block.
TypeScript/ESM requires all imports before statements; const mutationLogger sits between imports.
🔧 Proposed fix
import { VStack } from '@wcpos/components/vstack';
import { getLogger } from '@wcpos/utils/logger';
import { ERROR_CODES } from '@wcpos/utils/logger/error-codes';
import { useT } from '../../../../../contexts/translations';
-
-const mutationLogger = getLogger(['wcpos', 'mutations', 'product']);
import { CurrencyInput } from '../../../components/currency-input';
import { FormErrors } from '../../../components/form-errors';
import { MetaDataForm, metaDataSchema } from '../../../components/meta-data-form';
import { NumberInput } from '../../../components/number-input';
import { ProductStatusSelect } from '../../../components/product/status-select';
import { TaxClassSelect } from '../../../components/tax-class-select';
import { TaxStatusRadioGroup } from '../../../components/tax-status-radio-group';
import usePushDocument from '../../../contexts/use-push-document';
import { useLocalMutation } from '../../../hooks/mutations/use-local-mutation';
+
+const mutationLogger = getLogger(['wcpos', 'mutations', 'product']);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { getLogger } from '@wcpos/utils/logger'; | |
| import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; | |
| import { useT } from '../../../../../contexts/translations'; | |
| const mutationLogger = getLogger(['wcpos', 'mutations', 'product']); | |
| import { CurrencyInput } from '../../../components/currency-input'; | |
| import { VStack } from '@wcpos/components/vstack'; | |
| import { getLogger } from '@wcpos/utils/logger'; | |
| import { ERROR_CODES } from '@wcpos/utils/logger/error-codes'; | |
| import { useT } from '../../../../../contexts/translations'; | |
| import { CurrencyInput } from '../../../components/currency-input'; | |
| import { FormErrors } from '../../../components/form-errors'; | |
| import { MetaDataForm, metaDataSchema } from '../../../components/meta-data-form'; | |
| import { NumberInput } from '../../../components/number-input'; | |
| import { ProductStatusSelect } from '../../../components/product/status-select'; | |
| import { TaxClassSelect } from '../../../components/tax-class-select'; | |
| import { TaxStatusRadioGroup } from '../../../components/tax-status-radio-group'; | |
| import usePushDocument from '../../../contexts/use-push-document'; | |
| import { useLocalMutation } from '../../../hooks/mutations/use-local-mutation'; | |
| const mutationLogger = getLogger(['wcpos', 'mutations', 'product']); |
🤖 Prompt for AI Agents
In `@packages/core/src/screens/main/products/edit/product/form.tsx` around lines
20 - 26, The file currently declares const mutationLogger = getLogger([...]) in
the middle of import statements which breaks ESM/TypeScript rules; move the
declaration of mutationLogger (and any other runtime statements) below all
import lines so that imports like getLogger, ERROR_CODES, useT, and
CurrencyInput remain in a contiguous import block, then add the const
mutationLogger = getLogger(['wcpos','mutations','product']) immediately after
the final import.
| import { useT } from '../../../contexts/translations'; | ||
|
|
||
| const barcodeLogger = getLogger(['wcpos', 'barcode', 'product']); | ||
| import { useBarcodeDetection, useBarcodeSearch } from '../hooks/barcodes'; |
There was a problem hiding this comment.
Syntax error: import statement after non-import statement.
Line 8 declares const barcodeLogger between import statements. ES modules require all import declarations to appear before any other statements. This will cause a compile-time error.
🐛 Proposed fix
import { useT } from '../../../contexts/translations';
-
-const barcodeLogger = getLogger(['wcpos', 'barcode', 'product']);
import { useBarcodeDetection, useBarcodeSearch } from '../hooks/barcodes';
+
+const barcodeLogger = getLogger(['wcpos', 'barcode', 'product']);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { useT } from '../../../contexts/translations'; | |
| const barcodeLogger = getLogger(['wcpos', 'barcode', 'product']); | |
| import { useBarcodeDetection, useBarcodeSearch } from '../hooks/barcodes'; | |
| import { useT } from '../../../contexts/translations'; | |
| import { useBarcodeDetection, useBarcodeSearch } from '../hooks/barcodes'; | |
| const barcodeLogger = getLogger(['wcpos', 'barcode', 'product']); |
🤖 Prompt for AI Agents
In `@packages/core/src/screens/main/products/use-barcode.ts` around lines 6 - 9,
Move the const barcodeLogger declaration so all import statements come first:
keep the import of useT and import { useBarcodeDetection, useBarcodeSearch } at
the top, then declare const barcodeLogger = getLogger([...]); (and ensure
getLogger is imported if it isn't already). Specifically, relocate the
barcodeLogger line out of the import block and place it after the import
statements in use-barcode.ts so ES module imports remain before any other
statements.
| import { useT } from '../../../contexts/translations'; | ||
|
|
||
| const httpLogger = getLogger(['wcpos', 'http', 'rest']); | ||
| import { FormErrors } from '../components/form-errors'; |
There was a problem hiding this comment.
Syntax error: import statement after const declaration.
The const httpLogger declaration on line 16 is placed before the import { FormErrors } statement on line 17. In ECMAScript modules, all import declarations must appear before any other statements. This will cause a parse error.
Proposed fix: move httpLogger declaration after all imports
import { useT } from '../../../contexts/translations';
-
-const httpLogger = getLogger(['wcpos', 'http', 'rest']);
import { FormErrors } from '../components/form-errors';
import { useRestHttpClient } from '../hooks/use-rest-http-client';
+
+const httpLogger = getLogger(['wcpos', 'http', 'rest']);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { useT } from '../../../contexts/translations'; | |
| const httpLogger = getLogger(['wcpos', 'http', 'rest']); | |
| import { FormErrors } from '../components/form-errors'; | |
| import { useT } from '../../../contexts/translations'; | |
| import { FormErrors } from '../components/form-errors'; | |
| import { useRestHttpClient } from '../hooks/use-rest-http-client'; | |
| const httpLogger = getLogger(['wcpos', 'http', 'rest']); |
🤖 Prompt for AI Agents
In `@packages/core/src/screens/main/receipt/email.tsx` around lines 14 - 17, The
const declaration for httpLogger (const httpLogger = getLogger(['wcpos', 'http',
'rest'])) is placed before an import (import { FormErrors }) which causes a
syntax error; move the httpLogger declaration below all import statements (after
imports like import { useT } and import { FormErrors }) so all import
declarations appear first, and ensure getLogger is available in scope where you
place httpLogger.
| // Log metadata at debug level to avoid exposing sensitive info like licenseKey | ||
| log.debug('Novu: Subscriber metadata', { context: { metadata } }); | ||
| novuLogger.debug('Novu: Subscriber metadata', { context: { metadata } }); | ||
|
|
There was a problem hiding this comment.
Avoid logging full subscriber metadata / notification data (PII risk).
metadata can include licenseKey, and notification.data can include user/customer fields. Logging raw objects risks leaking sensitive info to persisted logs. Please redact or log only safe keys.
🔒 Example redaction
- novuLogger.debug('Novu: Subscriber metadata', { context: { metadata } });
+ const redactedMetadata = metadata
+ ? { ...metadata, licenseKey: metadata.licenseKey ? '[redacted]' : undefined }
+ : undefined;
+ novuLogger.debug('Novu: Subscriber metadata', { context: { metadata: redactedMetadata } });
- novuLogger.debug('Novu: WebSocket notification received', {
- context: {
- id: notificationId,
- dataValue: JSON.stringify(notification?.data),
- },
- });
+ novuLogger.debug('Novu: WebSocket notification received', {
+ context: {
+ id: notificationId,
+ dataKeys: notification?.data ? Object.keys(notification.data as object) : [],
+ },
+ });Also applies to: 269-274
🤖 Prompt for AI Agents
In `@packages/core/src/services/novu/client.ts` around lines 152 - 154, The debug
logging currently emits raw subscriber/notification objects via
novuLogger.debug('Novu: Subscriber metadata', { context: { metadata } }) and a
similar log around notification.data (lines noted in review); replace these raw
logs with a sanitized payload by implementing and using a redaction helper
(e.g., redactSensitive(obj) or pickSafeKeys(obj)) that either whitelists safe
keys or masks known sensitive keys (licenseKey, email, name, payment/customer
identifiers, notification.data fields) before passing to novuLogger.debug;
update both the metadata log and the notification.data log calls to log only the
redacted or whitelisted object and include brief context text indicating
redaction.
| // Store subscriptions so they can be cleaned up | ||
| const collectionSubscriptions = new Map<string, Subscription[]>(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/database/src/plugins/audit-log.tsRepository: wcpos/monorepo
Length of output: 8853
🏁 Script executed:
# Check if preDestroyRxDatabase hook is used elsewhere
rg "preDestroyRxDatabase|destroyRxDatabase" --type tsRepository: wcpos/monorepo
Length of output: 40
🏁 Script executed:
# Look for RxDB hook patterns
rg "hooks:\s*\{" packages/database/src/plugins/ -A 10 --type tsRepository: wcpos/monorepo
Length of output: 5734
🏁 Script executed:
# Check RxDB version
cat packages/database/package.json | grep -A 2 -B 2 "rxdb"Repository: wcpos/monorepo
Length of output: 213
🏁 Script executed:
# Look for RxDB hook definitions in the codebase
rg "type.*Hook|interface.*Hook" packages/database/src/ --type ts | head -20Repository: wcpos/monorepo
Length of output: 40
🏁 Script executed:
# Check what hooks are available in RxDB types
find node_modules/rxdb -name "*.d.ts" -type f 2>/dev/null | head -5 | xargs grep -l "Hook\|hook" 2>/dev/null | head -3Repository: wcpos/monorepo
Length of output: 40
🏁 Script executed:
# Search for RxDB plugin hook documentation or types
rg "destroyRxDatabase|removeRxDatabase|closeRxDatabase" packages/ --type tsRepository: wcpos/monorepo
Length of output: 151
🏁 Script executed:
# Check RxDB plugin hooks available in types
rg "postRemoveRxDatabase|preDestroyRxDatabase|postDestroyRxDatabase" --type tsRepository: wcpos/monorepo
Length of output: 126
🏁 Script executed:
# Look at RxDB hooks in the existing plugin implementations
cat -n packages/database/src/plugins/delete-db.tsRepository: wcpos/monorepo
Length of output: 2607
🏁 Script executed:
# Search for all hook names used in plugins to understand the pattern
rg "^\s+(create|post|pre)[A-Z]" packages/database/src/plugins/ --type ts | grep -o "[a-zA-Z]*RxDatabase\|[a-zA-Z]*RxCollection" | sort | uniqRepository: wcpos/monorepo
Length of output: 136
🏁 Script executed:
# Check if preDestroyRxDatabase is a valid hook in RxDB 16 by looking at imports and types
rg "from 'rxdb" packages/database/src/plugins/audit-log.tsRepository: wcpos/monorepo
Length of output: 119
🌐 Web query:
RxDB 16 hooks preDestroyRxDatabase plugin
💡 Result:
RxDB v16 has a plugin hook named preDestroyRxDatabase (runs at the beginning of the database destroy process). It’s part of RxDB’s internal plugin hook registry (along with e.g. preCreateRxDatabase, postRemoveRxDatabase, etc.). [1]
To run code before db.destroy(), register a plugin with a hooks entry:
import { addRxPlugin } from 'rxdb';
export const MyPlugin = {
name: 'my-plugin',
rxdb: true,
hooks: {
preDestroyRxDatabase: {
before: async ({ database }) => {
// your cleanup logic here
// (close connections, stop replications, flush telemetry, etc.)
}
}
}
};
addRxPlugin(MyPlugin);This hooks: { ...: { before()/after() } } structure is how RxDB plugins wire into hook points. [2]
Related hook: postRemoveRxDatabase exists too (runs after a database was removed). [1]
Sources:
[1] RxDB hook list includes preDestroyRxDatabase and postRemoveRxDatabase (hooks registry). (app.unpkg.com)
[2] Example RxDB plugin structure showing hooks with { before, after }. (app.unpkg.com)
Citations:
- 1: https://app.unpkg.com/rxdb%4015.37.0/files/dist/cjs/hooks.js.map?utm_source=openai
- 2: https://app.unpkg.com/rxdb%4016.8.1/files/dist/cjs/plugins/dev-mode/index.js.map?utm_source=openai
Add preDestroyRxDatabase hook to clean up subscriptions on database destruction.
The collectionSubscriptions Map stores subscriptions at the module level without cleanup when the database is destroyed. This causes memory leaks in long-running applications or when databases are created/destroyed multiple times (e.g., in tests).
♻️ Suggested fix - add preDestroyRxDatabase hook
export const RxDBAuditLogPlugin: RxPlugin = {
name: 'audit-log',
rxdb: true,
prototypes: {},
overwritable: {},
hooks: {
createRxCollection: {
after({ collection }) {
// ... existing code
},
},
+ preDestroyRxDatabase: {
+ before: async ({ database }) => {
+ // Clean up all subscriptions for this database
+ for (const [key, subs] of collectionSubscriptions.entries()) {
+ if (key.startsWith(`${database.name}-`)) {
+ subs.forEach((sub) => sub.unsubscribe());
+ collectionSubscriptions.delete(key);
+ }
+ }
+ },
+ },
},
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Store subscriptions so they can be cleaned up | |
| const collectionSubscriptions = new Map<string, Subscription[]>(); | |
| export const RxDBAuditLogPlugin: RxPlugin = { | |
| name: 'audit-log', | |
| rxdb: true, | |
| prototypes: {}, | |
| overwritable: {}, | |
| hooks: { | |
| createRxCollection: { | |
| after({ collection }) { | |
| // ... existing code | |
| }, | |
| }, | |
| preDestroyRxDatabase: { | |
| before: async ({ database }) => { | |
| // Clean up all subscriptions for this database | |
| for (const [key, subs] of collectionSubscriptions.entries()) { | |
| if (key.startsWith(`${database.name}-`)) { | |
| subs.forEach((sub) => sub.unsubscribe()); | |
| collectionSubscriptions.delete(key); | |
| } | |
| } | |
| }, | |
| }, | |
| }, | |
| }; |
🤖 Prompt for AI Agents
In `@packages/database/src/plugins/audit-log.ts` around lines 39 - 40, The
module-level collectionSubscriptions Map is never cleaned up, causing leaks; add
a preDestroyRxDatabase hook implementation (RxDB plugin hook) that runs when a
database is destroyed, locates the subscriptions for that database (use the same
key you used when adding entries to collectionSubscriptions—e.g., a db name or
identifier), iterates each Subscription in the array and calls unsubscribe(),
then removes the Map entry; update the plugin (audit-log.ts) to register this
preDestroyRxDatabase hook so collectionSubscriptions is cleaned and memory is
freed when RxDatabase instances are torn down.
Summary
Implements a comprehensive logging system with automatic audit logging and enhanced manual logging capabilities.
Phase 1: RxDB Audit Plugin
Phase 2: Enhanced Logger API
getLogger(['wcpos', 'pos', 'cart'])- Hierarchical category-based loggers.getChild('checkout')- Create child loggers with inherited categories.with({ orderId: '123' })- Bind context that persists across all log callsPhase 3: User-Facing Logs
Files Changed
packages/database/src/plugins/audit-log.tspackages/database/src/plugins/index.tspackages/utils/src/logger/index.tspackages/utils/src/logger/README.mdpackages/core/.../pay.tsxpackages/core/.../payment-webview.tsxpackages/core/.../use-add-customer.tspackages/core/.../use-add-fee.tspackages/core/.../use-add-shipping.tsTest plan
Summary by CodeRabbit
New Features
Improvements
Documentation
Tests
✏️ Tip: You can customize this high-level summary in your review settings.