11import {
22 BaseType ,
33 BaseSchema ,
4+ Branch ,
45 ErrorHook ,
6+ LogicalType ,
57 NamedSchema ,
8+ Schema ,
69 Type ,
10+ TypeCloneOptions ,
11+ TypeIsValidOptions ,
12+ TypeSchemaOptions ,
713 PrimitiveTypeName ,
814 primitiveTypeNames ,
915} from '../interfaces.js' ;
@@ -19,13 +25,37 @@ import {
1925// Encoding tap (shared for performance).
2026const TAP = Tap . withCapacity ( 1024 ) ;
2127
28+ export function resizeDefaultBuffer ( size : number ) : void {
29+ TAP . reinitialize ( size ) ;
30+ }
31+
2232// Currently active logical type, used for name redirection.
2333let activeLogicalType : Type | null = null ;
2434
2535// Underlying types of logical types currently being instantiated. This is used
2636// to be able to reference names (i.e. for branches) during instantiation.
2737const activeUnderlyingTypes : [ Type , RealType ] [ ] = [ ] ;
2838
39+ export type TypeRegistry = Map < string , Type > ;
40+
41+ export function isType < N extends string > (
42+ arg : unknown ,
43+ ...prefixes : N [ ]
44+ ) : arg is Type & { readonly typeName : `${N } ${string } `} {
45+ if ( ! arg || ! ( arg instanceof RealType ) ) {
46+ // Not fool-proof, but most likely good enough.
47+ return false ;
48+ }
49+ return prefixes . some ( ( p ) => arg . typeName . startsWith ( p ) ) ;
50+ }
51+
52+ export interface TypeContext {
53+ readonly factory : ( s : Schema , ctx : TypeContext ) => Type ;
54+ readonly registry : TypeRegistry ;
55+ readonly namespace ?: string ;
56+ readonly depth : number ; // TODO: Use.
57+ }
58+
2959/**
3060 * "Abstract" base Avro type.
3161 *
@@ -43,7 +73,8 @@ export abstract class RealType<V = any> implements BaseType<V> {
4373 readonly name : string | undefined ;
4474 readonly aliases : string [ ] | undefined ;
4575 readonly doc : string | undefined ;
46- protected constructor ( schema : BaseSchema | NamedSchema , opts ?: TypeOptions ) {
76+ protected branchConstructor : BranchConstructor | undefined ;
77+ protected constructor ( schema : BaseSchema | NamedSchema , ctx : TypeContext ) {
4778 let type ;
4879 if ( activeLogicalType ) {
4980 type = activeLogicalType ;
@@ -61,7 +92,7 @@ export abstract class RealType<V = any> implements BaseType<V> {
6192 let name = schema . name ;
6293 const namespace =
6394 schema . namespace === undefined
64- ? opts && opts . namespace
95+ ? ctx . namespace
6596 : schema . namespace ;
6697 if ( name !== undefined ) {
6798 // This isn't an anonymous type.
@@ -70,37 +101,33 @@ export abstract class RealType<V = any> implements BaseType<V> {
70101 // Avro doesn't allow redefining primitive names.
71102 throw new Error ( `cannot rename primitive type: ${ j ( name ) } ` ) ;
72103 }
73- const registry = opts && opts . registry ;
104+ const registry = ctx . registry ;
74105 if ( registry ) {
75- if ( registry [ name ] !== undefined ) {
106+ if ( registry . has ( name ) ) {
76107 throw new Error ( `duplicate type name: ${ name } ` ) ;
77108 }
78- registry [ name ] = type ;
109+ registry . set ( name , type as any ) ;
79110 }
80- } else if ( opts && opts . noAnonymousTypes ) {
81- throw new Error ( `missing name property in schema: ${ j ( schema ) } ` ) ;
82111 }
83112 this . name = name ;
84113 this . aliases = schema . aliases
85- ? schema . aliases . map ( ( s : string ) => {
86- return maybeQualify ( s , namespace ) ;
87- } )
114+ ? schema . aliases . map ( ( s : string ) => maybeQualify ( s , namespace ) )
88115 : [ ] ;
89116 }
90117 }
91118
92- get branchName ( ) : string {
93- const type = isType ( this , 'logical' ) ? this . underlyingType : this ;
94- if ( type . name ) {
95- return type . name ;
119+ get branchName ( ) : string | undefined {
120+ let t : Type = this as any ;
121+ if ( isType ( t , 'logical' ) ) {
122+ t = t . underlyingType ;
96123 }
97- if ( isType ( type , 'abstract' ) ) {
98- return type . _concreteTypeName ;
124+ if ( t . name ) {
125+ return t . name ;
99126 }
100- return isType ( type , 'union' ) ? undefined : type . typeName ;
127+ return isType ( t , 'union' ) ? undefined : t . typeName ;
101128 }
102129
103- clone ( val : V , opts ?: CloneOptions ) : any {
130+ clone ( val : V , opts ?: TypeCloneOptions ) : any {
104131 if ( opts ) {
105132 opts = {
106133 coerce : ! ! opts . coerceBuffers | 0 , // Coerce JSON to Buffer.
@@ -113,10 +140,10 @@ export abstract class RealType<V = any> implements BaseType<V> {
113140 }
114141 // If no modifications are required, we can get by with a serialization
115142 // roundtrip (generally much faster than a standard deep copy).
116- return this . fromBuffer ( this . toBuffer ( val ) ) ;
143+ return this . binaryDecode ( this . binaryEncode ( val ) ) ;
117144 }
118145
119- compareBuffers ( buf1 : Uint8Array , buf2 : Uint8Array ) : number {
146+ binaryCompare ( buf1 : Uint8Array , buf2 : Uint8Array ) : - 1 | 0 | 1 {
120147 return this . _match ( Tap . fromBuffer ( buf1 ) , Tap . fromBuffer ( buf2 ) ) ;
121148 }
122149
@@ -175,7 +202,7 @@ export abstract class RealType<V = any> implements BaseType<V> {
175202 return Object . freeze ( resolver ) ;
176203 }
177204
178- decode ( buf : Uint8Array , pos ?: number , resolver ?: TypeResolver ) : V {
205+ binaryDecodeAt ( buf : Uint8Array , pos ?: number , resolver ?: TypeResolver ) : V {
179206 const tap = Tap . fromBuffer ( buf , pos ) ;
180207 const val = readValue ( this , tap , resolver ) ;
181208 if ( ! tap . isValid ( ) ) {
@@ -184,7 +211,7 @@ export abstract class RealType<V = any> implements BaseType<V> {
184211 return { value : val , offset : tap . pos } ;
185212 }
186213
187- encode ( val : V , buf : Uint8Array , pos ?: number ) : number {
214+ binaryEncodeAt ( val : V , buf : Uint8Array , pos ?: number ) : number {
188215 const tap = Tap . fromBuffer ( buf , pos ) ;
189216 this . _write ( tap , val ) ;
190217 if ( ! tap . isValid ( ) ) {
@@ -238,9 +265,9 @@ export abstract class RealType<V = any> implements BaseType<V> {
238265 return `<${ className } ${ j ( obj ) } >` ;
239266 }
240267
241- isValid ( val : V , opts ?: IsValidOptions ) : boolean {
268+ isValid ( arg : unknown , opts ?: TypeIsValidOptions ) : boolean {
242269 // We only have a single flag for now, so no need to complicate things.
243- const flags = ( opts && opts . noUndeclaredFields ) | 0 ;
270+ const flags = ( opts && opts . allowUndeclaredFields ) | 0 ;
244271 const errorHook = opts && opts . errorHook ;
245272 let hook , path ;
246273 if ( errorHook ) {
@@ -249,10 +276,10 @@ export abstract class RealType<V = any> implements BaseType<V> {
249276 errorHook . call ( this , path . slice ( ) , any , type , val ) ;
250277 } ;
251278 }
252- return this . _check ( val , flags , hook , path ) ;
279+ return this . _check ( arg , flags , hook , path ) ;
253280 }
254281
255- schema ( opts ?: SchemaOptions ) : Schema {
282+ schema < E = SchemaExtensions > ( opts ?: TypeSchemaOptions ) : Schema < E , never > {
256283 // Copy the options to avoid mutating the original options object when we
257284 // add the registry of dereferenced types.
258285 return this . _attrs (
@@ -264,7 +291,7 @@ export abstract class RealType<V = any> implements BaseType<V> {
264291 ) ;
265292 }
266293
267- toBuffer ( val : V ) : Uint8Array {
294+ binaryEncode ( val : V ) : Uint8Array {
268295 TAP . pos = 0 ;
269296 this . _write ( TAP , val ) ;
270297 if ( TAP . isValid ( ) ) {
@@ -275,17 +302,8 @@ export abstract class RealType<V = any> implements BaseType<V> {
275302 return buf ;
276303 }
277304
278- toJSON ( ) : unknown {
279- // Convenience to allow using `JSON.stringify(type)` to get a type's schema.
280- return this . schema ( { exportAttrs : true } ) ;
281- }
282-
283- toString ( val ?: any ) : string {
284- if ( val === undefined ) {
285- // Consistent behavior with standard `toString` expectations.
286- return JSON . stringify ( this . schema ( { noDeref : true } ) ) ;
287- }
288- return JSON . stringify ( this . _copy ( val , { coerce : 3 } ) ) ;
305+ jsonEncode ( val ?: V ) : unknown {
306+ return this . _copy ( val , { coerce : 3 } ) ;
289307 }
290308
291309 wrap ( val : any ) : any {
@@ -331,22 +349,6 @@ export abstract class RealType<V = any> implements BaseType<V> {
331349 return schema ;
332350 }
333351
334- _createBranchConstructor ( ) {
335- const name = this . branchName ;
336- if ( name === 'null' ) {
337- return null ;
338- }
339- const attr = ~ name . indexOf ( '.' ) ? "this['" + name + "']" : 'this.' + name ;
340- const body = 'return function Branch$(val) { ' + attr + ' = val; };' ;
341-
342- const Branch = new Function ( body ) ( ) ;
343- Branch . type = this ;
344-
345- Branch . prototype . unwrap = new Function ( 'return ' + attr + ';' ) ;
346- Branch . prototype . unwrapped = Branch . prototype . unwrap ; // Deprecated.
347- return Branch ;
348- }
349-
350352 _peek ( tap : Tap ) : any {
351353 const pos = tap . pos ;
352354 const val = this . _read ( tap ) ;
@@ -357,7 +359,7 @@ export abstract class RealType<V = any> implements BaseType<V> {
357359 protected abstract compare ( obj1 : unknown , obj2 : unknown ) : - 1 | 0 | 1 ;
358360
359361 protected abstract _check (
360- args : unknown ,
362+ arg : unknown ,
361363 flags : any ,
362364 hook : ErrorHook ,
363365 path : string [ ]
@@ -367,19 +369,19 @@ export abstract class RealType<V = any> implements BaseType<V> {
367369
368370 protected abstract _deref ( ) : any ;
369371
370- protected abstract _match ( tap1 : Tap , tap2 : Tap ) : number ;
372+ protected abstract _match ( tap1 : Tap , tap2 : Tap ) : - 1 | 0 | 1 ;
371373
372374 abstract _read ( tap : Tap ) : V ;
373375
374376 protected abstract _skip ( tap : Tap ) : void ;
375377
376378 protected abstract _update ( resolver : TypeResolver ) : void ;
377379
378- protected abstract _write ( tap : Tap ) : void ;
380+ protected abstract _write ( tap : Tap , val : V ) : void ;
379381}
380382
381383/** Derived type abstract class. */
382- abstract class RealLogicalType extends RealType {
384+ export abstract class RealLogicalType extends RealType implements LogicalType {
383385 private _logicalTypeName : string ;
384386 constructor ( schema : Schema , opts ?: TypeOptions ) {
385387 super ( schema , opts ) ;
@@ -511,10 +513,6 @@ abstract class RealLogicalType extends RealType {
511513 protected abstract _resolve ( ) ;
512514}
513515
514- function __reset ( size : number ) : void {
515- TAP . reinitialize ( size ) ;
516- }
517-
518516/** TypeResolver to read a writer's schema as a new schema. */
519517class TypeResolver {
520518 _read : ( ( tap : Tap , lazy : boolean ) => any ) | undefined ;
@@ -643,13 +641,19 @@ export function anonymousName(): string {
643641 return 'Anonymous' ; // TODO: May unique.
644642}
645643
646- export function isType < N extends string > (
647- arg : unknown ,
648- ... prefixes : N [ ]
649- ) : arg is Type & { readonly typeName : `${ N } ${ string } ` } {
650- if ( ! arg || ! ( arg instanceof RealType ) ) {
651- // Not fool-proof, but most likely good enough.
652- return false ;
644+ type BranchConstructor = ( v : unknown ) => Branch ;
645+
646+ export function createBranchConstructor ( t : RealType ) : BranchConstructor | null {
647+ const name = t . branchName ;
648+ assert ( name , 'missing name' ) ;
649+ if ( name === 'null' ) {
650+ return null ;
653651 }
654- return prefixes . some ( ( p ) => arg . typeName . startsWith ( p ) ) ;
652+ const attr = name . includes ( '.' ) ? "this['" + name + "']" : 'this.' + name ;
653+ const body = 'return function Branch$(val) { ' + attr + ' = val; };' ;
654+
655+ const Branch = new Function ( body ) ( ) ;
656+ Branch . prototype . wrappedType = t ;
657+ Branch . prototype . unwrap = new Function ( 'return ' + attr + ';' ) ;
658+ return Branch ;
655659}
0 commit comments