Skip to content

Component improvements#1718

Open
gugu wants to merge 3 commits intomainfrom
component-improvements
Open

Component improvements#1718
gugu wants to merge 3 commits intomainfrom
component-improvements

Conversation

@gugu
Copy link
Copy Markdown
Contributor

@gugu gugu commented Apr 17, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added dedicated UI components for binary field handling, enabling users to view, edit, and filter binary data using hexadecimal format
    • Expanded filter options with "between" and "in" comparators for numeric and date-time fields
  • Refactor

    • Optimized binary data processing pipeline and conversion utilities
    • Reorganized filter component architecture to support additional comparison modes

gugu and others added 3 commits April 16, 2026 20:28
- Rewrite edit/view/table-display/filter binary widgets as hex-only UI with
  clean signal-based state. Edit emits {type:'Buffer', data:number[]} and
  converts incoming hex strings to the byte-array shape on init so the
  save payload is always binary even if the user does not touch the field.
- Parent db-table-row-edit.updateField now replaces a primitive field value
  when the incoming update is an object, so Buffer-JSON emissions actually
  land in tableRowValues.
- Stop converting Buffer to hex strings on reads: delete
  convertBinaryDataInRowUtil and drop the binary->hex loop from
  processRowsUtil. Buffer values flow from the DAO into the HTTP response
  and Nest serializes them as {type:'Buffer', data:[...]} natively.
- Accept Buffer-JSON (and Buffer/Uint8Array/hex) on writes via shared
  toBinaryBuffer helper used by convertHexDataInRowUtil and
  convertHexDataInPrimaryKeyUtil. Wire binary conversion into
  bulk-update-rows-in-table so bulk updates go through the same path as
  single-row updates.
- Update Postgres binary e2e tests to derive hex for the search query
  from the returned Buffer-JSON and compare rows with deepEqual.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Introduce frontend/src/app/lib/binary.ts with parseBinaryValue,
stringToBytes, hexStringToBytes, bytesToHex, and toBufferJson. The
edit, view, and table-display widgets now parse any incoming value
(string, Buffer-JSON, Uint8Array) to a byte array first and derive
the hex display from bytes. Strings received from the server are
treated as latin1 binary strings (one char = one byte) rather than
hex, so pg's default bytea-as-string output decodes correctly. Hex
input typed into the edit or filter widgets still goes through
hexStringToBytes for user-facing validation.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Copilot AI review requested due to automatic review settings April 17, 2026 12:31
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

This PR introduces comprehensive binary data handling improvements across the application. The backend refactors row-processing pipelines to modify when and how binary-to-hex and hex-to-binary conversions occur, removes binary conversion from certain post-fetch operations, and updates related utility functions. The frontend adds new dedicated binary UI components (filter, edit, view, display) to replace generic file components and provides binary-specific formatting and validation logic.

Changes

