@based/schema 3.0.1 → 3.2.0
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 +1 -20
- package/dist/src/compat/index.js +10 -2
- package/dist/src/generateQuery.d.ts +12 -0
- package/dist/src/generateQuery.js +75 -0
- package/dist/src/set/fields/references.js +40 -0
- package/dist/src/set/fields/string.js +1 -0
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.js +1 -0
- package/dist/test/compat.js +25 -0
- package/dist/test/query.d.ts +1 -0
- package/dist/test/query.js +93 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,21 +1,2 @@
|
|
|
1
|
-
# @based/
|
|
1
|
+
# @based/schema
|
|
2
2
|
|
|
3
|
-
To be used with based cloud functions, adds types and utilities.
|
|
4
|
-
|
|
5
|
-
- Example based function
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
import { BasedFunction } from '@based/functions'
|
|
9
|
-
|
|
10
|
-
const submitVote: BasedFunction<
|
|
11
|
-
{ target: number },
|
|
12
|
-
'ok' | 'not-ok'
|
|
13
|
-
> = async (based, { target }) => {
|
|
14
|
-
if (target > 10) {
|
|
15
|
-
return 'ok'
|
|
16
|
-
}
|
|
17
|
-
return 'not-ok'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default submitVote
|
|
21
|
-
```
|
package/dist/src/compat/index.js
CHANGED
|
@@ -58,7 +58,7 @@ const convertOldToNewMeta = (props) => {
|
|
|
58
58
|
};
|
|
59
59
|
const convertNewFieldToOldField = (newField) => {
|
|
60
60
|
// @ts-ignore
|
|
61
|
-
const { type, properties, values, items, ...props } = newField;
|
|
61
|
+
const { type, bidirectional, properties, values, items, ...props } = newField;
|
|
62
62
|
const meta = convertNewToOldMeta(props);
|
|
63
63
|
// @ts-ignore
|
|
64
64
|
const overwrite = newToOldType[type] || newToOldType[props.format];
|
|
@@ -70,6 +70,10 @@ const convertNewFieldToOldField = (newField) => {
|
|
|
70
70
|
if (meta) {
|
|
71
71
|
oldField.meta = meta;
|
|
72
72
|
}
|
|
73
|
+
if (bidirectional) {
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
oldField.bidirectional = bidirectional;
|
|
76
|
+
}
|
|
73
77
|
if (properties) {
|
|
74
78
|
// @ts-ignore
|
|
75
79
|
oldField.properties = {};
|
|
@@ -133,7 +137,7 @@ export const convertNewToOld = (newSchema) => {
|
|
|
133
137
|
};
|
|
134
138
|
const convertOldFieldToNewField = (oldField) => {
|
|
135
139
|
// @ts-ignore
|
|
136
|
-
const { type, properties, values, items, meta = {} } = oldField;
|
|
140
|
+
const { type, bidirectional, properties, values, items, meta = {} } = oldField;
|
|
137
141
|
const overwrite = oldToNewType[type] || oldToNewType[meta.format];
|
|
138
142
|
const newField = overwrite
|
|
139
143
|
? {
|
|
@@ -144,6 +148,10 @@ const convertOldFieldToNewField = (oldField) => {
|
|
|
144
148
|
type,
|
|
145
149
|
...convertOldToNewMeta(meta),
|
|
146
150
|
};
|
|
151
|
+
if (bidirectional) {
|
|
152
|
+
// @ts-ignore
|
|
153
|
+
newField.bidirectional = bidirectional;
|
|
154
|
+
}
|
|
147
155
|
if (properties) {
|
|
148
156
|
// @ts-ignore
|
|
149
157
|
newField.properties = {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BasedSchema, BasedSchemaField, BasedSchemaType } from './types.js';
|
|
2
|
+
type Query = Record<string, any>;
|
|
3
|
+
type Transformer = (Query: any, opts: {
|
|
4
|
+
type: string;
|
|
5
|
+
schema: BasedSchemaType;
|
|
6
|
+
depth: number;
|
|
7
|
+
}) => Query;
|
|
8
|
+
export declare const generateQueryForType: (type: string, schema: BasedSchema, depth?: number, transformer?: Transformer, query?: Query, nestedRefs?: Set<BasedSchemaField>) => Query;
|
|
9
|
+
export declare const generateQuery: (query: {
|
|
10
|
+
$id: string;
|
|
11
|
+
} & Query, schema: BasedSchema, depth?: number, transformer?: Transformer) => Query;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const parseSchemaField = (schemaField, schema, depth, transformer, nestedRefs, currentQuery) => {
|
|
2
|
+
if ('values' in schemaField) {
|
|
3
|
+
return parseSchemaField(schemaField.values, schema, depth, transformer, nestedRefs, currentQuery);
|
|
4
|
+
}
|
|
5
|
+
if ('items' in schemaField) {
|
|
6
|
+
return parseSchemaField(schemaField.items, schema, depth, transformer, nestedRefs, currentQuery);
|
|
7
|
+
}
|
|
8
|
+
if ('properties' in schemaField) {
|
|
9
|
+
currentQuery ??= {};
|
|
10
|
+
for (const i in schemaField.properties) {
|
|
11
|
+
const v = currentQuery[i];
|
|
12
|
+
if (v === true || v === false) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
currentQuery[i] = parseSchemaField(schemaField.properties[i], schema, depth, transformer, nestedRefs, currentQuery[i]);
|
|
16
|
+
}
|
|
17
|
+
return currentQuery;
|
|
18
|
+
}
|
|
19
|
+
if (schemaField.type === 'reference' || schemaField.type === 'references') {
|
|
20
|
+
const nestedDepth = depth - 1;
|
|
21
|
+
if (nestedDepth < 0) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (nestedRefs) {
|
|
25
|
+
if (nestedRefs.has(schemaField)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
nestedRefs.add(schemaField);
|
|
29
|
+
}
|
|
30
|
+
const allowedTypes = schemaField.allowedTypes?.length
|
|
31
|
+
? schemaField.allowedTypes
|
|
32
|
+
: Object.keys(schema.types);
|
|
33
|
+
for (const i of allowedTypes) {
|
|
34
|
+
currentQuery = generateQueryForType(typeof i === 'string' ? i : i.type, schema, nestedDepth, transformer, currentQuery, nestedRefs || new Set([schemaField]));
|
|
35
|
+
}
|
|
36
|
+
if (schemaField.type === 'references') {
|
|
37
|
+
currentQuery.$list = true;
|
|
38
|
+
}
|
|
39
|
+
return currentQuery;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
};
|
|
43
|
+
export const generateQueryForType = (type, schema, depth = 0, transformer, query = {}, nestedRefs) => {
|
|
44
|
+
const schemaType = type === 'root' ? schema.root : schema.types[type];
|
|
45
|
+
if (!schemaType) {
|
|
46
|
+
throw new Error('unknown type');
|
|
47
|
+
}
|
|
48
|
+
query.id ??= true;
|
|
49
|
+
query.type ??= true;
|
|
50
|
+
query.createdAt ??= true;
|
|
51
|
+
query.updatedAt ??= true;
|
|
52
|
+
if (schemaType.fields) {
|
|
53
|
+
for (const key in schemaType.fields) {
|
|
54
|
+
const v = query[key];
|
|
55
|
+
if (v === true || v === false) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const schemaField = schemaType.fields[key];
|
|
59
|
+
query[key] = parseSchemaField(schemaField, schema, depth, transformer, nestedRefs, query[key]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return transformer
|
|
63
|
+
? transformer(query, { type, depth, schema: schemaType })
|
|
64
|
+
: query;
|
|
65
|
+
};
|
|
66
|
+
export const generateQuery = (query, schema, depth, transformer) => {
|
|
67
|
+
const type = query.$id === 'root'
|
|
68
|
+
? query.$id
|
|
69
|
+
: schema.prefixToTypeMapping[query.$id.substring(0, 2)];
|
|
70
|
+
if (!type) {
|
|
71
|
+
throw new Error('invalid type');
|
|
72
|
+
}
|
|
73
|
+
return generateQueryForType(type, schema, depth, transformer, query);
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=generateQuery.js.map
|
|
@@ -86,6 +86,36 @@ export const reference = async (args) => {
|
|
|
86
86
|
args.error(ParseError.referenceIsIncorrectType);
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
|
+
function parseSortableOp(args, value, op) {
|
|
90
|
+
if (typeof value[op] !== 'object') {
|
|
91
|
+
args.error(ParseError.incorrectFormat);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!args.fieldSchema.sortable) {
|
|
95
|
+
args.error(ParseError.incorrectFieldType);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
switch (typeof value[op].$idx) {
|
|
99
|
+
case 'bigint':
|
|
100
|
+
break;
|
|
101
|
+
case 'number':
|
|
102
|
+
value[op].$idx = BigInt(value[op].$idx);
|
|
103
|
+
break;
|
|
104
|
+
default:
|
|
105
|
+
args.error(ParseError.incorrectFormat);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(value[op].$value)) {
|
|
109
|
+
// NOP
|
|
110
|
+
}
|
|
111
|
+
else if (typeof value[op].$value === 'string') {
|
|
112
|
+
value[op].$value = [value[op].$value];
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
args.error(ParseError.incorrectFormat);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
89
119
|
export const references = async (args) => {
|
|
90
120
|
const { value } = args;
|
|
91
121
|
if (typeof value !== 'object' || value === null) {
|
|
@@ -113,6 +143,16 @@ export const references = async (args) => {
|
|
|
113
143
|
else if (key === '$remove') {
|
|
114
144
|
args.value.$remove = await parseOperator(args, key);
|
|
115
145
|
}
|
|
146
|
+
else if (key === '$assign') {
|
|
147
|
+
parseSortableOp(args, value, '$assign');
|
|
148
|
+
}
|
|
149
|
+
else if (key === '$insert') {
|
|
150
|
+
parseSortableOp(args, value, '$insert');
|
|
151
|
+
}
|
|
152
|
+
else if (key === '$move') {
|
|
153
|
+
parseSortableOp(args, value, '$move');
|
|
154
|
+
value.$move.$value.reverse();
|
|
155
|
+
}
|
|
116
156
|
else {
|
|
117
157
|
args.create({ key }).error(ParseError.fieldDoesNotExist);
|
|
118
158
|
}
|
|
@@ -74,6 +74,7 @@ const formatPatterns = {
|
|
|
74
74
|
clike: () => true,
|
|
75
75
|
// Can add some more checks for this...
|
|
76
76
|
basedId: (value) => typeof value === 'string' && value.length < 16,
|
|
77
|
+
basedType: (value) => typeof value === 'string' && value.length < 64,
|
|
77
78
|
};
|
|
78
79
|
const validateString = (args, value) => {
|
|
79
80
|
if (typeof value !== 'string') {
|
package/dist/src/types.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export type BasedSchemaFieldShared = {
|
|
|
43
43
|
};
|
|
44
44
|
$delete?: boolean;
|
|
45
45
|
};
|
|
46
|
-
export declare const basedSchemaStringFormatValues: readonly ["email", "URL", "MACAddress", "IP", "IPRange", "FQDN", "IBAN", "BIC", "alpha", "alphaLocales", "alphanumeric", "alphanumericLocales", "passportNumber", "port", "lowercase", "uppercase", "ascii", "semVer", "surrogatePair", "IMEI", "hexadecimal", "octal", "hexColor", "rgbColor", "HSL", "ISRC", "MD5", "JWT", "UUID", "luhnNumber", "creditCard", "identityCard", "EAN", "ISIN", "ISBN", "ISSN", "mobilePhone", "mobilePhoneLocales", "postalCode", "postalCodeLocales", "ethereumAddress", "currency", "btcAddress", "ISO6391", "ISO8601", "RFC3339", "ISO31661Alpha2", "ISO31661Alpha3", "ISO4217", "base32", "base58", "base64", "dataURI", "magnetURI", "mimeType", "latLong", "slug", "strongPassword", "taxID", "licensePlate", "VAT", "code", "typescript", "javascript", "python", "rust", "css", "html", "json", "markdown", "clike", "basedId"];
|
|
46
|
+
export declare const basedSchemaStringFormatValues: readonly ["email", "URL", "MACAddress", "IP", "IPRange", "FQDN", "IBAN", "BIC", "alpha", "alphaLocales", "alphanumeric", "alphanumericLocales", "passportNumber", "port", "lowercase", "uppercase", "ascii", "semVer", "surrogatePair", "IMEI", "hexadecimal", "octal", "hexColor", "rgbColor", "HSL", "ISRC", "MD5", "JWT", "UUID", "luhnNumber", "creditCard", "identityCard", "EAN", "ISIN", "ISBN", "ISSN", "mobilePhone", "mobilePhoneLocales", "postalCode", "postalCodeLocales", "ethereumAddress", "currency", "btcAddress", "ISO6391", "ISO8601", "RFC3339", "ISO31661Alpha2", "ISO31661Alpha3", "ISO4217", "base32", "base58", "base64", "dataURI", "magnetURI", "mimeType", "latLong", "slug", "strongPassword", "taxID", "licensePlate", "VAT", "code", "typescript", "javascript", "python", "rust", "css", "html", "json", "markdown", "clike", "basedId", "basedType"];
|
|
47
47
|
export type BasedSchemaStringShared = {
|
|
48
48
|
minLength?: number;
|
|
49
49
|
maxLength?: number;
|
package/dist/src/types.js
CHANGED
package/dist/test/compat.js
CHANGED
|
@@ -50,6 +50,31 @@ test('refTypes', async (t) => {
|
|
|
50
50
|
// @ts-ignore
|
|
51
51
|
t.is(oldSchema.types.youzi.fields.image.meta.refTypes[0], 'youzi');
|
|
52
52
|
});
|
|
53
|
+
test('bidirectional', async (t) => {
|
|
54
|
+
const newSchema = convertOldToNew({
|
|
55
|
+
types: {
|
|
56
|
+
youzi: {
|
|
57
|
+
fields: {
|
|
58
|
+
image: {
|
|
59
|
+
type: 'reference',
|
|
60
|
+
bidirectional: {
|
|
61
|
+
fromField: 'success',
|
|
62
|
+
},
|
|
63
|
+
meta: {
|
|
64
|
+
refTypes: ['youzi'],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
console.log(newSchema.types.youzi.fields.image);
|
|
72
|
+
// @ts-ignore
|
|
73
|
+
t.is(newSchema.types.youzi.fields.image.bidirectional.fromField, 'success');
|
|
74
|
+
const oldSchema = convertNewToOld(newSchema);
|
|
75
|
+
// @ts-ignore
|
|
76
|
+
t.is(oldSchema.types.youzi.fields.image.bidirectional.fromField, 'success');
|
|
77
|
+
});
|
|
53
78
|
test('old schema compat mode', async (t) => {
|
|
54
79
|
for (let i = 0; i < oldSchemas.length - 1; i++) {
|
|
55
80
|
const oldSchema = oldSchemas[i];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { generateQuery } from '../src/generateQuery.js';
|
|
3
|
+
const schema = {
|
|
4
|
+
types: {
|
|
5
|
+
thing: {
|
|
6
|
+
prefix: 'ti',
|
|
7
|
+
fields: {
|
|
8
|
+
priority: { type: 'number' },
|
|
9
|
+
something: { type: 'string', format: 'strongPassword' },
|
|
10
|
+
blaRef: {
|
|
11
|
+
type: 'reference',
|
|
12
|
+
allowedTypes: ['bla'],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
bla: {
|
|
17
|
+
prefix: 'bl',
|
|
18
|
+
fields: {
|
|
19
|
+
arr: {
|
|
20
|
+
type: 'array',
|
|
21
|
+
items: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
refArr: {
|
|
26
|
+
type: 'array',
|
|
27
|
+
items: {
|
|
28
|
+
type: 'reference',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
refRec: {
|
|
32
|
+
type: 'record',
|
|
33
|
+
values: {
|
|
34
|
+
type: 'reference',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
obj: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
blafurp: { type: 'string' },
|
|
41
|
+
nesto: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
properties: {
|
|
44
|
+
nestfield: { type: 'string' },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
reffie: { type: 'reference' },
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
ref: {
|
|
51
|
+
type: 'reference',
|
|
52
|
+
},
|
|
53
|
+
children: {
|
|
54
|
+
type: 'references',
|
|
55
|
+
},
|
|
56
|
+
referencesToThings: {
|
|
57
|
+
type: 'references',
|
|
58
|
+
allowedTypes: ['thing'],
|
|
59
|
+
},
|
|
60
|
+
referenceToThing: {
|
|
61
|
+
type: 'reference',
|
|
62
|
+
allowedTypes: ['thing'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
$defs: {},
|
|
68
|
+
language: 'en',
|
|
69
|
+
translations: ['de', 'nl', 'ro', 'za', 'ae'],
|
|
70
|
+
root: {
|
|
71
|
+
fields: {},
|
|
72
|
+
},
|
|
73
|
+
prefixToTypeMapping: {
|
|
74
|
+
bl: 'bla',
|
|
75
|
+
ti: 'thing',
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
test('generate query', async (t) => {
|
|
79
|
+
const query = generateQuery({
|
|
80
|
+
$id: 'bl1',
|
|
81
|
+
$language: 'en',
|
|
82
|
+
children: {
|
|
83
|
+
id: false,
|
|
84
|
+
},
|
|
85
|
+
refArr: {
|
|
86
|
+
id: false,
|
|
87
|
+
obj: false,
|
|
88
|
+
},
|
|
89
|
+
}, schema, 1);
|
|
90
|
+
console.dir(query, { depth: null });
|
|
91
|
+
t.pass();
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=query.js.map
|