Skip to content

Commit 998e85e

Browse files
committed
dependencies
1 parent 8bbd80d commit 998e85e

11 files changed

+238
-242
lines changed

src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import type {
1717
SimplifiedSchemaField,
1818
SimplifiedSchema
1919
} from './schema-analyzer';
20+
import { convertInternalToExpanded } from './schema-converters/internalToExpanded';
21+
import { convertInternalToMongodb } from './schema-converters/internalToMongoDB';
22+
import { convertInternalToStandard } from './schema-converters/internalToStandard';
2023
import * as schemaStats from './stats';
2124
import { AnyIterable, StandardJSONSchema, MongoDBJSONSchema, ExpandedJSONSchema } from './types';
2225

@@ -28,7 +31,11 @@ async function analyzeDocuments(
2831
options?: SchemaParseOptions
2932
): Promise<SchemaAccessor> {
3033
const internalSchema = (await getCompletedSchemaAnalyzer(source, options)).getResult();
31-
return new InternalSchemaBasedAccessor(internalSchema);
34+
return new InternalSchemaBasedAccessor(internalSchema, {
35+
internalToStandard: convertInternalToStandard,
36+
internalToMongoDB: convertInternalToMongodb,
37+
internalToExpanded: convertInternalToExpanded
38+
});
3239
}
3340

