feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint no-param-reassign: ["error", { "props": false }] */
import * as path from 'path';
import * as fs from 'fs';
import { parse as protoParse } from 'proto-parser';
import { logAndThrowError } from '../utils';
import {
type ProtoDocument,
type ProtoError,
SyntaxType,
type MessageDefinition,
type ServiceDefinition,
type MethodDefinition,
type NamespaceBase,
type NamespaceDefinition,
type FieldExtensionConfig,
type ServiceExtensionConfig,
type FunctionExtensionConfig,
} from './type';
import * as extensionUtil from '../common/extension_util';
export * from './type';
// NOTE: cover old Rules: (api_method) = 'POST'
const oldRuleRegExp =
/\(api_req\)\.|\(api_resp\)\.|\(api_method\)\.|\(pb_idl\.api_method\)\./;
function extractExtensionConfigFromOption(
optionKey: string,
optionValue: string,
) {
let key = '';
if (/^\(api\.(.*)\)/.test(optionKey)) {
key = optionKey.replace(/^\(api\.(.*)\)/, '$1');
} else if (oldRuleRegExp.test(optionKey)) {
key = optionKey.replace(oldRuleRegExp, '');
} else {
return false;
}
const config = extensionUtil.extractExtensionConfig(key, optionValue);
return config;
}
function convertFieldOptions(message: MessageDefinition) {
const { name, fields } = message;
for (const field of Object.values(fields)) {
if (field.options && Object.keys(field.options).length > 0) {
const extensionConfig: FieldExtensionConfig = {};
const fieldName = field.name;
const fieldType = field.type;
for (const key of Object.keys(field.options)) {
const value = field.options[key];
const config = extractExtensionConfigFromOption(key, value);
/* istanbul ignore else */
if (config) {
if ('key' in config) {
// a key which is the same with the field name will make no sense
if (config.key === fieldName) {
delete config.key;
}
} else if (config.position === 'path') {
if (
['double', 'float', 'bool', 'bytes'].includes(fieldType.value)
) {
const errorMessage = `the type of path parameter '${fieldName}' in '${name}' should be string or integer`;
logAndThrowError(errorMessage);
}
}
Object.assign(extensionConfig, config);
}
}
field.extensionConfig =
extensionUtil.filterFieldExtensionConfig(extensionConfig);
}
}
}
function convertMethodOptions(method: MethodDefinition) {
if (!(method.options && Object.keys(method.options).length > 0)) {
return;
}
const extensionConfig: FunctionExtensionConfig = {};
for (const key of Object.keys(method.options)) {
const value = method.options[key];
const config = extractExtensionConfigFromOption(key, value);
/* istanbul ignore else */
if (config) {
Object.assign(extensionConfig, config);
}
}
method.extensionConfig =
extensionUtil.filterFunctionExtensionConfig(extensionConfig);
}
// NOTE: omit message definitions nested in a service definition
function convertServiceOptions(service: ServiceDefinition) {
if (service.options && Object.keys(service.options).length > 0) {
const extensionConfig: ServiceExtensionConfig = {};
for (const key of Object.keys(service.options)) {
const value = service.options[key];
const config = extractExtensionConfigFromOption(key, value);
/* istanbul ignore else */
if (config) {
Object.assign(extensionConfig, config);
}
}
service.extensionConfig =
extensionUtil.filterServiceExtensionConfig(extensionConfig);
}
if (Object.keys(service.methods).length > 0) {
Object.keys(service.methods).forEach(serviceName => {
convertMethodOptions(service.methods[serviceName]);
});
}
}
function convertNamespace(namespaceBase: NamespaceBase) {
const { nested } = namespaceBase;
/* istanbul ignore next */
if (!(nested && Object.keys(nested).length > 0)) {
return;
}
for (const name of Object.keys(nested)) {
const nestedStatement = nested[name];
const { syntaxType } = nestedStatement;
/* istanbul ignore else */
if (syntaxType === SyntaxType.ServiceDefinition) {
convertServiceOptions(nestedStatement as ServiceDefinition);
} else if (syntaxType === SyntaxType.MessageDefinition) {
convertFieldOptions(nestedStatement as MessageDefinition);
} else if (syntaxType === SyntaxType.NamespaceDefinition) {
convertNamespace(nestedStatement as NamespaceDefinition);
}
}
}
export function parse(source: string) {
let content: string;
let filePath = 'source';
if (/\.proto$/.test(source)) {
filePath = path.resolve(process.cwd(), source);
if (!fs.existsSync(filePath)) {
const message = `no such file: ${filePath}`;
logAndThrowError(message);
}
content = fs.readFileSync(filePath, 'utf8');
} else {
content = source;
}
const document: ProtoDocument | ProtoError = protoParse(content);
if ((document as ProtoError).syntaxType === SyntaxType.ProtoError) {
const { line, message } = document as ProtoError;
const fullMessage = `${message}(${filePath}:${line}:0)`;
logAndThrowError(fullMessage);
}
convertNamespace((document as ProtoDocument).root);
return document as ProtoDocument;
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type FieldExtensionConfig,
type ServiceExtensionConfig,
type FunctionExtensionConfig,
} from '../common/extension_type';
export * from '../common/extension_type';
export type KeywordType =
| 'double'
| 'float'
| 'int32'
| 'int64'
| 'uint32'
| 'uint64'
| 'sint32'
| 'sint64'
| 'fixed32'
| 'fixed64'
| 'sfixed32'
| 'sfixed64'
| 'bool'
| 'string'
| 'bytes';
export type FieldRule = 'repeated' | 'required';
export interface BaseType {
syntaxType: SyntaxType.BaseType;
value: KeywordType;
}
export interface Identifier {
syntaxType: SyntaxType.Identifier;
value: string;
resolvedValue?: string;
}
export type FieldType = BaseType | Identifier;
export interface ReflectionObject {
name: string;
fullName?: string;
options?: Record<string, string>;
comment?: string;
}
export interface FieldDefinition extends ReflectionObject {
syntaxType: SyntaxType.FieldDefinition;
id: number;
type: FieldType;
rule?: FieldRule;
optional: boolean;
required: boolean;
repeated: boolean;
map: boolean;
extend?: string;
keyType?: FieldType;
extensionConfig?: FieldExtensionConfig;
}
export interface OneofDefinition extends ReflectionObject {
syntaxType: SyntaxType.OneofDefinition;
oneof: string[];
}
export interface MethodDefinition extends ReflectionObject {
syntaxType: SyntaxType.MethodDefinition;
requestType: FieldType;
responseType: FieldType;
extensionConfig?: FunctionExtensionConfig;
}
export interface NamespaceBase extends ReflectionObject {
syntaxType: SyntaxType;
nested?: Record<string, NamespaceBase>;
}
export interface NamespaceDefinition extends NamespaceBase {
syntaxType: SyntaxType.NamespaceDefinition;
}
export interface MessageDefinition extends NamespaceBase {
syntaxType: SyntaxType.MessageDefinition;
fields: Record<string, FieldDefinition>;
oneofs: Record<string, OneofDefinition>;
extensions?: string[];
reserved?: number[] | string;
}
export interface EnumDefinition extends NamespaceBase {
syntaxType: SyntaxType.EnumDefinition;
values: Record<string, number>;
reserved?: number[] | string;
}
export interface ServiceDefinition extends NamespaceBase {
syntaxType: SyntaxType.ServiceDefinition;
methods: Record<string, MethodDefinition>;
extensionConfig?: ServiceExtensionConfig;
}
export interface ProtoRoot extends NamespaceBase {
syntaxType: SyntaxType.ProtoRoot;
}
export interface ProtoDocument {
syntaxType: SyntaxType.ProtoDocument;
imports?: string[];
weakImports?: string[];
package?: string;
syntax: 'proto2' | 'proto3';
root: ProtoRoot;
}
export interface ProtoError {
syntaxType: SyntaxType.ProtoError;
line: number;
message: string;
error: Error;
}
export enum SyntaxType {
BaseType = 'BaseType',
Identifier = 'Identifier',
OneofDefinition = 'OneofDefinition',
FieldDefinition = 'FieldDefinition',
MethodDefinition = 'MethodDefinition',
NamespaceDefinition = 'NamespaceDefinition',
MessageDefinition = 'MessageDefinition',
EnumDefinition = 'EnumDefinition',
ServiceDefinition = 'ServiceDefinition',
ProtoRoot = 'ProtoRoot',
ProtoDocument = 'ProtoDocument',
ProtoError = 'ProtoError',
}