Cohort / File(s) Summary
Backend Binary Utility Functions
backend/src/helpers/binary-to-hex.ts, backend/src/entities/table/utils/convert-binary-data-in-row.util.ts, backend/src/entities/table/utils/convert-hex-data-in-primary-key.util.ts, backend/src/entities/table/utils/convert-hex-data-in-row.util.ts, backend/src/entities/table/utils/process-found-rows-util.ts
Modified binary/hex conversion utilities: removed convertBinaryDataInRowUtil, updated convertHexDataIn* functions to use toBinaryBuffer instead of hexToBinary, added new toBinaryBuffer for flexible input handling, and removed structure parameter from processRowsUtil.
Backend Table Use Cases
backend/src/entities/table/use-cases/add-row-in-table.use.case.ts, backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts, backend/src/entities/table/use-cases/bulk-update-rows-in-table.use.case.ts, backend/src/entities/table/use-cases/update-row-in-table.use.case.ts, backend/src/entities/table/use-cases/get-table-rows.use.case.ts
Updated use-case implementations to refactor imports, restructure call signatures to multi-line formatting, remove convertBinaryDataInRowUtil from post-fetch pipelines in add/get operations, and add convertHexDataInRowUtil before database persist operations.
Backend Integration Tests
backend/test/ava-tests/non-saas-tests/non-saas-postgres-with-binary-e2e.test.ts, backend/test/ava-tests/saas-tests/postgres-with-binary-e2e.test.ts
Updated test fixtures to use binaryToHex for search query parameters and replaced strict equality with deep equality assertions for binary column comparisons.
Frontend Binary Components - Filter/Display
frontend/src/app/components/ui-components/filter-fields/binary/*, frontend/src/app/components/ui-components/table-display-fields/binary/*
Added new BinaryFilterComponent and BinaryDisplayComponent with hex validation, multiple filter modes (eq, contains, startswith, empty for filter; truncation display for table), and copy-to-clipboard functionality.
Frontend Binary Components - Edit/View
frontend/src/app/components/ui-components/record-edit-fields/binary/*, frontend/src/app/components/ui-components/record-view-fields/binary/*
Added new BinaryEditComponent and BinaryRecordViewComponent for editing hex values with validation and viewing binary data with optional truncation; includes form field styling and error handling.
Frontend Extended Filter Support
frontend/src/app/components/ui-components/filter-fields/date-time/*, frontend/src/app/components/ui-components/filter-fields/default-filter/*
Extended filter components to support between and in comparator modes: added UI templates for range inputs and comma-separated value lists, updated component logic and tests to handle new filter modes.
Frontend Configuration and Constants
frontend/src/app/consts/filter-types.ts, frontend/src/app/consts/record-edit-types.ts, frontend/src/app/consts/record-view-types.ts, frontend/src/app/consts/table-display-types.ts
Registered new BinaryFilterComponent, BinaryEditComponent, BinaryRecordViewComponent, and BinaryDisplayComponent in UI widget mappings and updated all binary/bytea type definitions across database systems to use binary-specific components instead of generic file components.
Frontend Binary Helper Library
frontend/src/app/lib/binary.ts, frontend/src/app/lib/binary.spec.ts
Added utility functions for binary data conversion: parseBinaryValue, stringToBytes, hexStringToBytes, bytesToHex, toBufferJson, and type BinaryBufferJson with comprehensive test coverage.
Frontend Row Edit Safety Improvement
frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts
Enhanced updateField method to safely check if field values are objects before applying property deletion and merging operations.
Shared Enums
shared-code/src/shared/enums/table-widget-type.enum.ts
Added Binary = 'Binary' member to TableWidgetTypeEnum.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • lyubov-voloshko
  • Artuomka

Poem

🐰 Hex and bytes now dance with grace,
Binary data finds its place,
From filter to view, a unified way,
No more files where bytes hold sway,
Conversion flows through buffers bright,
The rabbit hops in pure delight!

🚥 Pre-merge checks | ✅ 1 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Security Check ⚠️ Warning PR introduces critical security vulnerabilities in binary data handling: unvalidated hex conversion, unsafe object handling, and reversal of data flow from display to storage. Add server-side validation with regex, throw errors for invalid formats, validate object shapes strictly, implement end-to-end tests with malformed inputs, and add rate-limiting.
Title check ❓ Inconclusive The title 'Component improvements' is vague and generic, failing to convey the specific nature of the changes. The PR introduces substantial changes including binary data handling improvements, new binary UI components, filter enhancements, and backend refactoring, none of which are clearly indicated by the title. Consider a more specific title that reflects the primary changes, such as 'Add binary data handling components and refactor table row processing' or 'Implement dedicated binary field components across edit/view/filter/display layers'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch component-improvements

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces first-class “Binary” handling across the UI (display/view/edit/filter) and adjusts backend handling so binary values can round-trip as Buffer-like payloads instead of being coerced to hex strings early.

Changes:

  • Add a new Binary table widget type and map common binary DB column types to new Binary UI components.
  • Add shared binary utilities (parse/hex encode/decode) plus comprehensive unit tests for helpers and components.
  • Extend filtering UX to support in / between for default (number/datetime) filters, and update backend row update paths to accept Buffer-like inputs.

Reviewed changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
shared-code/src/shared/enums/table-widget-type.enum.ts Adds Binary widget type enum value.
frontend/src/app/lib/binary.ts New binary parsing/hex encode-decode helpers and Buffer-JSON adapter.
frontend/src/app/lib/binary.spec.ts Unit tests for binary helper functions.
frontend/src/app/consts/table-display-types.ts Map binary column types to BinaryDisplayComponent instead of FileDisplayComponent.
frontend/src/app/consts/record-view-types.ts Map binary column types to BinaryRecordViewComponent instead of FileRecordViewComponent.
frontend/src/app/consts/record-edit-types.ts Map binary column types to BinaryEditComponent instead of FileEditComponent.
frontend/src/app/consts/filter-types.ts Map binary column types to BinaryFilterComponent.
frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.ts New table cell binary display (hex, truncation, copy-to-clipboard).
frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.spec.ts Tests for binary table display behavior.
frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.html Template for binary table display + copy button.
frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.css Styling for monospace hex display.
frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.ts New record view binary display (longer truncation + tooltip).
frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.spec.ts Tests for record view binary behavior (tooltip/truncation).
frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.html Template for record view binary display + copy button.
frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.css Styling/layout for record view hex rendering.
frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.ts New binary editor using hex textarea with validation and Buffer-JSON output.
frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.spec.ts Tests for binary edit parsing + emitted values.
frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.html Template for binary edit textarea + validation error.
frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.css Styling for binary edit form field.
frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.ts Add in/between comparator handling and associated state.
frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.spec.ts Tests for in/between behavior and comparator options.
frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.html Render IN text input or BETWEEN dual inputs depending on comparator.
frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.css Layout rules for BETWEEN dual inputs.
frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts Adds in/between modes to the dedicated datetime filter component.
frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.spec.ts Tests for datetime in/between emissions and restore logic.
frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.html UI additions for datetime in/between mode inputs.
frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.css Layout rules for datetime BETWEEN inputs.
frontend/src/app/components/ui-components/filter-fields/binary/binary.component.ts New binary filter component (hex + comparator modes).
frontend/src/app/components/ui-components/filter-fields/binary/binary.component.spec.ts Tests for binary filter modes and emissions.
frontend/src/app/components/ui-components/filter-fields/binary/binary.component.html Template for binary filter comparator + hex input.
frontend/src/app/components/ui-components/filter-fields/binary/binary.component.css Layout for binary filter row.
frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts Safer object-merge update logic for edited row values.
backend/test/ava-tests/saas-tests/postgres-with-binary-e2e.test.ts Update search to use hex derived from returned binary payload; deepEqual binary value.
backend/test/ava-tests/non-saas-tests/non-saas-postgres-with-binary-e2e.test.ts Same as above for non-saas suite.
backend/src/helpers/binary-to-hex.ts Add toBinaryBuffer to normalize inputs to Buffer.
backend/src/entities/table/utils/process-found-rows-util.ts Stop converting binary columns to hex during row post-processing.
backend/src/entities/table/utils/convert-hex-data-in-row.util.ts Use toBinaryBuffer and handle null/empty values.
backend/src/entities/table/utils/convert-hex-data-in-primary-key.util.ts Use toBinaryBuffer and handle null/empty values.
backend/src/entities/table/utils/convert-binary-data-in-row.util.ts Removed (no longer converting outgoing binary to hex).
backend/src/entities/table/use-cases/update-row-in-table.use.case.ts Convert incoming hex/Buffer-like values to Buffer before update; stop outgoing binary-to-hex conversion.
backend/src/entities/table/use-cases/get-table-rows.use.case.ts Adjust processRowsUtil call signature and related imports.
backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts Remove outgoing binary-to-hex conversion and reorder imports.
backend/src/entities/table/use-cases/bulk-update-rows-in-table.use.case.ts Convert incoming binary values to Buffer before bulk update.
backend/src/entities/table/use-cases/add-row-in-table.use.case.ts Convert incoming binary values to Buffer before insert; stop outgoing binary-to-hex conversion.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 33 to 56
@@ -33,7 +50,9 @@ export class DateTimeFilterComponent extends BaseFilterFieldComponent implements
}

ngAfterViewInit(): void {
if (this.value) {
if (this.filterMode === 'between' || this.filterMode === 'in') {
this.onComparatorChange.emit(this.filterMode);
} else if (this.value) {
this.onComparatorChange.emit(this.filterMode);
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DateTimeFilterComponent can't restore previously-selected 'between'/'in' modes from saved filters: filterMode always starts as 'last_day', so the ngOnInit branches that restore array values never run, and ngAfterViewInit will emit 'last_day' as the comparator even when value is an array. Consider accepting an @Input() comparator (passed from the filters dialog) and setting filterMode from it before emitting, or explicitly inferring the mode from the incoming value and not emitting a preset comparator for array values.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +34
export function toBinaryBuffer(value: unknown): Buffer {
if (Buffer.isBuffer(value)) return value;
if (value instanceof Uint8Array) return Buffer.from(value);
if (typeof value === 'string') return hexToBinary(value);
if (value && typeof value === 'object') {
const v = value as { type?: string; data?: unknown };
if (v.type === 'Buffer' && Array.isArray(v.data)) return Buffer.from(v.data as number[]);
if (Array.isArray(v.data)) return Buffer.from(v.data as number[]);
return Buffer.from(Object.values(value as Record<string, number>));
}
return hexToBinary(String(value));
}
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

toBinaryBuffer converts any string via Buffer.from(value, 'hex') without validating that the string is valid hex. This can silently turn malformed user input (or unexpected strings) into an empty/partial Buffer, leading to silent data corruption instead of a validation error. Recommend validating the string first (hex-only, even length) and throwing a 400/validation error when invalid; similarly, avoid Buffer.from(Object.values(...)) unless you can guarantee numeric byte values.

Copilot uses AI. Check for mistakes.
.set('Cookie', firstUserToken)
.set('Content-Type', 'application/json')
.set('Accept', 'application/json');
t.is(getTableRowsResponse.status, 200);
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is checking getTableRowsResponse.status after the second request. It should assert getTableRowsWithSearchResponse.status so the test fails if the search request returns a non-200 status.

Suggested change
t.is(getTableRowsResponse.status, 200);
t.is(getTableRowsWithSearchResponse.status, 200);

Copilot uses AI. Check for mistakes.
.set('Accept', 'application/json');

const getTableRowsWithSearchRO = JSON.parse(getTableRowsWithSearchResponse.text);
t.is(getTableRowsResponse.status, 200);
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is checking getTableRowsResponse.status after the search request. It should assert getTableRowsWithSearchResponse.status so the test actually verifies the second request succeeded.

Suggested change
t.is(getTableRowsResponse.status, 200);
t.is(getTableRowsWithSearchResponse.status, 200);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts (1)

613-632: ⚠️ Potential issue | 🟡 Minor

Object reference mutation pattern works for known widgets but lacks test coverage for object payloads.

The in-place merge (delete + assign) assumes callers emit complete object payloads. Evidence shows this holds for the two widgets that emit objects:

  • Binary widget always emits { type: 'Buffer', data: number[] } (complete shape)
  • Money widget always emits either { amount, currency } or a scalar (complete shape)

However, there are no tests validating updateField with object payloads—all spec tests use scalars (strings, null, numbers). This is a gap: if any future widget emits a partial object, keys not in the new payload would be lost.

Two safer alternatives without behavior changes:

  1. Add explicit tests for object-based updates to prevent regressions when new widgets are added
  2. Use shallow merge without deletion: Object.assign(existing, updatedValue) alone, which is safer if callers ever send partial updates

The OnPush change detection concern is less likely in practice here since ndc-dynamic bindings emit events directly on each input change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts`
around lines 613 - 632, The in-place object replacement in updateField (in
db-table-row-edit.component, operating on this.tableRowValues[field]) currently
deletes existing props then Object.assigns the new object; add safety and tests:
change the mutation to a shallow merge using only Object.assign(existing,
updatedValue) (remove the for-delete loop) so partial payloads don't drop keys,
and add unit tests calling updateField with object payloads (e.g., the binary
shape {type:'Buffer',data:[] } and money shape {amount, currency}) to assert
existing objects are merged correctly and that this.isPrimaryKeyUpdated still
toggles when field is in keyAttributesFromURL.
🧹 Nitpick comments (11)
frontend/src/app/components/ui-components/filter-fields/binary/binary.component.css (1)

1-15: Consider extracting shared filter-row styles.

.filter-row, .comparator-field, and .value-field are duplicated verbatim across filter-fields/binary/binary.component.css, filter-fields/date-time/date-time.component.css, and filter-fields/default-filter/default-filter.component.css. Consider hoisting them into a shared stylesheet (or a base filter component style) to reduce drift as the filter UI evolves.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/binary/binary.component.css`
around lines 1 - 15, The three duplicated CSS rules (.filter-row,
.comparator-field, .value-field) should be extracted into a shared stylesheet or
base component style to avoid drift; create a new shared CSS/SCSS (or a base
component stylesheet) containing those class rules and replace the duplicated
blocks in binary.component.css, date-time.component.css, and
default-filter.component.css with an import or by extending the base styles
(ensure the shared file is imported in the components or referenced by the base
filter component), keeping selectors identical so existing markup (classes
.filter-row, .comparator-field, .value-field) continues to work.
frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.css (1)

24-31: Mixing ::ng-deep with descendant selector — double-check the intended scope.

Two concerns on these selectors:

  1. .between-fields ::ng-deep > ndc-dynamic places ::ng-deep between the parent and child, which shadow-pierces everything after it. Since ndc-dynamic is actually a direct child in the template (frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.html:21-37), you can style it without ::ng-deep at all. The ::ng-deep is only needed for the nested mat-form-field rule below.
  2. ::ng-deep is deprecated (and is what triggers the stylelint selector-pseudo-element-no-unknown errors on lines 24 and 29). If you want to keep stylelint clean without disabling the rule, either add ::ng-deep to the stylelint pseudoElements allowlist project-wide, or narrow usage and add inline /* stylelint-disable-next-line selector-pseudo-element-no-unknown */ on lines 24 and 29.