3441
/**

src/schema-accessor.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import { Schema as InternalSchema } from './schema-analyzer';
2-
import { InternalToExpandedConverter } from './schema-converters/internalToExpanded';
3-
import { InternalToMongoDBConverter } from './schema-converters/internalToMongoDB';
4-
import { InternalToStandardConverter } from './schema-converters/internalToStandard';
5-
import { ExpandedJSONSchema, MongoDBJSONSchema, StandardJSONSchema } from './types';
2+
import { ExpandedJSONSchema, MongoDBJSONSchema, SchemaConverterFn, StandardJSONSchema } from './types';
63

74
export interface SchemaAccessor {
85
getStandardJsonSchema: () => Promise<StandardJSONSchema>;
@@ -21,20 +18,22 @@ type Options = {
2118
* the others are converted lazily and memoized.
2219
* Conversion can be aborted.
2320
*/
21+
22+
export type InternalConverters = {
23+
internalToStandard: SchemaConverterFn<InternalSchema, StandardJSONSchema>,
24+
internalToExpanded: SchemaConverterFn<InternalSchema, ExpandedJSONSchema>,
25+
internalToMongoDB: SchemaConverterFn<InternalSchema, MongoDBJSONSchema>,
26+
}
2427
export class InternalSchemaBasedAccessor implements SchemaAccessor {
2528
private internalSchema: InternalSchema;
2629
private standardJSONSchema?: StandardJSONSchema;
2730
private mongodbJSONSchema?: MongoDBJSONSchema;
28-
private ExpandedJSONSchema?: ExpandedJSONSchema;
29-
public internalToStandardConverter: InternalToStandardConverter;
30-
public internalToExpandedConverter: InternalToExpandedConverter;
31-
public internalToMongoDBConverter: InternalToMongoDBConverter;
31+
private expandedJSONSchema?: ExpandedJSONSchema;
32+
private converters: InternalConverters;
3233

33-
constructor(internalSchema: InternalSchema) {
34+
constructor(internalSchema: InternalSchema, converters: InternalConverters) {
3435
this.internalSchema = internalSchema;
35-
this.internalToStandardConverter = new InternalToStandardConverter();
36-
this.internalToExpandedConverter = new InternalToExpandedConverter();
37-
this.internalToMongoDBConverter = new InternalToMongoDBConverter();
36+
this.converters = converters;
3837
}
3938

4039
async getInternalSchema(): Promise<InternalSchema> {
@@ -46,20 +45,20 @@ export class InternalSchemaBasedAccessor implements SchemaAccessor {
4645
* https://json-schema.org/draft/2020-12/schema
4746
*/
4847
async getStandardJsonSchema(options: Options = {}): Promise<StandardJSONSchema> {
49-
return this.standardJSONSchema ??= await this.internalToStandardConverter.convert(this.internalSchema, options);
48+
return this.standardJSONSchema ??= await this.converters.internalToStandard(this.internalSchema, options);
5049
}
5150

5251
/**
5352
* Get MongoDB's $jsonSchema
5453
*/
5554
async getMongoDBJsonSchema(options: Options = {}): Promise<MongoDBJSONSchema> {
56-
return this.mongodbJSONSchema ??= await this.internalToMongoDBConverter.convert(this.internalSchema, options);
55+
return this.mongodbJSONSchema ??= await this.converters.internalToMongoDB(this.internalSchema, options);
5756
}
5857

5958
/**
6059
* Get expanded JSON Schema - with additional properties
6160
*/
6261
async getExpandedJSONSchema(options: Options = {}): Promise<ExpandedJSONSchema> {
63-
return this.ExpandedJSONSchema ??= await this.internalToExpandedConverter.convert(this.internalSchema, options);
62+
return this.expandedJSONSchema ??= await this.converters.internalToExpanded(this.internalSchema, options);
6463
}
6564
}

src/schema-converters/internalToExpanded.test.ts

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import assert from 'assert';
22
import { RELAXED_EJSON_DEFINITIONS } from './internalToStandard';
3-
import { InternalToExpandedConverter } from './internalToExpanded';
3+
import { convertInternalToExpanded } from './internalToExpanded';
44

55
describe('internalSchemaToExpanded', async function() {
66
describe('Converts: ', async function() {
@@ -336,8 +336,7 @@ describe('internalSchemaToExpanded', async function() {
336336
}
337337
]
338338
};
339-
const converter = new InternalToExpandedConverter();
340-
const expanded = await converter.convert(internal);
339+
const expanded = await convertInternalToExpanded(internal);
341340
const expectedDefinitions: any = RELAXED_EJSON_DEFINITIONS;
342341
delete expectedDefinitions.BSONSymbol;
343342
delete expectedDefinitions.CodeWScope;
@@ -602,8 +601,7 @@ describe('internalSchemaToExpanded', async function() {
602601
}
603602
]
604603
};
605-
const converter = new InternalToExpandedConverter();
606-
const expanded = await converter.convert(internal);
604+
const expanded = await convertInternalToExpanded(internal);
607605
const expectedDefinitions = {
608606
Double: RELAXED_EJSON_DEFINITIONS.Double
609607
};
@@ -717,8 +715,7 @@ describe('internalSchemaToExpanded', async function() {
717715
}
718716
]
719717
};
720-
const converter = new InternalToExpandedConverter();
721-
const expanded = await converter.convert(internal);
718+
const expanded = await convertInternalToExpanded(internal);
722719
assert.deepStrictEqual(expanded, {
723720
type: 'object',
724721
'x-bsonType': 'object',
@@ -881,8 +878,7 @@ describe('internalSchemaToExpanded', async function() {
881878
}
882879
]
883880
};
884-
const converter = new InternalToExpandedConverter();
885-
const expanded = await converter.convert(internal);
881+
const expanded = await convertInternalToExpanded(internal);
886882
assert.deepStrictEqual(expanded, {
887883
type: 'object',
888884
'x-bsonType': 'object',
@@ -1017,8 +1013,7 @@ describe('internalSchemaToExpanded', async function() {
10171013
}
10181014
]
10191015
};
1020-
const converter = new InternalToExpandedConverter();
1021-
const expanded = await converter.convert(internal);
1016+
const expanded = await convertInternalToExpanded(internal);
10221017
assert.deepStrictEqual(expanded, {
10231018
type: 'object',
10241019
'x-bsonType': 'object',
@@ -1127,8 +1122,7 @@ describe('internalSchemaToExpanded', async function() {
11271122
}
11281123
]
11291124
};
1130-
const converter = new InternalToExpandedConverter();
1131-
const expanded = await converter.convert(internal);
1125+
const expanded = await convertInternalToExpanded(internal);
11321126
assert.deepStrictEqual(expanded, {
11331127
type: 'object',
11341128
'x-bsonType': 'object',
@@ -1269,8 +1263,7 @@ describe('internalSchemaToExpanded', async function() {
12691263
}
12701264
]
12711265
};
1272-
const converter = new InternalToExpandedConverter();
1273-
const expanded = await converter.convert(internal);
1266+
const expanded = await convertInternalToExpanded(internal);
12741267
assert.deepStrictEqual(expanded, {
12751268
type: 'object',
12761269
'x-bsonType': 'object',
@@ -1378,8 +1371,7 @@ describe('internalSchemaToExpanded', async function() {
13781371
}
13791372
]
13801373
};
1381-
const converter = new InternalToExpandedConverter();
1382-
const expanded = await converter.convert(internal);
1374+
const expanded = await convertInternalToExpanded(internal);
13831375
const expectedDefinitions = {
13841376
ObjectId: RELAXED_EJSON_DEFINITIONS.ObjectId
13851377
};
@@ -1529,8 +1521,7 @@ describe('internalSchemaToExpanded', async function() {
15291521
]
15301522
};
15311523
const abortController = new AbortController();
1532-
const converter = new InternalToExpandedConverter();
1533-
const promise = converter.convert(internal, { signal: abortController.signal });
1524+
const promise = convertInternalToExpanded(internal, { signal: abortController.signal });
15341525
abortController.abort(new Error('Too long, didn\'t wait.'));
15351526
await assert.rejects(promise, {
15361527
name: 'Error',

src/schema-converters/internalToExpanded.ts

Lines changed: 38 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,59 @@ import { InternalTypeToStandardTypeMap, RELAXED_EJSON_DEFINITIONS } from './inte
44
import { InternalTypeToBsonTypeMap } from './internalToMongoDB';
55
import { allowAbort } from './util';
66

7-
export class InternalToExpandedConverter {
8-
private usedDefinitions = new Set<string>();
7+
export const convertInternalToExpanded = (function() {
8+
const usedDefinitions = new Set<string>();
99

10-
private clearUsedDefintions() {
11-
this.usedDefinitions.clear();
10+
function clearUsedDefinitions() {
11+
usedDefinitions.clear();
1212
}
1313

14-
private getUsedDefinitions() {
14+
function getUsedDefinitions() {
1515
const filteredDefinitions = Object.fromEntries(
16-
Object.entries(RELAXED_EJSON_DEFINITIONS)
17-
.filter(([key]) => this.usedDefinitions.has(key))
16+
Object.entries(RELAXED_EJSON_DEFINITIONS).filter(([key]) => usedDefinitions.has(key))
1817
);
1918
return Object.freeze(filteredDefinitions);
2019
}
2120

22-
private markUsedDefinition(ref: string) {
23-
this.usedDefinitions.add(ref.split('/')[2]);
21+
function markUsedDefinition(ref: string) {
22+
usedDefinitions.add(ref.split('/')[2]);
2423
}
2524

26-
private getStandardType(internalType: string) {
27-
const type = { ...InternalTypeToStandardTypeMap[internalType] };
25+
function getStandardType(internalType: string) {
26+
const type = InternalTypeToStandardTypeMap[internalType];
2827
if (!type) throw new Error(`Encountered unknown type: ${internalType}`);
29-
return type;
28+
return { ...type };
3029
}
3130

32-
private getBsonType(internalType: string) {
31+
function getBsonType(internalType: string) {
3332
const type = InternalTypeToBsonTypeMap[internalType];
3433
if (!type) throw new Error(`Encountered unknown type: ${internalType}`);
3534
return type;
3635
}
3736

38-
private async parseType(type: SchemaType, signal?: AbortSignal): Promise<ExpandedJSONSchema> {
37+
async function parseType(type: SchemaType, signal?: AbortSignal): Promise<ExpandedJSONSchema> {
3938
await allowAbort(signal);
4039
const schema: ExpandedJSONSchema = {
41-
...this.getStandardType(type.bsonType),
42-
'x-bsonType': this.getBsonType(type.bsonType),
43-
'x-metadata': this.getMetadata(type)
40+
...getStandardType(type.bsonType),
41+
'x-bsonType': getBsonType(type.bsonType),
42+
'x-metadata': getMetadata(type)
4443
};
45-
if ('values' in type && !!type.values) {
44+
if ('values' in type && type.values) {
4645
schema['x-sampleValues'] = type.values;
4746
}
48-
if (schema.$ref) this.markUsedDefinition(schema.$ref);
47+
if (schema.$ref) markUsedDefinition(schema.$ref);
4948
switch (type.bsonType) {
5049
case 'Array':
51-
schema.items = await this.parseTypes((type as ArraySchemaType).types);
50+
schema.items = await parseTypes((type as ArraySchemaType).types, signal);
5251
break;
5352
case 'Document':
54-
Object.assign(schema,
55-
await this.parseFields((type as DocumentSchemaType).fields, signal)
56-
);
53+
Object.assign(schema, await parseFields((type as DocumentSchemaType).fields, signal));
5754
break;
5855
}
59-
6056
return schema;
6157
}
6258

63-
private getMetadata<TType extends SchemaField | SchemaType>({
59+
function getMetadata<TType extends SchemaField | SchemaType>({
6460
hasDuplicates,
6561
probability,
6662
count
@@ -72,50 +68,49 @@ export class InternalToExpandedConverter {
7268
};
7369
}
7470

75-
private async parseTypes(types: SchemaType[], signal?: AbortSignal): Promise<ExpandedJSONSchema> {
71+
async function parseTypes(types: SchemaType[], signal?: AbortSignal): Promise<ExpandedJSONSchema> {
7672
await allowAbort(signal);
7773
const definedTypes = types.filter(type => type.bsonType.toLowerCase() !== 'undefined');
7874
const isSingleType = definedTypes.length === 1;
7975
if (isSingleType) {
80-
return this.parseType(definedTypes[0], signal);
76+
return parseType(definedTypes[0], signal);
8177
}
82-
const parsedTypes = await Promise.all(definedTypes.map(type => this.parseType(type, signal)));
78+
const parsedTypes = await Promise.all(definedTypes.map(type => parseType(type, signal)));
8379
return {
8480
anyOf: parsedTypes
8581
};
8682
}
8783

88-
private async parseFields(fields: DocumentSchemaType['fields'], signal?: AbortSignal): Promise<{
89-
required: ExpandedJSONSchema['required'],
90-
properties: ExpandedJSONSchema['properties'],
91-
}> {
84+
async function parseFields(
85+
fields: DocumentSchemaType['fields'],
86+
signal?: AbortSignal
87+
): Promise<{ required: ExpandedJSONSchema['required']; properties: ExpandedJSONSchema['properties'] }> {
9288
const required = [];
9389
const properties: ExpandedJSONSchema['properties'] = {};
9490
for (const field of fields) {
9591
if (field.probability === 1) required.push(field.name);
9692
properties[field.name] = {
97-
...await this.parseTypes(field.types, signal),
98-
'x-metadata': this.getMetadata(field)
93+
...await parseTypes(field.types, signal),
94+
'x-metadata': getMetadata(field)
9995
};
10096
}
10197

10298
return { required, properties };
10399
}
104100

105-
public async convert(
101+
return async function convert(
106102
internalSchema: InternalSchema,
107-
options: {
108-
signal?: AbortSignal
109-
} = {}): Promise<ExpandedJSONSchema> {
110-
this.clearUsedDefintions();
111-
const { required, properties } = await this.parseFields(internalSchema.fields, options.signal);
103+
options: { signal?: AbortSignal } = {}
104+
): Promise<ExpandedJSONSchema> {
105+
clearUsedDefinitions();
106+
const { required, properties } = await parseFields(internalSchema.fields, options.signal);
112107
const schema: ExpandedJSONSchema = {
113108
type: 'object',
114109
'x-bsonType': 'object',
115110
required,
116111
properties,
117-
$defs: this.getUsedDefinitions()
112+
$defs: getUsedDefinitions()
118113
};
119114
return schema;
120-
}
121-
}
115+
};
116+
})();

0 commit comments

Comments
 (0)