protons 7.7.0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/bin/protons.js +1 -1
- package/dist/src/fields/array-field.d.ts +18 -0
- package/dist/src/fields/array-field.d.ts.map +1 -0
- package/dist/src/fields/array-field.js +83 -0
- package/dist/src/fields/array-field.js.map +1 -0
- package/dist/src/fields/enum-field.d.ts +9 -0
- package/dist/src/fields/enum-field.d.ts.map +1 -0
- package/dist/src/fields/enum-field.js +21 -0
- package/dist/src/fields/enum-field.js.map +1 -0
- package/dist/src/fields/field.d.ts +45 -0
- package/dist/src/fields/field.d.ts.map +1 -0
- package/dist/src/fields/field.js +147 -0
- package/dist/src/fields/field.js.map +1 -0
- package/dist/src/fields/map-field.d.ts +22 -0
- package/dist/src/fields/map-field.d.ts.map +1 -0
- package/dist/src/fields/map-field.js +83 -0
- package/dist/src/fields/map-field.js.map +1 -0
- package/dist/src/fields/message-field.d.ts +9 -0
- package/dist/src/fields/message-field.d.ts.map +1 -0
- package/dist/src/fields/message-field.js +23 -0
- package/dist/src/fields/message-field.js.map +1 -0
- package/dist/src/index.d.ts +190 -16
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -963
- package/dist/src/index.js.map +1 -1
- package/dist/src/types/enum.d.ts +21 -0
- package/dist/src/types/enum.d.ts.map +1 -0
- package/dist/src/types/enum.js +87 -0
- package/dist/src/types/enum.js.map +1 -0
- package/dist/src/types/index.d.ts +20 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +2 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/message.d.ts +49 -0
- package/dist/src/types/message.d.ts.map +1 -0
- package/dist/src/types/message.js +478 -0
- package/dist/src/types/message.js.map +1 -0
- package/dist/src/types/module.d.ts +30 -0
- package/dist/src/types/module.d.ts.map +1 -0
- package/dist/src/types/module.js +184 -0
- package/dist/src/types/module.js.map +1 -0
- package/dist/src/types/primitive.d.ts +13 -0
- package/dist/src/types/primitive.d.ts.map +1 -0
- package/dist/src/types/primitive.js +174 -0
- package/dist/src/types/primitive.js.map +1 -0
- package/dist/typedoc-urls.json +2 -4
- package/package.json +103 -16
- package/src/fields/array-field.ts +109 -0
- package/src/fields/enum-field.ts +30 -0
- package/src/fields/field.ts +201 -0
- package/src/fields/map-field.ts +107 -0
- package/src/fields/message-field.ts +29 -0
- package/src/index.ts +6 -1202
- package/src/types/enum.ts +112 -0
- package/src/types/index.ts +21 -0
- package/src/types/message.ts +558 -0
- package/src/types/module.ts +234 -0
- package/src/types/primitive.ts +215 -0
- package/LICENSE +0 -4
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { ParseError } from 'protons-runtime'
|
|
2
|
+
import type { Parent, Type } from './index.ts'
|
|
3
|
+
import type { Field } from '../fields/field.ts'
|
|
4
|
+
|
|
5
|
+
export interface EnumDef {
|
|
6
|
+
values: Record<string, number>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isEnumDef (obj?: any): obj is EnumDef {
|
|
10
|
+
return obj?.values != null
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class Enum implements Type {
|
|
14
|
+
public pbType: string
|
|
15
|
+
public jsType: string
|
|
16
|
+
public values: Map<string, number>
|
|
17
|
+
public lowestValue: number
|
|
18
|
+
public lowestValueName: string
|
|
19
|
+
|
|
20
|
+
constructor (pbType: string, jsType: string, def: EnumDef) {
|
|
21
|
+
this.pbType = pbType
|
|
22
|
+
this.jsType = jsType
|
|
23
|
+
this.values = new Map(Object.entries(def.values))
|
|
24
|
+
|
|
25
|
+
// select lowest-value enum - should be 0 but it's not guaranteed
|
|
26
|
+
const lowestValue = [...this.values.entries()]
|
|
27
|
+
.sort((a, b) => {
|
|
28
|
+
if (a[1] < b[1]) {
|
|
29
|
+
return 1
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (a[1] > b[1]) {
|
|
33
|
+
return -1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return 0
|
|
37
|
+
})
|
|
38
|
+
.pop()
|
|
39
|
+
|
|
40
|
+
if (lowestValue == null) {
|
|
41
|
+
throw new Error(`Could not find lowest enum value for ${pbType}`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.lowestValue = lowestValue[1]
|
|
45
|
+
this.lowestValueName = lowestValue[0]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
init (): void {
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getDecoder (field: Field): string {
|
|
53
|
+
return `${this.jsType}.codec().decode(reader)`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getStreamingDecoder (field: Field): string {
|
|
57
|
+
return `${this.jsType}.codec().stream(reader)`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
getEncoder (field: Field, accessor: string): string {
|
|
61
|
+
return `${this.jsType}.codec().encode(${accessor}, w)`
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getValueTest (field: Field, accessor: string): string {
|
|
65
|
+
if (field.proto2Required) {
|
|
66
|
+
return 'true'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const valueTest = `${accessor} != null`
|
|
70
|
+
|
|
71
|
+
// singular enums default to 0, but enums can be defined without a 0
|
|
72
|
+
// value which is against the proto3 spec but is tolerated
|
|
73
|
+
if (field.optional || field.proto2Required) {
|
|
74
|
+
return valueTest
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return `${valueTest} && __${field.type}Values[${accessor}] !== 0`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public compile (parent: Parent): string {
|
|
81
|
+
// import required modules
|
|
82
|
+
parent.addImport('protons-runtime', 'enumeration')
|
|
83
|
+
|
|
84
|
+
// check that the enum def values start from 0
|
|
85
|
+
if ([...this.values.values()][0] !== 0) {
|
|
86
|
+
const message = `enum ${this.pbType} does not contain a value that maps to zero as the first element, this is required in proto3 - see https://protobuf.dev/programming-guides/proto3/#enum`
|
|
87
|
+
|
|
88
|
+
if (parent.flags.strict === true) {
|
|
89
|
+
throw new ParseError(message)
|
|
90
|
+
} else {
|
|
91
|
+
// eslint-disable-next-line no-console
|
|
92
|
+
console.info(`[WARN] ${message}`)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return `
|
|
97
|
+
export enum ${this.pbType} {
|
|
98
|
+
${[...this.values.keys()].map(key => `${key} = '${key}'`).join(',\n ')}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
enum __${this.pbType}Values {
|
|
102
|
+
${[...this.values.entries()].map(([key, value]) => `${key} = ${value}`).join(',\n ')}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export namespace ${this.pbType} {
|
|
106
|
+
export const codec = (): Codec<${this.pbType}> => {
|
|
107
|
+
return enumeration<${this.pbType}>(__${this.pbType}Values)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
`.trimStart()
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Field } from '../fields/field.ts'
|
|
2
|
+
import type { Flags } from '../index.ts'
|
|
3
|
+
import type { Module } from './module.ts'
|
|
4
|
+
|
|
5
|
+
export interface Type {
|
|
6
|
+
jsType: string
|
|
7
|
+
pbType: string
|
|
8
|
+
init(module: Module): void
|
|
9
|
+
getDecoder(field: Field, indent?: string): string
|
|
10
|
+
getStreamingDecoder(field: Field, prefix: string, indent?: string): string
|
|
11
|
+
getEncoder(field: Field, accessor: string): string
|
|
12
|
+
getValueTest(field: Field, accessor: string): string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Parent {
|
|
16
|
+
flags: Flags
|
|
17
|
+
findType (type: string, jsOverride?: 'string' | 'number'): Type
|
|
18
|
+
addImport (module: string, symbol: string, alias?: string): void
|
|
19
|
+
addTypeImport (module: string, symbol: string, alias?: string): void
|
|
20
|
+
addEslintIgnore (rule: string): void
|
|
21
|
+
}
|
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
import { ArrayField, isArrayFieldDef } from '../fields/array-field.ts'
|
|
2
|
+
import { EnumField } from '../fields/enum-field.ts'
|
|
3
|
+
import { Field } from '../fields/field.ts'
|
|
4
|
+
import { isMapFieldDef, MapField } from '../fields/map-field.ts'
|
|
5
|
+
import { MessageField } from '../fields/message-field.ts'
|
|
6
|
+
import { Enum } from './enum.ts'
|
|
7
|
+
import { Primitive } from './primitive.ts'
|
|
8
|
+
import type { EnumDef } from './enum.ts'
|
|
9
|
+
import type { Parent, Type } from './index.ts'
|
|
10
|
+
import type { FieldDef } from '../fields/field.ts'
|
|
11
|
+
import type { Flags } from '../index.ts'
|
|
12
|
+
|
|
13
|
+
interface StreamEvent {
|
|
14
|
+
name: string
|
|
15
|
+
fields: string[]
|
|
16
|
+
type: 'field'
|
|
17
|
+
| 'collection-primitive-member'
|
|
18
|
+
| 'collection-message-member-field'
|
|
19
|
+
| 'sub-message-field'
|
|
20
|
+
| 'sub-message-collection-primitive-member'
|
|
21
|
+
| 'sub-message-collection-message-member-field'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function camelize (input: string): string {
|
|
25
|
+
return `${input.substring(0, 1).toUpperCase()}${input.substring(1)}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface MessageDef {
|
|
29
|
+
fields?: Record<string, FieldDef>
|
|
30
|
+
oneofs?: Record<string, { oneof: string[] }>
|
|
31
|
+
nested?: Record<string, MessageDef | EnumDef>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isMessageDef (obj?: any): obj is MessageDef {
|
|
35
|
+
return obj?.fields != null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class Message implements Type {
|
|
39
|
+
public pbType: string
|
|
40
|
+
public jsType: string
|
|
41
|
+
public fields: Field[]
|
|
42
|
+
public oneOfs: string[][]
|
|
43
|
+
public nested: Record<string, Message | Enum>
|
|
44
|
+
private def: MessageDef
|
|
45
|
+
private parent: Parent
|
|
46
|
+
|
|
47
|
+
constructor (pbType: string, jsType: string, def: MessageDef, parent: Parent) {
|
|
48
|
+
this.pbType = pbType
|
|
49
|
+
this.jsType = jsType
|
|
50
|
+
this.oneOfs = []
|
|
51
|
+
this.nested = {}
|
|
52
|
+
this.fields = []
|
|
53
|
+
this.def = def
|
|
54
|
+
this.parent = parent
|
|
55
|
+
|
|
56
|
+
def.fields ??= {}
|
|
57
|
+
def.nested ??= {}
|
|
58
|
+
def.oneofs ??= {}
|
|
59
|
+
|
|
60
|
+
// create extra message types for map type backwards compatibility
|
|
61
|
+
// https://developers.google.com/protocol-buffers/docs/proto3#backwards
|
|
62
|
+
for (const [fieldName, fieldDef] of Object.entries<any>(def.fields)) {
|
|
63
|
+
if (fieldDef.keyType == null) {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const mapEntryType = `${this.pbType}$${fieldName}Entry`
|
|
68
|
+
|
|
69
|
+
def.nested[mapEntryType] = {
|
|
70
|
+
fields: {
|
|
71
|
+
key: {
|
|
72
|
+
type: fieldDef.keyType,
|
|
73
|
+
id: 1
|
|
74
|
+
},
|
|
75
|
+
value: {
|
|
76
|
+
type: fieldDef.type,
|
|
77
|
+
id: 2
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
fieldDef.valueType = fieldDef.type
|
|
83
|
+
fieldDef.type = mapEntryType
|
|
84
|
+
fieldDef.rule = 'repeated'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
Object.entries(def.nested ?? {}).forEach(([name, def]) => {
|
|
88
|
+
const fullName = `${this.jsType}.${name}`
|
|
89
|
+
|
|
90
|
+
if (isMessageDef(def)) {
|
|
91
|
+
this.nested[name] = new Message(name, fullName, def, this)
|
|
92
|
+
} else {
|
|
93
|
+
this.nested[name] = new Enum(name, fullName, def)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
for (const [, { oneof: fields }] of Object.entries(def.oneofs)) {
|
|
98
|
+
for (const field of fields) {
|
|
99
|
+
if (def.fields[field] == null) {
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
def.fields[field].options ??= {}
|
|
104
|
+
def.fields[field].options.proto3_optional = true
|
|
105
|
+
def.fields[field].oneof = fields
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// reverse order of oneofs as spec says last field overwrites all others
|
|
110
|
+
this.oneOfs = Object.values(def.oneofs).map(({ oneof }) => oneof.reverse())
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
init (): void {
|
|
114
|
+
for (const [name, fieldDef] of Object.entries(this.def.fields ?? {})) {
|
|
115
|
+
if (isMapFieldDef(fieldDef)) {
|
|
116
|
+
this.fields.push(new MapField(name, fieldDef, this))
|
|
117
|
+
} else if (isArrayFieldDef(fieldDef)) {
|
|
118
|
+
this.fields.push(new ArrayField(name, fieldDef, this))
|
|
119
|
+
} else {
|
|
120
|
+
const type = this.findType(fieldDef.type)
|
|
121
|
+
|
|
122
|
+
if (type instanceof Enum) {
|
|
123
|
+
this.fields.push(new EnumField(name, fieldDef, this))
|
|
124
|
+
} else if (type instanceof Primitive) {
|
|
125
|
+
this.fields.push(new Field(name, fieldDef, this))
|
|
126
|
+
} else {
|
|
127
|
+
this.fields.push(new MessageField(name, fieldDef, this))
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Object.values(this.nested).forEach(type => type.init())
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
findType (jsType: string, override?: 'string' | 'number'): Type {
|
|
136
|
+
const type = this.nested[override ?? jsType]
|
|
137
|
+
|
|
138
|
+
if (type == null) {
|
|
139
|
+
return this.parent.findType(jsType, override)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return type
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
addImport (module: string, symbol: string, alias?: string): void {
|
|
146
|
+
this.parent.addImport(module, symbol, alias)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
addTypeImport (module: string, symbol: string, alias?: string): void {
|
|
150
|
+
this.parent.addTypeImport(module, symbol, alias)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
addEslintIgnore (rule: string): void {
|
|
154
|
+
this.parent.addEslintIgnore(rule)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get flags (): Flags {
|
|
158
|
+
return this.parent.flags
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
getDecoder (field: Field, indent = ''): string {
|
|
162
|
+
let opts = ''
|
|
163
|
+
|
|
164
|
+
if (field instanceof MessageField) {
|
|
165
|
+
opts = `, {
|
|
166
|
+
${indent} limits: opts.limits?.${field.name}
|
|
167
|
+
${indent} }`
|
|
168
|
+
} else if (field instanceof ArrayField) {
|
|
169
|
+
opts = `, {
|
|
170
|
+
${indent} limits: opts.limits?.${field.name}$
|
|
171
|
+
${indent} }`
|
|
172
|
+
} else if (field instanceof MapField) {
|
|
173
|
+
opts = `, {
|
|
174
|
+
${indent} limits: {
|
|
175
|
+
${indent} value: opts.limits?.${field.name}$value
|
|
176
|
+
}
|
|
177
|
+
${indent} }`
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return `${this.jsType}.codec().decode(reader, reader.uint32()${opts})`
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
getStreamingDecoder (field: Field, prefix: string, indent = ''): string {
|
|
184
|
+
let opts = ''
|
|
185
|
+
|
|
186
|
+
if (field instanceof MessageField) {
|
|
187
|
+
opts = `, {
|
|
188
|
+
${indent} limits: opts.limits?.${field.name}
|
|
189
|
+
${indent} }`
|
|
190
|
+
} else if (field instanceof ArrayField) {
|
|
191
|
+
opts = `, {
|
|
192
|
+
${indent} limits: opts.limits?.${field.name}$
|
|
193
|
+
${indent} }`
|
|
194
|
+
|
|
195
|
+
return `for (const evt of ${this.jsType}.codec().stream(reader, reader.uint32(), ${prefix}${opts})) {
|
|
196
|
+
yield {
|
|
197
|
+
...evt,
|
|
198
|
+
index: obj.${field.name}
|
|
199
|
+
}
|
|
200
|
+
}`
|
|
201
|
+
} else if (field instanceof MapField) {
|
|
202
|
+
opts = `, {
|
|
203
|
+
${indent} limits: {
|
|
204
|
+
${indent} value: opts.limits?.${field.name}$value
|
|
205
|
+
}
|
|
206
|
+
${indent} }`
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return `yield * ${this.jsType}.codec().stream(reader, reader.uint32(), ${prefix}${opts})`
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
getEncoder (field: Field, accessor: string): string {
|
|
213
|
+
if (field instanceof ArrayField) {
|
|
214
|
+
// message fields are only written if they have values. But if a message
|
|
215
|
+
// is part of a repeated field, and consists of only default values it
|
|
216
|
+
// won't be written, so write a zero-length buffer if that's the case
|
|
217
|
+
// writeField = (): string => `w.uint32(${id})
|
|
218
|
+
// ${type.jsType}.codec().encode(${valueVar}, w)`
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return `${this.jsType}.codec().encode(${accessor}, w)`
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
getValueTest (field: Field): string {
|
|
225
|
+
return `obj.${field.name} != null`
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
public compile (parent: Parent, indent: string = ''): string {
|
|
229
|
+
// import required modules
|
|
230
|
+
parent.addImport('protons-runtime', 'encodeMessage')
|
|
231
|
+
parent.addImport('protons-runtime', 'decodeMessage')
|
|
232
|
+
parent.addImport('protons-runtime', 'streamMessage')
|
|
233
|
+
parent.addImport('protons-runtime', 'message')
|
|
234
|
+
parent.addTypeImport('protons-runtime', 'Codec')
|
|
235
|
+
parent.addTypeImport('protons-runtime', 'DecodeOptions')
|
|
236
|
+
parent.addTypeImport('uint8arraylist', 'Uint8ArrayList')
|
|
237
|
+
|
|
238
|
+
let nested = ''
|
|
239
|
+
|
|
240
|
+
if (this.nested != null && [...Object.values(this.nested)].length > 0) {
|
|
241
|
+
nested += Object.values(this.nested)
|
|
242
|
+
.map(def => def.compile(this, `${indent} `).trim())
|
|
243
|
+
.join('\n\n')
|
|
244
|
+
nested = nested.split('\n').join('\n ')
|
|
245
|
+
nested = `
|
|
246
|
+
${nested}
|
|
247
|
+
`
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const interfaceFields = this.fields.map(field => field.getInterfaceField(this))
|
|
251
|
+
.join('\n ')
|
|
252
|
+
.trim()
|
|
253
|
+
|
|
254
|
+
let interfaceDef = ''
|
|
255
|
+
let interfaceCodecDef = ''
|
|
256
|
+
|
|
257
|
+
if (interfaceFields === '') {
|
|
258
|
+
interfaceDef = `
|
|
259
|
+
export interface ${this.pbType} {}`
|
|
260
|
+
} else {
|
|
261
|
+
interfaceDef = `
|
|
262
|
+
export interface ${this.pbType} {
|
|
263
|
+
${interfaceFields}
|
|
264
|
+
}`
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const enforceOneOfEncoding = this.createOneOfEncoding()
|
|
268
|
+
const enforceOneOfDecoding = this.createOneOfDecoding()
|
|
269
|
+
const streamEvents = this.getStreamEvents()
|
|
270
|
+
|
|
271
|
+
const encodeFields = this.fields.map(field => field.getEncoder(this))
|
|
272
|
+
const decodeFields = this.fields.map(field => field.getDecoder(this))
|
|
273
|
+
const streamFields = this.fields.map(field => field.getStreamingDecoder(this))
|
|
274
|
+
|
|
275
|
+
const streamGeneratorEvents = streamEvents.map(evt => evt.name)
|
|
276
|
+
|
|
277
|
+
if (streamGeneratorEvents.length === 0) {
|
|
278
|
+
this.addEslintIgnore('require-yield')
|
|
279
|
+
streamGeneratorEvents.push('{}')
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interfaceCodecDef = `
|
|
283
|
+
let _codec: Codec<${this.pbType}>
|
|
284
|
+
|
|
285
|
+
export const codec = (): Codec<${this.pbType}> => {
|
|
286
|
+
if (_codec == null) {
|
|
287
|
+
_codec = message<${this.pbType}>((obj, w, opts = {}) => {
|
|
288
|
+
if (opts.lengthDelimited !== false) {
|
|
289
|
+
w.fork()
|
|
290
|
+
}${enforceOneOfEncoding}${this.formatFields(encodeFields)}
|
|
291
|
+
|
|
292
|
+
if (opts.lengthDelimited !== false) {
|
|
293
|
+
w.ldelim()
|
|
294
|
+
}
|
|
295
|
+
}, (reader, length, opts = {}) => {
|
|
296
|
+
const obj: any = {${this.createDefaultObject()}}
|
|
297
|
+
|
|
298
|
+
const end = length == null ? reader.len : reader.pos + length
|
|
299
|
+
|
|
300
|
+
while (reader.pos < end) {
|
|
301
|
+
const tag = reader.uint32()
|
|
302
|
+
|
|
303
|
+
switch (tag >>> 3) {${this.formatFields(decodeFields, '\n ')}
|
|
304
|
+
default: {
|
|
305
|
+
reader.skipType(tag & 7)
|
|
306
|
+
break
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
${enforceOneOfDecoding === '' ? '' : `${enforceOneOfDecoding}\n`}
|
|
311
|
+
return obj
|
|
312
|
+
}, function * (reader, length, prefix, opts = {}) {
|
|
313
|
+
${this.createLimitObject()}const end = length == null ? reader.len : reader.pos + length
|
|
314
|
+
|
|
315
|
+
while (reader.pos < end) {
|
|
316
|
+
const tag = reader.uint32()
|
|
317
|
+
|
|
318
|
+
switch (tag >>> 3) {${this.formatFields(streamFields, '\n ')}
|
|
319
|
+
default: {
|
|
320
|
+
reader.skipType(tag & 7)
|
|
321
|
+
break
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return _codec
|
|
329
|
+
}${this.formatStreamEvents(streamEvents)}
|
|
330
|
+
|
|
331
|
+
export function encode (obj: Partial<${this.pbType}>): Uint8Array {
|
|
332
|
+
return encodeMessage(obj, ${this.pbType}.codec())
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function decode (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<${this.pbType}>): ${this.pbType} {
|
|
336
|
+
return decodeMessage(buf, ${this.pbType}.codec(), opts)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export function stream (buf: Uint8Array | Uint8ArrayList, opts?: DecodeOptions<${this.pbType}>): Generator<${streamGeneratorEvents.join(' | ')}> {
|
|
340
|
+
return streamMessage(buf, ${this.pbType}.codec(), opts)
|
|
341
|
+
}`
|
|
342
|
+
|
|
343
|
+
return `
|
|
344
|
+
${interfaceDef}
|
|
345
|
+
|
|
346
|
+
export namespace ${this.pbType} {${nested}${interfaceCodecDef.trimEnd()}
|
|
347
|
+
}
|
|
348
|
+
`.trimStart()
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private createOneOfEncoding (): string {
|
|
352
|
+
let oneOfs = this.oneOfs.map(fields => {
|
|
353
|
+
if (fields.length < 2) {
|
|
354
|
+
return ''
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let oneOf = ''
|
|
358
|
+
|
|
359
|
+
for (const name of fields) {
|
|
360
|
+
oneOf += `
|
|
361
|
+
if (obj.${name} != null) {
|
|
362
|
+
${fields
|
|
363
|
+
.filter(field => field !== name)
|
|
364
|
+
.map(name => ` obj.${name} = undefined`).join('\n')}
|
|
365
|
+
}
|
|
366
|
+
`
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return oneOf.trimEnd()
|
|
370
|
+
}).join('\n').trimEnd()
|
|
371
|
+
|
|
372
|
+
if (oneOfs !== '') {
|
|
373
|
+
oneOfs = `
|
|
374
|
+
|
|
375
|
+
obj = { ...obj }
|
|
376
|
+
${oneOfs}`
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return oneOfs
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private createOneOfDecoding (): string {
|
|
383
|
+
return this.oneOfs.map(fields => {
|
|
384
|
+
if (fields.length < 2) {
|
|
385
|
+
return ''
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
let oneOf = ''
|
|
389
|
+
|
|
390
|
+
for (const name of fields) {
|
|
391
|
+
oneOf += `
|
|
392
|
+
if (obj.${name} != null) {
|
|
393
|
+
${fields
|
|
394
|
+
.filter(field => field !== name)
|
|
395
|
+
.map(name => ` delete obj.${name}`).join('\n')}
|
|
396
|
+
}
|
|
397
|
+
`
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return oneOf.trimEnd()
|
|
401
|
+
}).join('\n').trimEnd()
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
createDefaultObject (indent = ''): string {
|
|
405
|
+
const output = this.fields
|
|
406
|
+
.map((field) => field.getDefaultField(this))
|
|
407
|
+
.filter(Boolean)
|
|
408
|
+
.join(`,\n ${indent}`)
|
|
409
|
+
|
|
410
|
+
if (output !== '') {
|
|
411
|
+
return `
|
|
412
|
+
${indent}${output}
|
|
413
|
+
${indent}`
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return ''
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
createLimitObject (): string {
|
|
420
|
+
const output = this.fields.map(field => field.getLimitField())
|
|
421
|
+
.filter(Boolean)
|
|
422
|
+
.join(',\n ')
|
|
423
|
+
|
|
424
|
+
if (output !== '') {
|
|
425
|
+
return `const obj = {
|
|
426
|
+
${output}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
`
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
return ''
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
formatStreamEvents (streamEvents: StreamEvent[]): string {
|
|
436
|
+
if (streamEvents.length === 0) {
|
|
437
|
+
return ''
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return `
|
|
441
|
+
|
|
442
|
+
${streamEvents.map(evt => `export interface ${evt.name} {
|
|
443
|
+
${evt.fields.join('\n ')}
|
|
444
|
+
}`).join('\n\n ')}`
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
formatFields (fields: string[], delimiter = '\n'): string {
|
|
448
|
+
if (fields.length === 0) {
|
|
449
|
+
return ''
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return `
|
|
453
|
+
${fields.join(delimiter)}`
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
getStreamEvents (fieldPrefix = '$.'): StreamEvent[] {
|
|
457
|
+
const streamEvents: StreamEvent[] = []
|
|
458
|
+
|
|
459
|
+
const addMessageFields = (field: Field, message: Message, extraFields: string[]): void => {
|
|
460
|
+
let fieldSuffix = '.'
|
|
461
|
+
|
|
462
|
+
if (field instanceof ArrayField) {
|
|
463
|
+
fieldSuffix = '[].'
|
|
464
|
+
} else if (field instanceof MapField) {
|
|
465
|
+
fieldSuffix = '{}.'
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// include sub messages
|
|
469
|
+
streamEvents.push(...message.getStreamEvents(`${fieldPrefix}${field.name}${fieldSuffix}`).map(evt => {
|
|
470
|
+
let type = evt.type
|
|
471
|
+
|
|
472
|
+
if (evt.type === 'field') {
|
|
473
|
+
type = (field instanceof MapField || field instanceof ArrayField) ? 'collection-message-member-field' : 'sub-message-field'
|
|
474
|
+
} else if (evt.type === 'collection-primitive-member') {
|
|
475
|
+
type = 'sub-message-collection-primitive-member'
|
|
476
|
+
} else if (evt.type === 'collection-message-member-field') {
|
|
477
|
+
type = 'sub-message-collection-message-member-field'
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const fields = new Map<string, string>()
|
|
481
|
+
evt.fields.forEach(field => {
|
|
482
|
+
const [key, value] = field.split(':')
|
|
483
|
+
fields.set(key.trim(), value.trim())
|
|
484
|
+
})
|
|
485
|
+
extraFields.forEach(field => {
|
|
486
|
+
const [key, value] = field.split(':')
|
|
487
|
+
fields.set(key.trim(), value.trim())
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
...evt,
|
|
492
|
+
name: `${fieldPrefix === '$.' ? this.pbType : ''}${camelize(field.name)}${evt.name}`,
|
|
493
|
+
type,
|
|
494
|
+
fields: [...fields.entries()].map(([key, value]) => `${key}: ${value}`)
|
|
495
|
+
}
|
|
496
|
+
}))
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
this.fields.forEach(field => {
|
|
500
|
+
if (field instanceof MapField) {
|
|
501
|
+
const keyType = this.findType(field.keyType)
|
|
502
|
+
const valueType = this.findType(field.valueType)
|
|
503
|
+
|
|
504
|
+
if (valueType instanceof Primitive || valueType instanceof Enum) {
|
|
505
|
+
streamEvents.push({
|
|
506
|
+
name: `${fieldPrefix === '$.' ? this.pbType : ''}${camelize(field.name)}FieldEvent`,
|
|
507
|
+
fields: [
|
|
508
|
+
`field: '${fieldPrefix}${field.name}{}'`,
|
|
509
|
+
`key: ${keyType.jsType}`,
|
|
510
|
+
`value: ${valueType.jsType}`
|
|
511
|
+
],
|
|
512
|
+
type: 'collection-primitive-member'
|
|
513
|
+
})
|
|
514
|
+
} else if (valueType instanceof Message) {
|
|
515
|
+
addMessageFields(field, valueType, [
|
|
516
|
+
`key: ${keyType.jsType}`,
|
|
517
|
+
`value: ${valueType.jsType}`
|
|
518
|
+
])
|
|
519
|
+
}
|
|
520
|
+
} else if (field instanceof ArrayField) {
|
|
521
|
+
const type = this.findType(field.type)
|
|
522
|
+
|
|
523
|
+
if (type instanceof Primitive || type instanceof Enum) {
|
|
524
|
+
streamEvents.push({
|
|
525
|
+
name: `${fieldPrefix === '$.' ? this.pbType : ''}${camelize(field.name)}FieldEvent`,
|
|
526
|
+
fields: [
|
|
527
|
+
`field: '${fieldPrefix}${field.name}[]'`,
|
|
528
|
+
'index: number',
|
|
529
|
+
`value: ${type.jsType}`
|
|
530
|
+
],
|
|
531
|
+
type: 'collection-primitive-member'
|
|
532
|
+
})
|
|
533
|
+
} else if (type instanceof Message) {
|
|
534
|
+
addMessageFields(field, type, [
|
|
535
|
+
'index: number'
|
|
536
|
+
])
|
|
537
|
+
}
|
|
538
|
+
} else {
|
|
539
|
+
const type = this.findType(field.type)
|
|
540
|
+
|
|
541
|
+
if (type instanceof Primitive || type instanceof Enum) {
|
|
542
|
+
streamEvents.push({
|
|
543
|
+
name: `${fieldPrefix === '$.' ? this.pbType : ''}${camelize(field.name)}FieldEvent`,
|
|
544
|
+
fields: [
|
|
545
|
+
`field: '${fieldPrefix}${field.name}'`,
|
|
546
|
+
`value: ${type.jsType}`
|
|
547
|
+
],
|
|
548
|
+
type: 'field'
|
|
549
|
+
})
|
|
550
|
+
} else if (type instanceof Message) {
|
|
551
|
+
addMessageFields(field, type, [])
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
})
|
|
555
|
+
|
|
556
|
+
return streamEvents
|
|
557
|
+
}
|
|
558
|
+
}
|