@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 CHANGED
@@ -1,21 +1,2 @@
1
- # @based/functions
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
- ```
@@ -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') {
@@ -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
@@ -94,5 +94,6 @@ export const basedSchemaStringFormatValues = [
94
94
  'markdown',
95
95
  'clike',
96
96
  'basedId',
97
+ 'basedType',
97
98
  ];
98
99
  //# sourceMappingURL=types.js.map
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@based/schema",
3
- "version": "3.0.1",
3
+ "version": "3.2.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/src/index.js",
6
6
  "files": [