♻️ Suggested adjustment
-.between-fields ::ng-deep > ndc-dynamic {
-	flex: 1;
-	min-width: 0;
-}
-
-.between-fields ::ng-deep mat-form-field {
-	width: 100%;
-}
+.between-fields > ndc-dynamic {
+	flex: 1;
+	min-width: 0;
+}
+
+/* stylelint-disable-next-line selector-pseudo-element-no-unknown */
+.between-fields ::ng-deep mat-form-field {
+	width: 100%;
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.css`
around lines 24 - 31, The selector `.between-fields ::ng-deep > ndc-dynamic`
unnecessarily uses `::ng-deep` and should be changed to target the direct child
(`.between-fields > ndc-dynamic`) so it no longer shadow-pierces; for the nested
rule that needs shadow-piercing (`.between-fields ::ng-deep mat-form-field`)
either keep `::ng-deep` but suppress the stylelint warning with `/*
stylelint-disable-next-line selector-pseudo-element-no-unknown */` immediately
above that selector, or instead add `::ng-deep` to the stylelint
`pseudoElements` allowlist project-wide—pick one approach and update the
selectors `.between-fields > ndc-dynamic` and `.between-fields ::ng-deep
mat-form-field` accordingly.
frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.html (1)

3-4: Consider always showing hex tooltip when truncated.

The current logic is correct: empty tooltip when not truncated avoids noise. A minor nit — you could use null instead of '' to fully disable the tooltip (matTooltip with empty string still registers a no-op tooltip instance). Non-blocking.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.html`
around lines 3 - 4, The matTooltip binding should provide null when not
truncated to fully disable the tooltip; update the binding that uses
isTruncated() and hexValue() so it returns hexValue() when isTruncated() is true
and null otherwise (replace the current '' fallback), referencing the
isTruncated() and hexValue() expressions used in that matTooltip attribute to
locate where to change.
frontend/src/app/components/ui-components/filter-fields/binary/binary.component.html (1)

11-25: LGTM — hex filter UI is clean.

Conditional rendering via @if follows Angular 19 control flow guidelines, validator wiring and error message are correctly tied to the template ref.

Minor nit: consider adding autocomplete="off" and spellcheck="false" on the hex input to avoid browser interference.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/binary/binary.component.html`
around lines 11 - 25, Add autocomplete="off" and spellcheck="false" to the hex
input element to prevent browser autofill and spellchecking from interfering
with binary/hex entry; update the <input matInput ...> tag (the element using
hexValidator, name="{{label()}}-{{key()}}", `#inputElement`, `#hexInput`="ngModel",
[(ngModel)]="hexValue" and (ngModelChange)="onHexValueChange($event)") to
include these attributes while keeping existing bindings and validators intact.
frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.html (1)

3-8: Remove form wrapper concern; form context is properly inherited via parent.

The binary component is dynamically loaded within the parent form context (via ndc-dynamic inside <form #editRowForm="ngForm">), so [(ngModel)] binding and validators work correctly. No form wrapper is needed.

However, consider adding autocomplete="off" and spellcheck="false" on the hex textarea to prevent browser autofill and spellcheck interference with hex data.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.html`
around lines 3 - 8, The parent form context is inherited so no extra form
wrapper is needed; update the hex textarea element used by the BinaryComponent
template to disable browser autofill and spellcheck by adding autocomplete="off"
and spellcheck="false" to the <textarea> that binds [(ngModel)]="hexData"
(template ref `#hexContent`="ngModel", and uses label(), key(), required(),
disabled(), readonly(), and onHexChange()) so the hex input won't be interfered
with by browser autofill or spellchecking.
frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.ts (1)

18-25: Signals should be protected or private readonly, not public.

Per coding guidelines, component state signals must not be exposed as public. Since these are consumed from the template, protected readonly is the appropriate visibility.

