From 4ef8861e66e25db32c830b6f33ada92aeaa7e0dd Mon Sep 17 00:00:00 2001 From: KushalLukhi Date: Fri, 20 Mar 2026 06:38:52 +0000 Subject: [PATCH] fix(protobuf): sanitize invalid JSON Schema property identifiers --- pages/json-schema-to-protobuf.tsx | 57 ++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/pages/json-schema-to-protobuf.tsx b/pages/json-schema-to-protobuf.tsx index 90a84737..9e9aad52 100644 --- a/pages/json-schema-to-protobuf.tsx +++ b/pages/json-schema-to-protobuf.tsx @@ -3,9 +3,64 @@ import * as React from "react"; import { useCallback } from "react"; import convert from "jsonschema-protobuf"; +const PROTO_IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_]*$/; + +function sanitizeIdentifier(identifier: string) { + let sanitized = identifier.replace(/[^A-Za-z0-9_]/g, "_"); + if (!sanitized || /^[0-9]/.test(sanitized)) { + sanitized = `field_${sanitized}`; + } + return sanitized; +} + +function sanitizeSchema(value: unknown): unknown { + if (Array.isArray(value)) { + return value.map(sanitizeSchema); + } + + if (!value || typeof value !== "object") { + return value; + } + + const source = value as Record; + const next: Record = {}; + + for (const [key, entry] of Object.entries(source)) { + if (key === "properties" && entry && typeof entry === "object" && !Array.isArray(entry)) { + const sanitizedProperties: Record = {}; + const nameCounts: Record = {}; + + for (const [propName, propValue] of Object.entries(entry as Record)) { + let safeName = PROTO_IDENTIFIER_RE.test(propName) + ? propName + : sanitizeIdentifier(propName); + + const count = nameCounts[safeName] ?? 0; + nameCounts[safeName] = count + 1; + if (count > 0) { + safeName = `${safeName}_${count + 1}`; + } + + sanitizedProperties[safeName] = sanitizeSchema(propValue); + } + + next[key] = sanitizedProperties; + continue; + } + + next[key] = sanitizeSchema(entry); + } + + return next; +} + export default function JsonSchemaToProtobuf() { const transformer = useCallback( - async ({ value }) => convert(value), + async ({ value }) => { + const parsed = JSON.parse(value); + const sanitized = sanitizeSchema(parsed); + return convert(JSON.stringify(sanitized)); + }, [] );