♻️ Proposed change
-	public readonly bytes = computed(() => parseBinaryValue(this.value()));
-	public readonly hexValue = computed(() => bytesToHex(this.bytes()));
-
-	public readonly displayText = computed(() => {
+	protected readonly bytes = computed(() => parseBinaryValue(this.value()));
+	protected readonly hexValue = computed(() => bytesToHex(this.bytes()));
+
+	protected readonly displayText = computed(() => {

Note: the sibling spec (binary.component.spec.ts) asserts against these signals; if they must remain accessible from tests, consider a testable type alias as per the spec guidelines rather than widening visibility to public.

As per coding guidelines: "Signals for component state must be declared as protected or private readonly and initialized with signal() or computed()".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.ts`
around lines 18 - 25, The three component signals bytes, hexValue, and
displayText are declared as public but should be protected readonly per
component state guidelines; change their declarations from public readonly to
protected readonly for bytes, hexValue, and displayText (in binary.component.ts)
so they remain accessible to the template but not publicly exposed; if tests
require access (binary.component.spec.ts), avoid widening visibility and instead
introduce a testable type alias or testing accessor as described in the spec
guidelines rather than making these signals public.
frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.ts (1)

30-33: Minor: hexValidation()({ value: this.hexData } as never) is a typing escape hatch.

Casting the control payload to never silences TS rather than modeling the validator input. Passing a small { value: string } as unknown as AbstractControl (or calling a simple isValidHex(hex) helper) would be cleaner and keep type info honest.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.ts`
around lines 30 - 33, onHexChange currently uses a typing escape hatch by
calling hexValidation()({ value: this.hexData } as never); instead, change it to
pass a correctly-typed control payload or use a simple helper: either construct
a minimal AbstractControl-like object (e.g. { value: this.hexData } cast only to
unknown then to AbstractControl) when calling hexValidation(), or replace the
validator call with a dedicated isValidHex(this.hexData) helper and set
this.isInvalidInput based on that; update onHexChange and references to
hexValidation/validation logic accordingly so TypeScript types are preserved and
no 'as never' cast is used.
frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.ts (1)

21-27: Consider extracting the hex-display logic shared with BinaryDisplayComponent.

The identical MAX_DISPLAY_LENGTH/displayText/isTruncated block also lives in frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.ts. A small helper in frontend/src/app/lib/binary.ts (e.g. formatHexForDisplay(hex, max)) would avoid drift if the truncation rule changes later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.ts`
around lines 21 - 27, Extract the shared hex truncation logic into a helper
(e.g., formatHexForDisplay(hex: string, max?: number)) in
frontend/src/app/lib/binary.ts and replace the duplicated code in both
BinaryDisplayComponent and the table-display binary component: remove the inline
logic that uses MAX_DISPLAY_LENGTH in displayText and isTruncated, call
formatHexForDisplay(this.hexValue(), MAX_DISPLAY_LENGTH) to compute displayText
(returning the em-dash for empty values and ellipses when truncated) and compute
isTruncated by comparing the raw hex length to max (or expose an API like {
text, truncated } from the helper); ensure the helper is exported and both
components import and use it so truncation behaviour is centralized.
frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts (1)

115-127: onBetweenLowerChange and onBetweenUpperChange are byte-for-byte identical — consolidate.

Both recompose [lower, upper] and emit the same events. A single private helper (e.g. _emitBetween()) invoked from the two template handlers would eliminate the duplication.

Proposed refactor
-	onBetweenLowerChange(): void {
-		const lower = this._composeDateTime(this.lowerDate, this.lowerTime);
-		const upper = this._composeDateTime(this.upperDate, this.upperTime);
-		this.onFieldChange.emit([lower, upper]);
-		this.onComparatorChange.emit('between');
-	}
-
-	onBetweenUpperChange(): void {
-		const lower = this._composeDateTime(this.lowerDate, this.lowerTime);
-		const upper = this._composeDateTime(this.upperDate, this.upperTime);
-		this.onFieldChange.emit([lower, upper]);
-		this.onComparatorChange.emit('between');
-	}
+	onBetweenLowerChange(): void {
+		this._emitBetween();
+	}
+
+	onBetweenUpperChange(): void {
+		this._emitBetween();
+	}

and add a private helper (placed with other private methods):

private _emitBetween(): void {
  const lower = this._composeDateTime(this.lowerDate, this.lowerTime);
  const upper = this._composeDateTime(this.upperDate, this.upperTime);
  this.onFieldChange.emit([lower, upper]);
  this.onComparatorChange.emit('between');
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts`
around lines 115 - 127, Both onBetweenLowerChange and onBetweenUpperChange
contain identical logic; create a private helper named _emitBetween that calls
_composeDateTime for this.lowerDate/this.lowerTime and
this.upperDate/this.upperTime and then emits this.onFieldChange and
this.onComparatorChange('between'), replace both onBetweenLowerChange and
onBetweenUpperChange bodies with a single call to this._emitBetween(), and place
_emitBetween alongside the other private methods.
frontend/src/app/lib/binary.ts (1)

22-32: hexStringToBytes silently swallows malformed input.

Returning [] on a bad parse is convenient for rendering, but callers like BinaryEditComponent.emitCurrentValue() guard against this only via a separate validator run. If that validator ever disagrees with parseInt(..., 16) (e.g. whitespace, embedded 0x prefix), you can end up emitting toBufferJson([]) and silently wiping the field. Consider either throwing/returning null and making callers handle it, or documenting the invariant that the caller has already validated the hex.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/lib/binary.ts` around lines 22 - 32, The hexStringToBytes
function currently swallows malformed input by returning [] — change it to
return null for any invalid input (use hex = hex.trim(); strip a leading
"0x"/"0X" before normalizing) and make parse checks explicit (if parseInt yields
NaN -> return null) so callers can distinguish "empty result" from "invalid
input"; update BinaryEditComponent.emitCurrentValue() (and any other callers) to
handle a null return (skip emitting toBufferJson([]) and surface validation
errors) rather than relying on [] to signal failure.
backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts (1)

147-153: Inconsistent mutation semantics between related filtering functions — consider aligning patterns.

filterReferencedTablesByPermission returns Promise<void> and mutates the input array in place (reassigning each item's referenced_by property), making the current code at lines 147-153 safe. However, this differs from filterForeignKeysByReadPermission (used at line 106), which returns Promise<Array<ForeignKeyDS>> with a new filtered array without mutating the input. For consistency and clarity, these two closely-related permission filters should use the same mutation pattern.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts`
around lines 147 - 153, The two related permission filters are inconsistent:
filterForeignKeysByReadPermission returns a new filtered array while
filterReferencedTablesByPermission currently mutates its input
(referencedTableNamesAndColumns) and returns void; align them by changing
filterReferencedTablesByPermission to return Promise<Array<...>> (a new filtered
array) and update the call site in get-row-by-primary-key.use.case to assign its
result back to referencedTableNamesAndColumns (i.e.,
referencedTableNamesAndColumns = await filterReferencedTablesByPermission(...));
update the filterReferencedTablesByPermission signature and internal logic to
build and return a new array rather than mutating referenced_by in place so both
functions share the same non-mutating pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/src/helpers/binary-to-hex.ts`:
- Around line 23-34: The toBinaryBuffer function silently corrupts data: the
string branch calls hexToBinary (which uses Buffer.from(...,'hex')) without
validating hex content/length, and the generic-object branch uses
Buffer.from(Object.values(...)) which coerces non-numeric values to garbage;
update toBinaryBuffer to validate strings with a hex regex and even length and
throw on invalid input (referencing toBinaryBuffer and hexToBinary), and for the
object branch (the path that currently does Buffer.from(Object.values(value as
Record<string, number>))) validate that Object.values are all finite
numbers/bytes before constructing the Buffer and throw or reject otherwise; keep
the existing valid branches for Buffer/Uint8Array and the explicit
{type:'Buffer',data:[]} path.

In
`@frontend/src/app/components/ui-components/filter-fields/binary/binary.component.ts`:
- Around line 25-27: ngOnInit currently converts this.value directly with
hexStringToBytes -> bytesToHex which silently drops non-hex inputs; instead
normalize the incoming value with parseBinaryValue first (e.g. call
parseBinaryValue(this.value) to get a normalized binary form) and then convert
that normalized result to bytes/hex for hexValue. Update ngOnInit to use
parseBinaryValue before hexStringToBytes/bytesToHex (and import parseBinaryValue
if missing) so persisted non-hex shapes are handled consistently with the
view/edit/display components.

In
`@frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.html`:
- Around line 53-61: The comma-separated ISO datetimes input in the 'in' branch
is missing the readonly binding; update the <input> used for the 'in' branch
(the one bound to [(ngModel)]="inValueText" and
(ngModelChange)="onInTextChange($event)") to include the same readonly binding
as the other branches (i.e. add [readonly]="readonly()") alongside the existing
[required]="required()" and [disabled]="disabled()" so it respects the
readonly() state like the other inputs (use normalizedLabel(), label(), key()
references as-is).
- Around line 19-52: The between-mode date/time inputs do not respect the
component's readonly state; add the readonly binding to each input in the
between branch so they mirror the non-preset branch. Specifically, for the
inputs named "{{label()}}-{{key()}}-lower-date", "-lower-time", "-upper-date",
and "-upper-time" (the inputs using
[(ngModel)]="lowerDate"/"lowerTime"/"upperDate"/"upperTime" and change handlers
onBetweenLowerChange/onBetweenUpperChange), add [readonly]="readonly()"
alongside the existing [required]="required()" and [disabled]="disabled()". This
ensures the component methods normalizedLabel(), label(), key(), required(),
disabled(), and readonly() are used consistently.

In
`@frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts`:
- Around line 129-143: The onInTextChange handler currently emits user-typed
tokens raw; instead validate and normalize each comma-split token to an ISO
datetime before emitting: in onInTextChange use new Date(token) to check
validity (new Date(token).toString() !== 'Invalid Date'), map valid dates to new
Date(token).toISOString(), filter out invalid tokens, and then emit either the
array of ISO strings via onFieldChange or emit undefined if no valid tokens
remain; also keep emitting the comparator with onComparatorChange('in') and
consider setting a component error state/property when invalid tokens are
dropped so the UI can surface feedback (referencing onInTextChange, inValueText,
onFieldChange, onComparatorChange).

In
`@frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.html`:
- Around line 10-20: The input for the 'in' comparator is missing the readonly
binding; update the <input> inside the comparator === 'in' branch (the element
bound to [(ngModel)]="inValueText" and (ngModelChange)="onInTextChange($event)")
to include [readonly]="readonly()" so it behaves consistently with other filter
fields (same area using normalizedLabel(), label(), key(), required(), and
disabled()).
- Around line 21-37: The between-mode dynamic inputs are missing the same
readonly/autofocus flags used by innerInputs; update the component class where
lowerInputs and upperInputs are constructed so both include readonly:
this.comparator === 'empty', and ensure upperInputs also includes autofocus:
this.autofocus() (matching how innerInputs sets these properties) while keeping
other input properties intact.

In
`@frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.ts`:
- Around line 104-122: onComparatorSelect currently only clears array/range
state when entering 'in'/'between', but not when leaving them; update
onComparatorSelect to detect when previous was 'in' or 'between' and the new
comparator is not, then reset inValueText, betweenLower, betweenUpper and set
value = undefined and call this.onFieldChange.emit(undefined) before emitting
this.onComparatorChange.emit(comparator) so the inner dynamic component is not
re-bound with stale array/range state; locate the onComparatorSelect method and
add that conditional branch handling the reverse transition.

In
`@frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.ts`:
- Line 19: The static property on BinaryComponent is incorrectly set to 'file'
which can misroute type-dispatch; update BinaryComponent's static type to
'binary' (or remove the static type if unnecessary) so callers like
DefaultFilterComponent.comparatorOptions no longer treat it as
FileEditComponent; locate the static declaration in BinaryComponent (static type
= 'file') and change it to static type = 'binary' (or delete it) and run tests
to verify no callers rely on the 'file' value.
- Around line 24-28: The ngOnInit currently calls emitCurrentValue() which
causes untouched binary fields to be reported as changed; remove that init-time
emission or guard it by comparing the normalized value before emitting: in
ngOnInit (method ngOnInit of this component) compute the normalized/parsed value
via parseBinaryValue(this.value()) and only call emitCurrentValue() if the
freshly constructed BinaryBufferJson differs from the incoming value() (or skip
calling emitCurrentValue() here entirely and instead call emitCurrentValue()
from onHexChange when the user edits the hexData); update references to
bytesToHex/hexData as needed so onHexChange performs the emission on real user
interaction.

In `@frontend/src/app/consts/record-edit-types.ts`:
- Around line 219-225: The mapping incorrectly treats Oracle LONG as binary;
update the record-edit-types mapping so that LONG is routed to a text editor
(e.g., LongTextEditComponent) instead of BinaryEditComponent and ensure only
RAW, 'LONG RAW', BLOB, and BFILE remain mapped to BinaryEditComponent; change
the comment above the block to reflect that LONG is character data and verify
any imports/usages of LongTextEditComponent and BinaryEditComponent (symbols:
LONG, 'LONG RAW', RAW, BLOB, BFILE, BinaryEditComponent, LongTextEditComponent).
- Around line 257-260: The edit mapping for the MSSQL type "image" is
inconsistent: record-edit-types.ts maps "image" to BinaryEditComponent while
record-view-types.ts maps it to ImageRecordViewComponent; pick a single semantic
(render-as-image or edit-as-raw-binary) and make mappings consistent. To fix,
change the "image" entry in the edit registry (where BinaryEditComponent is
referenced) to the image-specific editor (e.g., ImageEditComponent) if you want
view/edit to render as an image, or change the view registry (where
ImageRecordViewComponent is referenced) to BinaryRecordViewComponent if you want
raw hex; then propagate the chosen mapping to other registries
(filter/table-display) so the same "image" symbol resolves the same across
view/edit/filter/table-display.

In `@frontend/src/app/consts/record-view-types.ts`:
- Around line 195-200: The mapping currently assigns LONG to
BinaryRecordViewComponent but in Oracle LONG is character data; update the
record view mappings so LONG maps to CharacterRecordViewComponent while keeping
BLOB, BFILE, RAW, and 'LONG RAW' mapped to BinaryRecordViewComponent (i.e.,
change the LONG entry to use CharacterRecordViewComponent and ensure 'LONG RAW'
remains BinaryRecordViewComponent to match the earlier fix in
record-edit-types.ts).

In `@frontend/src/app/lib/binary.ts`:
- Around line 3-12: parseBinaryValue currently treats any string as raw
characters which corrupts DynamoDB hex-encoded binaries (produced by binaryToHex
in transformAndFilterRow); update parseBinaryValue in
frontend/src/app/lib/binary.ts to detect hex-encoded strings (e.g., a
/^([0-9a-fA-F]{2})+$/ pattern) and, when matched, decode hex pairs into bytes
(byte per two hex chars) instead of calling stringToBytes; keep existing
branches for Uint8Array and {data: number[]} unchanged and fall back to
stringToBytes only for non-hex strings so all drivers (DynamoDB hex strings or
Buffer-style objects) are handled consistently.

---

Outside diff comments:
In
`@frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts`:
- Around line 613-632: The in-place object replacement in updateField (in
db-table-row-edit.component, operating on this.tableRowValues[field]) currently
deletes existing props then Object.assigns the new object; add safety and tests:
change the mutation to a shallow merge using only Object.assign(existing,
updatedValue) (remove the for-delete loop) so partial payloads don't drop keys,
and add unit tests calling updateField with object payloads (e.g., the binary
shape {type:'Buffer',data:[] } and money shape {amount, currency}) to assert
existing objects are merged correctly and that this.isPrimaryKeyUpdated still
toggles when field is in keyAttributesFromURL.

---

Nitpick comments:
In `@backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts`:
- Around line 147-153: The two related permission filters are inconsistent:
filterForeignKeysByReadPermission returns a new filtered array while
filterReferencedTablesByPermission currently mutates its input
(referencedTableNamesAndColumns) and returns void; align them by changing
filterReferencedTablesByPermission to return Promise<Array<...>> (a new filtered
array) and update the call site in get-row-by-primary-key.use.case to assign its
result back to referencedTableNamesAndColumns (i.e.,
referencedTableNamesAndColumns = await filterReferencedTablesByPermission(...));
update the filterReferencedTablesByPermission signature and internal logic to
build and return a new array rather than mutating referenced_by in place so both
functions share the same non-mutating pattern.

In
`@frontend/src/app/components/ui-components/filter-fields/binary/binary.component.css`:
- Around line 1-15: The three duplicated CSS rules (.filter-row,
.comparator-field, .value-field) should be extracted into a shared stylesheet or
base component style to avoid drift; create a new shared CSS/SCSS (or a base
component stylesheet) containing those class rules and replace the duplicated
blocks in binary.component.css, date-time.component.css, and
default-filter.component.css with an import or by extending the base styles
(ensure the shared file is imported in the components or referenced by the base
filter component), keeping selectors identical so existing markup (classes
.filter-row, .comparator-field, .value-field) continues to work.

In
`@frontend/src/app/components/ui-components/filter-fields/binary/binary.component.html`:
- Around line 11-25: Add autocomplete="off" and spellcheck="false" to the hex
input element to prevent browser autofill and spellchecking from interfering
with binary/hex entry; update the <input matInput ...> tag (the element using
hexValidator, name="{{label()}}-{{key()}}", `#inputElement`, `#hexInput`="ngModel",
[(ngModel)]="hexValue" and (ngModelChange)="onHexValueChange($event)") to
include these attributes while keeping existing bindings and validators intact.

In
`@frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts`:
- Around line 115-127: Both onBetweenLowerChange and onBetweenUpperChange
contain identical logic; create a private helper named _emitBetween that calls
_composeDateTime for this.lowerDate/this.lowerTime and
this.upperDate/this.upperTime and then emits this.onFieldChange and
this.onComparatorChange('between'), replace both onBetweenLowerChange and
onBetweenUpperChange bodies with a single call to this._emitBetween(), and place
_emitBetween alongside the other private methods.

In
`@frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.css`:
- Around line 24-31: The selector `.between-fields ::ng-deep > ndc-dynamic`
unnecessarily uses `::ng-deep` and should be changed to target the direct child
(`.between-fields > ndc-dynamic`) so it no longer shadow-pierces; for the nested
rule that needs shadow-piercing (`.between-fields ::ng-deep mat-form-field`)
either keep `::ng-deep` but suppress the stylelint warning with `/*
stylelint-disable-next-line selector-pseudo-element-no-unknown */` immediately
above that selector, or instead add `::ng-deep` to the stylelint
`pseudoElements` allowlist project-wide—pick one approach and update the
selectors `.between-fields > ndc-dynamic` and `.between-fields ::ng-deep
mat-form-field` accordingly.

In
`@frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.html`:
- Around line 3-8: The parent form context is inherited so no extra form wrapper
is needed; update the hex textarea element used by the BinaryComponent template
to disable browser autofill and spellcheck by adding autocomplete="off" and
spellcheck="false" to the <textarea> that binds [(ngModel)]="hexData" (template
ref `#hexContent`="ngModel", and uses label(), key(), required(), disabled(),
readonly(), and onHexChange()) so the hex input won't be interfered with by
browser autofill or spellchecking.

In
`@frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.ts`:
- Around line 30-33: onHexChange currently uses a typing escape hatch by calling
hexValidation()({ value: this.hexData } as never); instead, change it to pass a
correctly-typed control payload or use a simple helper: either construct a
minimal AbstractControl-like object (e.g. { value: this.hexData } cast only to
unknown then to AbstractControl) when calling hexValidation(), or replace the
validator call with a dedicated isValidHex(this.hexData) helper and set
this.isInvalidInput based on that; update onHexChange and references to
hexValidation/validation logic accordingly so TypeScript types are preserved and
no 'as never' cast is used.

In
`@frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.html`:
- Around line 3-4: The matTooltip binding should provide null when not truncated
to fully disable the tooltip; update the binding that uses isTruncated() and
hexValue() so it returns hexValue() when isTruncated() is true and null
otherwise (replace the current '' fallback), referencing the isTruncated() and
hexValue() expressions used in that matTooltip attribute to locate where to
change.

In
`@frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.ts`:
- Around line 21-27: Extract the shared hex truncation logic into a helper
(e.g., formatHexForDisplay(hex: string, max?: number)) in
frontend/src/app/lib/binary.ts and replace the duplicated code in both
BinaryDisplayComponent and the table-display binary component: remove the inline
logic that uses MAX_DISPLAY_LENGTH in displayText and isTruncated, call
formatHexForDisplay(this.hexValue(), MAX_DISPLAY_LENGTH) to compute displayText
(returning the em-dash for empty values and ellipses when truncated) and compute
isTruncated by comparing the raw hex length to max (or expose an API like {
text, truncated } from the helper); ensure the helper is exported and both
components import and use it so truncation behaviour is centralized.

In
`@frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.ts`:
- Around line 18-25: The three component signals bytes, hexValue, and
displayText are declared as public but should be protected readonly per
component state guidelines; change their declarations from public readonly to
protected readonly for bytes, hexValue, and displayText (in binary.component.ts)
so they remain accessible to the template but not publicly exposed; if tests
require access (binary.component.spec.ts), avoid widening visibility and instead
introduce a testable type alias or testing accessor as described in the spec
guidelines rather than making these signals public.

In `@frontend/src/app/lib/binary.ts`:
- Around line 22-32: The hexStringToBytes function currently swallows malformed
input by returning [] — change it to return null for any invalid input (use hex
= hex.trim(); strip a leading "0x"/"0X" before normalizing) and make parse
checks explicit (if parseInt yields NaN -> return null) so callers can
distinguish "empty result" from "invalid input"; update
BinaryEditComponent.emitCurrentValue() (and any other callers) to handle a null
return (skip emitting toBufferJson([]) and surface validation errors) rather
than relying on [] to signal failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 35727b39-a74e-4ee0-ac81-6ed0b5a20f07

📥 Commits

Reviewing files that changed from the base of the PR and between 6cac9f9 and b94e91f.

📒 Files selected for processing (44)
  • backend/src/entities/table/use-cases/add-row-in-table.use.case.ts
  • backend/src/entities/table/use-cases/bulk-update-rows-in-table.use.case.ts
  • backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts
  • backend/src/entities/table/use-cases/get-table-rows.use.case.ts
  • backend/src/entities/table/use-cases/update-row-in-table.use.case.ts
  • backend/src/entities/table/utils/convert-binary-data-in-row.util.ts
  • backend/src/entities/table/utils/convert-hex-data-in-primary-key.util.ts
  • backend/src/entities/table/utils/convert-hex-data-in-row.util.ts
  • backend/src/entities/table/utils/process-found-rows-util.ts
  • backend/src/helpers/binary-to-hex.ts
  • backend/test/ava-tests/non-saas-tests/non-saas-postgres-with-binary-e2e.test.ts
  • backend/test/ava-tests/saas-tests/postgres-with-binary-e2e.test.ts
  • frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts
  • frontend/src/app/components/ui-components/filter-fields/binary/binary.component.css
  • frontend/src/app/components/ui-components/filter-fields/binary/binary.component.html
  • frontend/src/app/components/ui-components/filter-fields/binary/binary.component.spec.ts
  • frontend/src/app/components/ui-components/filter-fields/binary/binary.component.ts
  • frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.css
  • frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.html
  • frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.spec.ts
  • frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts
  • frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.css
  • frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.html
  • frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.spec.ts
  • frontend/src/app/components/ui-components/filter-fields/default-filter/default-filter.component.ts
  • frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.css
  • frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.html
  • frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.spec.ts
  • frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.ts
  • frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.css
  • frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.html
  • frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.spec.ts
  • frontend/src/app/components/ui-components/record-view-fields/binary/binary.component.ts
  • frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.css
  • frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.html
  • frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.spec.ts
  • frontend/src/app/components/ui-components/table-display-fields/binary/binary.component.ts
  • frontend/src/app/consts/filter-types.ts
  • frontend/src/app/consts/record-edit-types.ts
  • frontend/src/app/consts/record-view-types.ts
  • frontend/src/app/consts/table-display-types.ts
  • frontend/src/app/lib/binary.spec.ts
  • frontend/src/app/lib/binary.ts
  • shared-code/src/shared/enums/table-widget-type.enum.ts
💤 Files with no reviewable changes (2)
  • backend/src/entities/table/utils/process-found-rows-util.ts
  • backend/src/entities/table/utils/convert-binary-data-in-row.util.ts

Comment on lines +23 to +34
export function toBinaryBuffer(value: unknown): Buffer {
if (Buffer.isBuffer(value)) return value;
if (value instanceof Uint8Array) return Buffer.from(value);
if (typeof value === 'string') return hexToBinary(value);
if (value && typeof value === 'object') {
const v = value as { type?: string; data?: unknown };
if (v.type === 'Buffer' && Array.isArray(v.data)) return Buffer.from(v.data as number[]);
if (Array.isArray(v.data)) return Buffer.from(v.data as number[]);
return Buffer.from(Object.values(value as Record<string, number>));
}
return hexToBinary(String(value));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Silent data corruption risk for non-hex strings and heterogeneous objects.

Two branches in toBinaryBuffer silently produce corrupted buffers instead of signaling the problem:

  1. Line 26: hexToBinary(value) delegates to Buffer.from(value, 'hex'), which silently skips any non-hex character and truncates on odd length. A stray non-hex character from the frontend (e.g. a user-entered ASCII value, base64, or legacy representation) becomes a partial/empty buffer that is persisted to the DB without error.
  2. Line 31: Buffer.from(Object.values(value as Record<string, number>)) is reached for any unrecognized object shape. If any value is non-numeric, Buffer.from coerces it to 0/garbage, again without error.

Given this helper is now in the write path for both rows and primary keys (convertHexDataInRowUtil / convertHexDataInPrimaryKeyUtil), a malformed payload can silently write wrong bytes. Consider validating hex input (e.g. /^[0-9a-fA-F]*$/ with even length) and either throwing or rejecting the unknown-object fallback.

🛡️ Proposed hardening
 export function toBinaryBuffer(value: unknown): Buffer {
 	if (Buffer.isBuffer(value)) return value;
 	if (value instanceof Uint8Array) return Buffer.from(value);
-	if (typeof value === 'string') return hexToBinary(value);
+	if (typeof value === 'string') {
+		if (value.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(value)) {
+			throw new Error('Invalid hex string for binary column');
+		}
+		return hexToBinary(value);
+	}
 	if (value && typeof value === 'object') {
 		const v = value as { type?: string; data?: unknown };
 		if (v.type === 'Buffer' && Array.isArray(v.data)) return Buffer.from(v.data as number[]);
 		if (Array.isArray(v.data)) return Buffer.from(v.data as number[]);
-		return Buffer.from(Object.values(value as Record<string, number>));
+		const values = Object.values(value as Record<string, unknown>);
+		if (!values.every((n) => typeof n === 'number' && Number.isInteger(n) && n >= 0 && n <= 255)) {
+			throw new Error('Unsupported object shape for binary column');
+		}
+		return Buffer.from(values as number[]);
 	}
-	return hexToBinary(String(value));
+	throw new Error(`Unsupported value type for binary column: ${typeof value}`);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/src/helpers/binary-to-hex.ts` around lines 23 - 34, The
toBinaryBuffer function silently corrupts data: the string branch calls
hexToBinary (which uses Buffer.from(...,'hex')) without validating hex
content/length, and the generic-object branch uses
Buffer.from(Object.values(...)) which coerces non-numeric values to garbage;
update toBinaryBuffer to validate strings with a hex regex and even length and
throw on invalid input (referencing toBinaryBuffer and hexToBinary), and for the
object branch (the path that currently does Buffer.from(Object.values(value as
Record<string, number>))) validate that Object.values are all finite
numbers/bytes before constructing the Buffer and throw or reject otherwise; keep
the existing valid branches for Buffer/Uint8Array and the explicit
{type:'Buffer',data:[]} path.

Comment on lines +25 to +27
override ngOnInit(): void {
this.hexValue = bytesToHex(hexStringToBytes(this.value ?? ''));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Silent loss of initial value when it isn't valid hex.

hexStringToBytes returns [] for malformed hex, so any pre-existing filter value that isn't pure hex (e.g. a char-code string, a Buffer-JSON object, or odd whitespace) is silently dropped here and hexValue becomes ''. If the filter is restored from a persisted state that was saved in a non-hex shape, the user loses their filter without feedback. Consider routing through parseBinaryValue first (to mirror the view/edit/display components) so the initial value is normalized consistently.

Proposed change
-import { bytesToHex, hexStringToBytes } from 'src/app/lib/binary';
+import { bytesToHex, parseBinaryValue } from 'src/app/lib/binary';
@@
-	override ngOnInit(): void {
-		this.hexValue = bytesToHex(hexStringToBytes(this.value ?? ''));
-	}
+	override ngOnInit(): void {
+		this.hexValue = bytesToHex(parseBinaryValue(this.value ?? ''));
+	}
📝 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.

Suggested change
override ngOnInit(): void {
this.hexValue = bytesToHex(hexStringToBytes(this.value ?? ''));
}
override ngOnInit(): void {
this.hexValue = bytesToHex(parseBinaryValue(this.value ?? ''));
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/binary/binary.component.ts`
around lines 25 - 27, ngOnInit currently converts this.value directly with
hexStringToBytes -> bytesToHex which silently drops non-hex inputs; instead
normalize the incoming value with parseBinaryValue first (e.g. call
parseBinaryValue(this.value) to get a normalized binary form) and then convert
that normalized result to bytes/hex for hexValue. Update ngOnInit to use
parseBinaryValue before hexStringToBytes/bytesToHex (and import parseBinaryValue
if missing) so persisted non-hex shapes are handled consistently with the
view/edit/display components.

Comment on lines +19 to +52
@if (filterMode === 'between') {
<div class="value-field between-fields">
<div class="field-couple">
<mat-form-field appearance="outline">
<mat-label>{{normalizedLabel()}} (from date)</mat-label>
<input type="date" matInput name="{{label()}}-{{key()}}-lower-date"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="lowerDate" (change)="onBetweenLowerChange()">
</mat-form-field>

<mat-form-field appearance="outline">
<mat-label>(from time)</mat-label>
<input type="time" matInput name="{{label()}}-{{key()}}-lower-time"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="lowerTime" (change)="onBetweenLowerChange()">
</mat-form-field>
</div>

<div class="field-couple">
<mat-form-field appearance="outline">
<mat-label>{{normalizedLabel()}} (to date)</mat-label>
<input type="date" matInput name="{{label()}}-{{key()}}-upper-date"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="upperDate" (change)="onBetweenUpperChange()">
</mat-form-field>

<mat-form-field appearance="outline">
<mat-label>(to time)</mat-label>
<input type="time" matInput name="{{label()}}-{{key()}}-upper-time"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="upperTime" (change)="onBetweenUpperChange()">
</mat-form-field>
</div>
</div>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistency: readonly not propagated to between-mode inputs.

The existing non-preset branch (lines 66–78) binds [readonly]="readonly()", but the new between branch only binds required and disabled. If this component can be rendered read-only, the between fields will remain editable.

Proposed fix
-                    <input type="date" matInput name="{{label()}}-{{key()}}-lower-date"
-                        [required]="required()" [disabled]="disabled()"
-                        [(ngModel)]="lowerDate" (change)="onBetweenLowerChange()">
+                    <input type="date" matInput name="{{label()}}-{{key()}}-lower-date"
+                        [required]="required()" [disabled]="disabled()" [readonly]="readonly()"
+                        [(ngModel)]="lowerDate" (change)="onBetweenLowerChange()">

(Apply similarly to the three remaining inputs in the between block.)

📝 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.

Suggested change
@if (filterMode === 'between') {
<div class="value-field between-fields">
<div class="field-couple">
<mat-form-field appearance="outline">
<mat-label>{{normalizedLabel()}} (from date)</mat-label>
<input type="date" matInput name="{{label()}}-{{key()}}-lower-date"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="lowerDate" (change)="onBetweenLowerChange()">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>(from time)</mat-label>
<input type="time" matInput name="{{label()}}-{{key()}}-lower-time"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="lowerTime" (change)="onBetweenLowerChange()">
</mat-form-field>
</div>
<div class="field-couple">
<mat-form-field appearance="outline">
<mat-label>{{normalizedLabel()}} (to date)</mat-label>
<input type="date" matInput name="{{label()}}-{{key()}}-upper-date"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="upperDate" (change)="onBetweenUpperChange()">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>(to time)</mat-label>
<input type="time" matInput name="{{label()}}-{{key()}}-upper-time"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="upperTime" (change)="onBetweenUpperChange()">
</mat-form-field>
</div>
</div>
`@if` (filterMode === 'between') {
<div class="value-field between-fields">
<div class="field-couple">
<mat-form-field appearance="outline">
<mat-label>{{normalizedLabel()}} (from date)</mat-label>
<input type="date" matInput name="{{label()}}-{{key()}}-lower-date"
[required]="required()" [disabled]="disabled()" [readonly]="readonly()"
[(ngModel)]="lowerDate" (change)="onBetweenLowerChange()">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>(from time)</mat-label>
<input type="time" matInput name="{{label()}}-{{key()}}-lower-time"
[required]="required()" [disabled]="disabled()" [readonly]="readonly()"
[(ngModel)]="lowerTime" (change)="onBetweenLowerChange()">
</mat-form-field>
</div>
<div class="field-couple">
<mat-form-field appearance="outline">
<mat-label>{{normalizedLabel()}} (to date)</mat-label>
<input type="date" matInput name="{{label()}}-{{key()}}-upper-date"
[required]="required()" [disabled]="disabled()" [readonly]="readonly()"
[(ngModel)]="upperDate" (change)="onBetweenUpperChange()">
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>(to time)</mat-label>
<input type="time" matInput name="{{label()}}-{{key()}}-upper-time"
[required]="required()" [disabled]="disabled()" [readonly]="readonly()"
[(ngModel)]="upperTime" (change)="onBetweenUpperChange()">
</mat-form-field>
</div>
</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.html`
around lines 19 - 52, The between-mode date/time inputs do not respect the
component's readonly state; add the readonly binding to each input in the
between branch so they mirror the non-preset branch. Specifically, for the
inputs named "{{label()}}-{{key()}}-lower-date", "-lower-time", "-upper-date",
and "-upper-time" (the inputs using
[(ngModel)]="lowerDate"/"lowerTime"/"upperDate"/"upperTime" and change handlers
onBetweenLowerChange/onBetweenUpperChange), add [readonly]="readonly()"
alongside the existing [required]="required()" and [disabled]="disabled()". This
ensures the component methods normalizedLabel(), label(), key(), required(),
disabled(), and readonly() are used consistently.

Comment on lines +53 to +61
} @else if (filterMode === 'in') {
<mat-form-field class="value-field" appearance="outline">
<mat-label>{{normalizedLabel()}} (comma-separated ISO datetimes)</mat-label>
<input matInput name="{{label()}}-{{key()}}-in"
#inputElement
placeholder="2024-01-01T00:00:00Z, 2024-02-01T00:00:00Z"
[required]="required()" [disabled]="disabled()"
[(ngModel)]="inValueText" (ngModelChange)="onInTextChange($event)">
</mat-form-field>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same readonly propagation gap on the in branch.

readonly() is not bound on the comma-separated ISO input. Mirror the other branches for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.html`
around lines 53 - 61, The comma-separated ISO datetimes input in the 'in' branch
is missing the readonly binding; update the <input> used for the 'in' branch
(the one bound to [(ngModel)]="inValueText" and
(ngModelChange)="onInTextChange($event)") to include the same readonly binding
as the other branches (i.e. add [readonly]="readonly()") alongside the existing
[required]="required()" and [disabled]="disabled()" so it respects the
readonly() state like the other inputs (use normalizedLabel(), label(), key()
references as-is).

Comment on lines +129 to +143
onInTextChange(text: string): void {
this.inValueText = text;
const parts = text
.split(',')
.map((v) => v.trim())
.filter((v) => v.length > 0);

if (parts.length === 0) {
this.onFieldChange.emit(undefined);
return;
}

this.onFieldChange.emit(parts);
this.onComparatorChange.emit('in');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

onInTextChange does not validate each entry as a datetime.

Unlike the preset/gte/between branches that build ISO strings from parsed date/time fields, the in branch forwards whatever the user typed, comma-split and trimmed. Non-ISO values will be emitted as-is and presumably fail at the backend. Consider validating each token with new Date(v).toString() !== 'Invalid Date' (or an explicit parse) and either dropping invalid entries or surfacing an error state.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts`
around lines 129 - 143, The onInTextChange handler currently emits user-typed
tokens raw; instead validate and normalize each comma-split token to an ISO
datetime before emitting: in onInTextChange use new Date(token) to check
validity (new Date(token).toString() !== 'Invalid Date'), map valid dates to new
Date(token).toISOString(), filter out invalid tokens, and then emit either the
array of ISO strings via onFieldChange or emit undefined if no valid tokens
remain; also keep emitting the comparator with onComparatorChange('in') and
consider setting a component error state/property when invalid tokens are
dropped so the UI can surface feedback (referencing onInTextChange, inValueText,
onFieldChange, onComparatorChange).

Comment on lines +24 to +28
ngOnInit(): void {
super.ngOnInit();
this.hexData = bytesToHex(parseBinaryValue(this.value()));
this.emitCurrentValue();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Emitting on ngOnInit may mark the form dirty on load.

emitCurrentValue() fires on every init, converting the incoming value (string or BinaryBufferJson) into a freshly constructed BinaryBufferJson and emitting it upward. If the parent tracks "field changed" via onFieldChange, every untouched binary field will be reported as modified the moment the edit view opens, potentially producing spurious dirty state or no-op updates sent back to the server.

Consider only emitting when the user actually interacts (in onHexChange), or guarding the init-time emission behind a check that the value needed normalization.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/components/ui-components/record-edit-fields/binary/binary.component.ts`
around lines 24 - 28, The ngOnInit currently calls emitCurrentValue() which
causes untouched binary fields to be reported as changed; remove that init-time
emission or guard it by comparing the normalized value before emitting: in
ngOnInit (method ngOnInit of this component) compute the normalized/parsed value
via parseBinaryValue(this.value()) and only call emitCurrentValue() if the
freshly constructed BinaryBufferJson differs from the incoming value() (or skip
calling emitCurrentValue() here entirely and instead call emitCurrentValue()
from onHexChange when the user edits the hexData); update references to
bytesToHex/hexData as needed so onHexChange performs the emission on real user
interaction.

Comment on lines +219 to 225
//binary
BLOB: BinaryEditComponent,
BFILE: BinaryEditComponent,
RAW: BinaryEditComponent,
'LONG RAW': BinaryEditComponent,
LONG: BinaryEditComponent,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Oracle LONG is character data, not binary.

In Oracle, LONG stores up to 2 GB of character data; it's LONG RAW that is the binary variant. Grouping LONG under the //binary block and routing it to BinaryEditComponent will render text content as hex in the editor. Consider keeping LONG on a text-oriented editor (e.g. LongTextEditComponent) and only mapping LONG RAW/RAW/BLOB/BFILE to BinaryEditComponent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/consts/record-edit-types.ts` around lines 219 - 225, The
mapping incorrectly treats Oracle LONG as binary; update the record-edit-types
mapping so that LONG is routed to a text editor (e.g., LongTextEditComponent)
instead of BinaryEditComponent and ensure only RAW, 'LONG RAW', BLOB, and BFILE
remain mapped to BinaryEditComponent; change the comment above the block to
reflect that LONG is character data and verify any imports/usages of
LongTextEditComponent and BinaryEditComponent (symbols: LONG, 'LONG RAW', RAW,
BLOB, BFILE, BinaryEditComponent, LongTextEditComponent).

Comment on lines +257 to +260
//binary
binary: BinaryEditComponent,
varbinary: BinaryEditComponent,
image: BinaryEditComponent,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Mapping for MSSQL image is inconsistent with the record-view side.

Here image is routed to BinaryEditComponent, but in frontend/src/app/consts/record-view-types.ts (line 236) image still resolves to ImageRecordViewComponent. Users will see the field rendered as an image in view mode and as a raw hex editor in edit mode. Pick one semantic and apply it consistently across edit/view/filter/table-display registries.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/consts/record-edit-types.ts` around lines 257 - 260, The
edit mapping for the MSSQL type "image" is inconsistent: record-edit-types.ts
maps "image" to BinaryEditComponent while record-view-types.ts maps it to
ImageRecordViewComponent; pick a single semantic (render-as-image or
edit-as-raw-binary) and make mappings consistent. To fix, change the "image"
entry in the edit registry (where BinaryEditComponent is referenced) to the
image-specific editor (e.g., ImageEditComponent) if you want view/edit to render
as an image, or change the view registry (where ImageRecordViewComponent is
referenced) to BinaryRecordViewComponent if you want raw hex; then propagate the
chosen mapping to other registries (filter/table-display) so the same "image"
symbol resolves the same across view/edit/filter/table-display.

Comment on lines +195 to +200
//binary
BLOB: BinaryRecordViewComponent,
BFILE: BinaryRecordViewComponent,
RAW: BinaryRecordViewComponent,
'LONG RAW': BinaryRecordViewComponent,
LONG: BinaryRecordViewComponent,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same Oracle LONG concern as in record-edit-types.ts.

LONG is character data in Oracle; only LONG RAW is binary. See the comment on frontend/src/app/consts/record-edit-types.ts lines 219–225 — please keep the mappings aligned once that is resolved.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/consts/record-view-types.ts` around lines 195 - 200, The
mapping currently assigns LONG to BinaryRecordViewComponent but in Oracle LONG
is character data; update the record view mappings so LONG maps to
CharacterRecordViewComponent while keeping BLOB, BFILE, RAW, and 'LONG RAW'
mapped to BinaryRecordViewComponent (i.e., change the LONG entry to use
CharacterRecordViewComponent and ensure 'LONG RAW' remains
BinaryRecordViewComponent to match the earlier fix in record-edit-types.ts).

Comment on lines +3 to +12
export function parseBinaryValue(value: unknown): number[] {
if (value == null || value === '') return [];
if (value instanceof Uint8Array) return Array.from(value);
if (typeof value === 'string') return stringToBytes(value);
if (typeof value === 'object') {
const data = (value as { data?: unknown }).data;
if (Array.isArray(data)) return (data as number[]).slice();
}
return [];
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for backend serialization of binary fields to see what shape reaches the frontend.
rg -nP -C3 '\b(bytea|binary|BLOB|RAW|LONG\s*RAW|varbinary)\b' --type=ts -g '!**/*.spec.ts' -g '!frontend/**'
rg -nP -C3 '\b(toBinaryBuffer|hexToBinary|binaryToHex|bytesToHex|bufferToJson)\b' --type=ts

Repository: rocket-admin/rocketadmin

Length of output: 47378


🏁 Script executed:

#!/bin/bash
# Check where convertHexDataInRowUtil is actually called and how rows are returned
rg -n "convertHexDataInRowUtil" --type=ts -A 5 -B 5
# Check the get-table-rows response construction
rg -n "getTableRows\|buildDAOsTableSettings" backend/src/entities/table/use-cases/get-table-rows.use.case.ts | head -30
# Find record-edit-types to see supported drivers
fd -name "*record*edit*types*" -type f

Repository: rocket-admin/rocketadmin

Length of output: 10767


🏁 Script executed:

#!/bin/bash
# Check the get-table-rows.use.case.ts file structure and how rows are returned
wc -l backend/src/entities/table/use-cases/get-table-rows.use.case.ts
sed -n '1,50p' backend/src/entities/table/use-cases/get-table-rows.use.case.ts
# Look for where rows are returned in response
rg -n "return {|rows:" backend/src/entities/table/use-cases/get-table-rows.use.case.ts | head -20

Repository: rocket-admin/rocketadmin

Length of output: 4789


🏁 Script executed:

#!/bin/bash
# Look for return statements and processRowsUtil
rg -n "processRowsUtil\|return {" backend/src/entities/table/use-cases/get-table-rows.use.case.ts
# Check the processRowsUtil implementation
cat backend/src/entities/table/utils/process-found-rows-util.ts

Repository: rocket-admin/rocketadmin

Length of output: 2427


🏁 Script executed:

#!/bin/bash
# Look at get-table-rows.use.case.ts around the DAO calls and response
sed -n '200,250p' backend/src/entities/table/use-cases/get-table-rows.use.case.ts
# Look for the main execute function
rg -n "async execute" backend/src/entities/table/use-cases/get-table-rows.use.case.ts -A 100 | head -80

Repository: rocket-admin/rocketadmin

Length of output: 2462


🏁 Script executed:

#!/bin/bash
# Look for the DAO call that fetches rows
rg -n "dao\.getRows\|dao\.foundRows" backend/src/entities/table/use-cases/get-table-rows.use.case.ts -B 5 -A 5
# Also check if there's any conversion happening on rows before they're returned
sed -n '100,200p' backend/src/entities/table/use-cases/get-table-rows.use.case.ts

Repository: rocket-admin/rocketadmin

Length of output: 3776


🏁 Script executed:

#!/bin/bash
# Check the test case to see what format binary data is received as from the backend
sed -n '80,130p' backend/test/ava-tests/non-saas-tests/non-saas-postgres-with-binary-e2e.test.ts

Repository: rocket-admin/rocketadmin

Length of output: 1900


🏁 Script executed:

#!/bin/bash
# The test shows binaryToHex() is called on the response, meaning it's getting Buffer-like objects.
# Let's check if anywhere else the backend sends binary as hex strings instead
rg -n "binaryToHex\|hexToBinary" backend/src/entities/table/use-cases/get-table-rows.use.case.ts -B 3 -A 3
# Check all database drivers to see if any might return hex strings
rg -n "driver\|connection\.type" backend/src/entities/table/use-cases/get-table-rows.use.case.ts | head -20
# Look for where binary data might be returned as hex in various DAOs
fd -name "data-access-object*.ts" -type f backend/src shared-code/src | head -10

Repository: rocket-admin/rocketadmin

Length of output: 298


🏁 Script executed:

#!/bin/bash
# Check what binaryToHex/hexToBinary are used for in get-table-rows
rg "binaryToHex|hexToBinary" backend/src/entities/table/use-cases/get-table-rows.use.case.ts -B 3 -A 3
# Find data access object implementations
find backend/src shared-code/src -name "data-access-object*.ts" -type f

Repository: rocket-admin/rocketadmin

Length of output: 2224


🏁 Script executed:

#!/bin/bash
# Check how Postgres DAO handles binary data
rg "getRowsFromTable" shared-code/src/data-access-layer/data-access-objects/data-access-object-postgres.ts -A 30 | head -50
# Check MongoDB DAO for binary handling
rg "getRowsFromTable" shared-code/src/data-access-layer/data-access-objects/data-access-object-mongodb.ts -A 30 | head -50
# Check if any DAO converts binary to hex before returning
rg "binaryToHex|\.toString\('hex'\)" shared-code/src/data-access-layer/data-access-objects/ -B 2 -A 2

Repository: rocket-admin/rocketadmin

Length of output: 4713


🏁 Script executed:

#!/bin/bash
# Check the full context of DynamoDB's binary conversion - where it happens
rg "binaryToHex" shared-code/src/data-access-layer/data-access-objects/data-access-object-dynamodb.ts -B 10 -A 5 | head -80
# Check what transformedRow is - is this the return data?
sed -n '770,800p' shared-code/src/data-access-layer/data-access-objects/data-access-object-dynamodb.ts

Repository: rocket-admin/rocketadmin

Length of output: 2673


🏁 Script executed:

#!/bin/bash
# Critical finding: DynamoDB converts binary to hex strings!
# Check if OTHER DAOs do the same conversion
rg "binaryToHex" shared-code/src/data-access-layer/data-access-objects/ -l
# Check what getRowsFromTable returns in DynamoDB
sed -n '600,650p' shared-code/src/data-access-layer/data-access-objects/data-access-object-dynamodb.ts
# Check if there's any similar conversion in other DAOs (MySQL, Postgres, etc)
rg "\.toString\('hex'\)|binaryToHex" shared-code/src/data-access-layer/data-access-objects/data-access-object-mysql.ts shared-code/src/data-access-layer/data-access-objects/data-access-object-postgres.ts shared-code/src/data-access-layer/data-access-objects/data-access-object-oracle.ts

Repository: rocket-admin/rocketadmin

Length of output: 1548


🏁 Script executed:

#!/bin/bash
# Check all DAOs for binary conversion on response
for dao in data-access-object-mysql data-access-object-postgres data-access-object-oracle data-access-object-mssql data-access-object-ibmdb2 data-access-object-cassandra data-access-object-clickhouse; do
  echo "=== Checking $dao ==="
  rg "binaryToHex|hexToBinary|toString\('hex'\)" "shared-code/src/data-access-layer/data-access-objects/$dao.ts" 2>/dev/null | head -5
done
# Check what format Postgres DAO returns (likely just Buffer objects from knex)
sed -n '60,120p' shared-code/src/data-access-layer/data-access-objects/data-access-object-postgres.ts

Repository: rocket-admin/rocketadmin

Length of output: 2432


🏁 Script executed:

#!/bin/bash
# Critical finding: DynamoDB converts to hex but others don't!
# Check if there's any post-processing that converts ALL responses to hex before frontend
rg "binaryToHex" backend/src/entities/table/use-cases/get-table-rows.use.case.ts -B 3 -A 3
# Check the response object construction - does it convert rows?
sed -n '160,180p' backend/src/entities/table/use-cases/get-table-rows.use.case.ts
# Look for any middleware or interceptor that processes responses
find backend/src -name "*interceptor*" -o -name "*middleware*" | head -5

Repository: rocket-admin/rocketadmin

Length of output: 965


🏁 Script executed:

#!/bin/bash
# Check the DynamoDB getRowsFromTable method - does it apply binaryToHex conversion?
sed -n '300,450p' shared-code/src/data-access-layer/data-access-objects/data-access-object-dynamodb.ts
# Look for where transformedRow is used in getRowsFromTable
rg "getRowsFromTable" shared-code/src/data-access-layer/data-access-objects/data-access-object-dynamodb.ts -A 100 | grep -A 100 "public async getRowsFromTable"

Repository: rocket-admin/rocketadmin

Length of output: 9123


🏁 Script executed:

#!/bin/bash
# DynamoDB clearly converts to hex! Check if this is an issue
# Let me check the transformAndFilterRow implementation more carefully
sed -n '750,820p' shared-code/src/data-access-layer/data-access-objects/data-access-object-dynamodb.ts
# Check record-edit-types to see which drivers are supported
find . -name "record-edit-types*" -type f

Repository: rocket-admin/rocketadmin

Length of output: 2169


🏁 Script executed:

#!/bin/bash
# Check the supported database drivers
cat frontend/src/app/consts/record-edit-types.ts

Repository: rocket-admin/rocketadmin

Length of output: 11653


DynamoDB returns binary values as hex strings, not {type: 'Buffer', data: number[]} format — this will break the frontend parser.

The backend has an inconsistency: parseBinaryValue() on the frontend expects binary values to be either Uint8Array or {type: 'Buffer', data: number[]}. However, the DynamoDB DAO explicitly converts binary fields to hex strings via binaryToHex() in its transformAndFilterRow() method. When the frontend receives a hex string and processes it through stringToBytes(), it treats each character as a byte (e.g., "61" becomes char codes [0x36, 0x31] instead of [0x61]), resulting in corrupted display.

Other supported drivers (Postgres, MySQL, Oracle, MSSQL) return native Buffer objects which serialize to {type: 'Buffer', data: [...]} and work correctly. Ensure DynamoDB (and any other drivers) return the same format, or update the frontend parser to handle hex-string inputs from all drivers consistently.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/lib/binary.ts` around lines 3 - 12, parseBinaryValue
currently treats any string as raw characters which corrupts DynamoDB
hex-encoded binaries (produced by binaryToHex in transformAndFilterRow); update
parseBinaryValue in frontend/src/app/lib/binary.ts to detect hex-encoded strings
(e.g., a /^([0-9a-fA-F]{2})+$/ pattern) and, when matched, decode hex pairs into
bytes (byte per two hex chars) instead of calling stringToBytes; keep existing
branches for Uint8Array and {data: number[]} unchanged and fall back to
stringToBytes only for non-hex strings so all drivers (DynamoDB hex strings or
Buffer-style objects) are handled consistently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants